The union of the color and the node_number in the struct screen_char.
[elinks.git] / src / terminal / terminal.c
blob139ecf71590ce420f98e3d0df6fb4e347af92341
1 /** Terminal interface - low-level displaying implementation.
2 * @file */
4 #ifdef HAVE_CONFIG_H
5 #include "config.h"
6 #endif
8 #include <stdlib.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <sys/types.h>
12 #ifdef HAVE_UNISTD_H
13 #include <unistd.h>
14 #endif
16 #include "elinks.h"
18 #include "bookmarks/bookmarks.h"
19 #include "config/options.h"
20 #include "main/main.h"
21 #include "main/module.h"
22 #include "main/object.h"
23 #include "main/select.h"
24 #include "osdep/osdep.h"
25 #include "osdep/signals.h"
26 #ifdef CONFIG_SCRIPTING_SPIDERMONKEY
27 # include "scripting/smjs/smjs.h"
28 #endif
29 #include "session/session.h"
30 #include "terminal/draw.h"
31 #include "terminal/event.h"
32 #include "terminal/hardio.h"
33 #include "terminal/kbd.h"
34 #include "terminal/screen.h"
35 #include "terminal/terminal.h"
36 #include "terminal/window.h"
37 #include "util/error.h"
38 #include "util/memory.h"
39 #include "util/string.h"
40 #include "viewer/text/textarea.h"
43 INIT_LIST_OF(struct terminal, terminals);
45 static void check_if_no_terminal(void);
47 void
48 redraw_terminal(struct terminal *term)
50 struct term_event ev;
52 set_redraw_term_event(&ev, term->width, term->height);
53 term_send_event(term, &ev);
56 void
57 redraw_terminal_cls(struct terminal *term)
59 struct term_event ev;
61 set_resize_term_event(&ev, term->width, term->height);
62 term_send_event(term, &ev);
65 void
66 cls_redraw_all_terminals(void)
68 struct terminal *term;
70 foreach (term, terminals)
71 redraw_terminal_cls(term);
74 /** Get the terminal in which message boxes should be displayed, if
75 * there is no specific reason to use some other terminal. This
76 * returns NULL if all terminals have been closed. (ELinks keeps
77 * running anyway if ui.sessions.keep_session_active is true.) */
78 struct terminal *
79 get_default_terminal(void)
81 if (list_empty(terminals))
82 return NULL;
83 else
84 return terminals.next;
87 struct terminal *
88 init_term(int fdin, int fdout)
90 unsigned char name[MAX_TERM_LEN + 9] = "terminal.";
91 struct terminal *term = mem_calloc(1, sizeof(*term));
93 if (!term) {
94 check_if_no_terminal();
95 return NULL;
98 term->screen = init_screen();
99 if (!term->screen) {
100 mem_free(term);
101 return NULL;
104 init_list(term->windows);
106 term->fdin = fdin;
107 term->fdout = fdout;
108 term->master = (term->fdout == get_output_handle());
109 term->blocked = -1;
111 get_terminal_name(name + 9);
112 term->spec = get_opt_rec(config_options, name);
113 object_lock(term->spec);
115 /* It's a new terminal, so assume the user is using it right now,
116 * and sort it to the front of the list. */
117 add_to_list(terminals, term);
119 set_handlers(fdin, (select_handler_T) in_term, NULL,
120 (select_handler_T) destroy_terminal, term);
121 return term;
124 /** Get the codepage of a terminal. The UTF-8 I/O option does not
125 * affect this.
127 * @todo Perhaps cache the value in struct terminal?
129 * @bug Bug 1064: If the charset has been set as "System", this should
130 * apply the locale environment variables of the slave ELinks process,
131 * not those of the master ELinks process that parsed the configuration
132 * file. That is why the parameter points to struct terminal and not
133 * merely to its option tree (term->spec).
135 * @see get_translation_table(), get_cp_mime_name() */
137 get_terminal_codepage(const struct terminal *term)
139 return get_opt_codepage_tree(term->spec, "charset", NULL);
142 void
143 redraw_all_terminals(void)
145 struct terminal *term;
147 foreach (term, terminals)
148 redraw_screen(term);
151 void
152 destroy_terminal(struct terminal *term)
154 #ifdef CONFIG_SCRIPTING_SPIDERMONKEY
155 smjs_detach_terminal_object(term);
156 #endif
157 #ifdef CONFIG_BOOKMARKS
158 bookmark_auto_save_tabs(term);
159 #endif
160 detach_downloads_from_terminal(term);
162 free_textarea_data(term);
164 /* delete_window doesn't update term->current_tab, but it
165 calls redraw_terminal, which requires term->current_tab
166 to be valid if there are any tabs left. So set a value
167 that will be valid for that long. */
168 term->current_tab = 0;
170 while (!list_empty(term->windows))
171 delete_window(term->windows.next);
173 /* mem_free_if(term->cwd); */
174 mem_free_if(term->title);
175 if (term->screen) done_screen(term->screen);
177 clear_handlers(term->fdin);
178 mem_free_if(term->interlink);
180 if (term->blocked != -1) {
181 close(term->blocked);
182 clear_handlers(term->blocked);
185 del_from_list(term);
186 close(term->fdin);
188 if (term->fdout != 1) {
189 if (term->fdout != term->fdin) close(term->fdout);
190 } else {
191 unhandle_terminal_signals(term);
192 free_all_itrms();
193 #ifndef NO_FORK_ON_EXIT
194 if (!list_empty(terminals)) {
195 if (fork()) exit(0);
197 #endif
200 object_unlock(term->spec);
201 mem_free(term);
202 check_if_no_terminal();
205 void
206 destroy_all_terminals(void)
208 while (!list_empty(terminals))
209 destroy_terminal(terminals.next);
212 static void
213 check_if_no_terminal(void)
215 program.terminate = list_empty(terminals)
216 && !get_opt_bool("ui.sessions.keep_session_active", NULL);
219 void
220 exec_thread(unsigned char *path, int p)
222 int plen = strlen(path + 1) + 2;
224 #if defined(HAVE_SETPGID) && !defined(CONFIG_OS_BEOS) && !defined(HAVE_BEGINTHREAD)
225 if (path[0] == TERM_EXEC_NEWWIN) setpgid(0, 0);
226 #endif
227 exe(path + 1);
228 if (path[plen]) unlink(path + plen);
231 void
232 close_handle(void *h)
234 close((long) h);
235 clear_handlers((long) h);
238 static void
239 unblock_terminal(struct terminal *term)
241 close_handle((void *) (long) term->blocked);
242 term->blocked = -1;
243 set_handlers(term->fdin, (select_handler_T) in_term, NULL,
244 (select_handler_T) destroy_terminal, term);
245 unblock_itrm();
246 redraw_terminal_cls(term);
247 if (term->textarea_data) /* XXX */
248 textarea_edit(1, term, NULL, NULL, NULL);
251 #ifndef CONFIG_FASTMEM
252 void
253 assert_terminal_ptr_not_dangling(const struct terminal *suspect)
255 struct terminal *term;
257 if (suspect == NULL)
258 return;
260 foreach (term, terminals) {
261 if (term == suspect)
262 return;
265 assertm(0, "Dangling pointer to struct terminal");
267 #endif /* !CONFIG_FASTMEM */
269 static void
270 exec_on_master_terminal(struct terminal *term,
271 unsigned char *path, int plen,
272 unsigned char *delete, int dlen,
273 enum term_exec fg)
275 int blockh;
276 int param_size = plen + dlen + 2 /* 2 null char */ + 1 /* fg */;
277 unsigned char *param = fmem_alloc(param_size);
279 if (!param) return;
281 param[0] = fg;
282 memcpy(param + 1, path, plen + 1);
283 memcpy(param + 1 + plen + 1, delete, dlen + 1);
285 if (fg == TERM_EXEC_FG) block_itrm();
287 blockh = start_thread((void (*)(void *, int)) exec_thread,
288 param, param_size);
289 fmem_free(param);
290 if (blockh == -1) {
291 if (fg == TERM_EXEC_FG) unblock_itrm();
292 return;
295 if (fg == TERM_EXEC_FG) {
296 term->blocked = blockh;
297 set_handlers(blockh,
298 (select_handler_T) unblock_terminal,
299 NULL,
300 (select_handler_T) unblock_terminal,
301 term);
302 set_handlers(term->fdin, NULL, NULL,
303 (select_handler_T) destroy_terminal,
304 term);
306 } else {
307 set_handlers(blockh, close_handle, NULL,
308 close_handle, (void *) (long) blockh);
312 static void
313 exec_on_slave_terminal( struct terminal *term,
314 unsigned char *path, int plen,
315 unsigned char *delete, int dlen,
316 enum term_exec fg)
318 int data_size = plen + dlen + 1 /* 0 */ + 1 /* fg */ + 2 /* 2 null char */;
319 unsigned char *data = fmem_alloc(data_size);
321 if (!data) return;
323 data[0] = 0;
324 data[1] = fg;
325 memcpy(data + 2, path, plen + 1);
326 memcpy(data + 2 + plen + 1, delete, dlen + 1);
327 hard_write(term->fdout, data, data_size);
328 fmem_free(data);
331 void
332 exec_on_terminal(struct terminal *term, unsigned char *path,
333 unsigned char *delete, enum term_exec fg)
335 if (path) {
336 if (!*path) return;
337 } else {
338 path = "";
341 #ifdef NO_FG_EXEC
342 fg = TERM_EXEC_BG;
343 #endif
345 if (term->master) {
346 if (!*path) {
347 dispatch_special(delete);
348 return;
351 /* TODO: Should this be changed to allow TERM_EXEC_NEWWIN
352 * in a blocked terminal? There is similar code in
353 * in_sock(). --KON, 2007 */
354 if (fg != TERM_EXEC_BG && is_blocked()) {
355 unlink(delete);
356 return;
359 exec_on_master_terminal(term,
360 path, strlen(path),
361 delete, strlen(delete),
362 fg);
363 } else {
364 exec_on_slave_terminal( term,
365 path, strlen(path),
366 delete, strlen(delete),
367 fg);
371 void
372 exec_shell(struct terminal *term)
374 unsigned char *sh;
376 if (!can_open_os_shell(term->environment)) return;
378 sh = get_shell();
379 if (sh && *sh)
380 exec_on_terminal(term, sh, "", TERM_EXEC_FG);
384 void
385 do_terminal_function(struct terminal *term, unsigned char code,
386 unsigned char *data)
388 int data_len = strlen(data);
389 unsigned char *x_data = fmem_alloc(data_len + 1 /* code */ + 1 /* null char */);
391 if (!x_data) return;
392 x_data[0] = code;
393 memcpy(x_data + 1, data, data_len + 1);
394 exec_on_terminal(term, NULL, x_data, TERM_EXEC_BG);
395 fmem_free(x_data);
398 /** @return negative on error; zero or positive on success. */
400 set_terminal_title(struct terminal *term, unsigned char *title)
402 int from_cp;
403 int to_cp;
404 unsigned char *converted = NULL;
406 if (term->title && !strcmp(title, term->title)) return 0;
408 /* In which codepage was the title parameter given? */
409 from_cp = get_terminal_codepage(term);
411 /* In which codepage does the terminal want the title? */
412 if (get_opt_bool_tree(term->spec, "latin1_title", NULL))
413 to_cp = get_cp_index("ISO-8859-1");
414 else if (get_opt_bool_tree(term->spec, "utf_8_io", NULL))
415 to_cp = get_cp_index("UTF-8");
416 else
417 to_cp = from_cp;
419 if (from_cp != to_cp) {
420 struct conv_table *convert_table;
422 convert_table = get_translation_table(from_cp, to_cp);
423 if (!convert_table) return -1;
424 converted = convert_string(convert_table, title, strlen(title),
425 to_cp, CSM_NONE, NULL, NULL, NULL);
426 if (!converted) return -1;
429 mem_free_set(&term->title, stracpy(title));
430 do_terminal_function(term, TERM_FN_TITLE_CODEPAGE,
431 get_cp_mime_name(to_cp));
432 do_terminal_function(term, TERM_FN_TITLE,
433 converted ? converted : title);
434 mem_free_if(converted);
435 return 0;
438 static int terminal_pipe[2];
441 check_terminal_pipes(void)
443 return c_pipe(terminal_pipe);
446 void
447 close_terminal_pipes(void)
449 close(terminal_pipe[0]);
450 close(terminal_pipe[1]);
453 struct terminal *
454 attach_terminal(int in, int out, int ctl, void *info, int len)
456 struct terminal *term;
458 if (set_nonblocking_fd(terminal_pipe[0]) < 0) return NULL;
459 if (set_nonblocking_fd(terminal_pipe[1]) < 0) return NULL;
460 handle_trm(in, out, out, terminal_pipe[1], ctl, info, len, 0);
462 term = init_term(terminal_pipe[0], out);
463 if (!term) {
464 close_terminal_pipes();
465 return NULL;
468 return term;
471 static struct module *terminal_submodules[] = {
472 &terminal_screen_module,
473 NULL
476 struct module terminal_module = struct_module(
477 /* Because this module is listed in main_modules rather than
478 * in builtin_modules, its name does not appear in the user
479 * interface and so need not be translatable. */
480 /* name: */ "Terminal",
481 /* options: */ NULL,
482 /* hooks: */ NULL,
483 /* submodules: */ terminal_submodules,
484 /* data: */ NULL,
485 /* init: */ NULL,
486 /* done: */ NULL