1 /** Terminal interface - low-level displaying implementation.
11 #include <sys/types.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"
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);
48 redraw_terminal(struct terminal
*term
)
52 set_redraw_term_event(&ev
, term
->width
, term
->height
);
53 term_send_event(term
, &ev
);
57 redraw_terminal_cls(struct terminal
*term
)
61 set_resize_term_event(&ev
, term
->width
, term
->height
);
62 term_send_event(term
, &ev
);
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.) */
79 get_default_terminal(void)
81 if (list_empty(terminals
))
84 return terminals
.next
;
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
));
94 check_if_no_terminal();
98 term
->screen
= init_screen();
104 init_list(term
->windows
);
108 term
->master
= (term
->fdout
== get_output_handle());
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
);
124 /** Get the codepage of a terminal. The UTF-8 I/O option does not
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
);
143 redraw_all_terminals(void)
145 struct terminal
*term
;
147 foreach (term
, terminals
)
152 destroy_terminal(struct terminal
*term
)
154 #ifdef CONFIG_SCRIPTING_SPIDERMONKEY
155 smjs_detach_terminal_object(term
);
157 #ifdef CONFIG_BOOKMARKS
158 bookmark_auto_save_tabs(term
);
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
);
188 if (term
->fdout
!= 1) {
189 if (term
->fdout
!= term
->fdin
) close(term
->fdout
);
191 unhandle_terminal_signals(term
);
193 #ifndef NO_FORK_ON_EXIT
194 if (!list_empty(terminals
)) {
200 object_unlock(term
->spec
);
202 check_if_no_terminal();
206 destroy_all_terminals(void)
208 while (!list_empty(terminals
))
209 destroy_terminal(terminals
.next
);
213 check_if_no_terminal(void)
215 program
.terminate
= list_empty(terminals
)
216 && !get_opt_bool("ui.sessions.keep_session_active", NULL
);
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);
228 if (path
[plen
]) unlink(path
+ plen
);
232 close_handle(void *h
)
235 clear_handlers((long) h
);
239 unblock_terminal(struct terminal
*term
)
241 close_handle((void *) (long) term
->blocked
);
243 set_handlers(term
->fdin
, (select_handler_T
) in_term
, NULL
,
244 (select_handler_T
) destroy_terminal
, term
);
246 redraw_terminal_cls(term
);
247 if (term
->textarea_data
) /* XXX */
248 textarea_edit(1, term
, NULL
, NULL
, NULL
);
251 #ifndef CONFIG_FASTMEM
253 assert_terminal_ptr_not_dangling(const struct terminal
*suspect
)
255 struct terminal
*term
;
260 foreach (term
, terminals
) {
265 assertm(0, "Dangling pointer to struct terminal");
267 #endif /* !CONFIG_FASTMEM */
270 exec_on_master_terminal(struct terminal
*term
,
271 unsigned char *path
, int plen
,
272 unsigned char *delete, int dlen
,
276 int param_size
= plen
+ dlen
+ 2 /* 2 null char */ + 1 /* fg */;
277 unsigned char *param
= fmem_alloc(param_size
);
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
,
291 if (fg
== TERM_EXEC_FG
) unblock_itrm();
295 if (fg
== TERM_EXEC_FG
) {
296 term
->blocked
= blockh
;
298 (select_handler_T
) unblock_terminal
,
300 (select_handler_T
) unblock_terminal
,
302 set_handlers(term
->fdin
, NULL
, NULL
,
303 (select_handler_T
) destroy_terminal
,
307 set_handlers(blockh
, close_handle
, NULL
,
308 close_handle
, (void *) (long) blockh
);
313 exec_on_slave_terminal( struct terminal
*term
,
314 unsigned char *path
, int plen
,
315 unsigned char *delete, int dlen
,
318 int data_size
= plen
+ dlen
+ 1 /* 0 */ + 1 /* fg */ + 2 /* 2 null char */;
319 unsigned char *data
= fmem_alloc(data_size
);
325 memcpy(data
+ 2, path
, plen
+ 1);
326 memcpy(data
+ 2 + plen
+ 1, delete, dlen
+ 1);
327 hard_write(term
->fdout
, data
, data_size
);
332 exec_on_terminal(struct terminal
*term
, unsigned char *path
,
333 unsigned char *delete, enum term_exec fg
)
347 dispatch_special(delete);
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()) {
359 exec_on_master_terminal(term
,
361 delete, strlen(delete),
364 exec_on_slave_terminal( term
,
366 delete, strlen(delete),
372 exec_shell(struct terminal
*term
)
376 if (!can_open_os_shell(term
->environment
)) return;
380 exec_on_terminal(term
, sh
, "", TERM_EXEC_FG
);
385 do_terminal_function(struct terminal
*term
, unsigned char code
,
388 int data_len
= strlen(data
);
389 unsigned char *x_data
= fmem_alloc(data_len
+ 1 /* code */ + 1 /* null char */);
393 memcpy(x_data
+ 1, data
, data_len
+ 1);
394 exec_on_terminal(term
, NULL
, x_data
, TERM_EXEC_BG
);
398 /** @return negative on error; zero or positive on success. */
400 set_terminal_title(struct terminal
*term
, unsigned char *title
)
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");
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
);
438 static int terminal_pipe
[2];
441 check_terminal_pipes(void)
443 return c_pipe(terminal_pipe
);
447 close_terminal_pipes(void)
449 close(terminal_pipe
[0]);
450 close(terminal_pipe
[1]);
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
);
464 close_terminal_pipes();
471 static struct module
*terminal_submodules
[] = {
472 &terminal_screen_module
,
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",
483 /* submodules: */ terminal_submodules
,