Add get_terminal_codepage().
[elinks.git] / src / terminal / terminal.c
blob1cfa08fdd08bb3b69db069371ca01421c06b63a8
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 #include "session/session.h"
27 #include "terminal/draw.h"
28 #include "terminal/event.h"
29 #include "terminal/hardio.h"
30 #include "terminal/kbd.h"
31 #include "terminal/screen.h"
32 #include "terminal/terminal.h"
33 #include "terminal/window.h"
34 #include "util/error.h"
35 #include "util/memory.h"
36 #include "util/string.h"
37 #include "viewer/text/textarea.h"
40 INIT_LIST_OF(struct terminal, terminals);
42 static void check_if_no_terminal(void);
44 void
45 redraw_terminal(struct terminal *term)
47 struct term_event ev;
49 set_redraw_term_event(&ev, term->width, term->height);
50 term_send_event(term, &ev);
53 void
54 redraw_terminal_cls(struct terminal *term)
56 struct term_event ev;
58 set_resize_term_event(&ev, term->width, term->height);
59 term_send_event(term, &ev);
62 void
63 cls_redraw_all_terminals(void)
65 struct terminal *term;
67 foreach (term, terminals)
68 redraw_terminal_cls(term);
71 struct terminal *
72 init_term(int fdin, int fdout)
74 unsigned char name[MAX_TERM_LEN + 9] = "terminal.";
75 struct terminal *term = mem_calloc(1, sizeof(*term));
77 if (!term) {
78 check_if_no_terminal();
79 return NULL;
82 term->screen = init_screen();
83 if (!term->screen) {
84 mem_free(term);
85 return NULL;
88 init_list(term->windows);
90 term->fdin = fdin;
91 term->fdout = fdout;
92 term->master = (term->fdout == get_output_handle());
93 term->blocked = -1;
95 get_terminal_name(name + 9);
96 term->spec = get_opt_rec(config_options, name);
97 object_lock(term->spec);
99 add_to_list(terminals, term);
101 set_handlers(fdin, (select_handler_T) in_term, NULL,
102 (select_handler_T) destroy_terminal, term);
103 return term;
106 /** Get the codepage of a terminal. The UTF-8 I/O option does not
107 * affect this.
109 * @todo Perhaps cache the value in struct terminal?
111 * @bug Bug 1064: If the charset has been set as "System", this should
112 * apply the locale environment variables of the slave ELinks process,
113 * not those of the master ELinks process that parsed the configuration
114 * file. That is why the parameter points to struct terminal and not
115 * merely to its option tree (term->spec).
117 * @see get_translation_table(), get_cp_mime_name() */
119 get_terminal_codepage(const struct terminal *term)
121 return get_opt_codepage_tree(term->spec, "charset");
124 void
125 redraw_all_terminals(void)
127 struct terminal *term;
129 foreach (term, terminals)
130 redraw_screen(term);
133 void
134 destroy_terminal(struct terminal *term)
136 #ifdef CONFIG_BOOKMARKS
137 bookmark_auto_save_tabs(term);
138 #endif
139 detach_downloads_from_terminal(term);
141 /* delete_window doesn't update term->current_tab, but it
142 calls redraw_terminal, which requires term->current_tab
143 to be valid if there are any tabs left. So set a value
144 that will be valid for that long. */
145 term->current_tab = 0;
147 while (!list_empty(term->windows))
148 delete_window(term->windows.next);
150 /* mem_free_if(term->cwd); */
151 mem_free_if(term->title);
152 if (term->screen) done_screen(term->screen);
154 clear_handlers(term->fdin);
155 mem_free_if(term->interlink);
157 if (term->blocked != -1) {
158 close(term->blocked);
159 clear_handlers(term->blocked);
162 del_from_list(term);
163 close(term->fdin);
165 if (term->fdout != 1) {
166 if (term->fdout != term->fdin) close(term->fdout);
167 } else {
168 unhandle_terminal_signals(term);
169 free_all_itrms();
170 #ifndef NO_FORK_ON_EXIT
171 if (!list_empty(terminals)) {
172 if (fork()) exit(0);
174 #endif
177 object_unlock(term->spec);
178 mem_free(term);
179 check_if_no_terminal();
182 void
183 destroy_all_terminals(void)
185 while (!list_empty(terminals))
186 destroy_terminal(terminals.next);
189 static void
190 check_if_no_terminal(void)
192 program.terminate = list_empty(terminals)
193 && !get_opt_bool("ui.sessions.keep_session_active");
196 void
197 exec_thread(unsigned char *path, int p)
199 int plen = strlen(path + 1) + 2;
201 #if defined(HAVE_SETPGID) && !defined(CONFIG_OS_BEOS) && !defined(HAVE_BEGINTHREAD)
202 if (path[0] == TERM_EXEC_NEWWIN) setpgid(0, 0);
203 #endif
204 exe(path + 1);
205 if (path[plen]) unlink(path + plen);
208 void
209 close_handle(void *h)
211 close((long) h);
212 clear_handlers((long) h);
215 static void
216 unblock_terminal(struct terminal *term)
218 close_handle((void *) (long) term->blocked);
219 term->blocked = -1;
220 set_handlers(term->fdin, (select_handler_T) in_term, NULL,
221 (select_handler_T) destroy_terminal, term);
222 unblock_itrm();
223 redraw_terminal_cls(term);
224 if (textarea_editor) /* XXX */
225 textarea_edit(1, NULL, NULL, NULL, NULL);
228 #ifndef CONFIG_FASTMEM
229 void
230 assert_terminal_ptr_not_dangling(const struct terminal *suspect)
232 struct terminal *term;
234 if (suspect == NULL)
235 return;
237 foreach (term, terminals) {
238 if (term == suspect)
239 return;
242 assertm(0, "Dangling pointer to struct terminal");
244 #endif /* !CONFIG_FASTMEM */
246 static void
247 exec_on_master_terminal(struct terminal *term,
248 unsigned char *path, int plen,
249 unsigned char *delete, int dlen,
250 enum term_exec fg)
252 int blockh;
253 int param_size = plen + dlen + 2 /* 2 null char */ + 1 /* fg */;
254 unsigned char *param = fmem_alloc(param_size);
256 if (!param) return;
258 param[0] = fg;
259 memcpy(param + 1, path, plen + 1);
260 memcpy(param + 1 + plen + 1, delete, dlen + 1);
262 if (fg == TERM_EXEC_FG) block_itrm();
264 blockh = start_thread((void (*)(void *, int)) exec_thread,
265 param, param_size);
266 fmem_free(param);
267 if (blockh == -1) {
268 if (fg == TERM_EXEC_FG) unblock_itrm();
269 return;
272 if (fg == TERM_EXEC_FG) {
273 term->blocked = blockh;
274 set_handlers(blockh,
275 (select_handler_T) unblock_terminal,
276 NULL,
277 (select_handler_T) unblock_terminal,
278 term);
279 set_handlers(term->fdin, NULL, NULL,
280 (select_handler_T) destroy_terminal,
281 term);
283 } else {
284 set_handlers(blockh, close_handle, NULL,
285 close_handle, (void *) (long) blockh);
289 static void
290 exec_on_slave_terminal( struct terminal *term,
291 unsigned char *path, int plen,
292 unsigned char *delete, int dlen,
293 enum term_exec fg)
295 int data_size = plen + dlen + 1 /* 0 */ + 1 /* fg */ + 2 /* 2 null char */;
296 unsigned char *data = fmem_alloc(data_size);
298 if (!data) return;
300 data[0] = 0;
301 data[1] = fg;
302 memcpy(data + 2, path, plen + 1);
303 memcpy(data + 2 + plen + 1, delete, dlen + 1);
304 hard_write(term->fdout, data, data_size);
305 fmem_free(data);
308 void
309 exec_on_terminal(struct terminal *term, unsigned char *path,
310 unsigned char *delete, enum term_exec fg)
312 if (path) {
313 if (!*path) return;
314 } else {
315 path = "";
318 #ifdef NO_FG_EXEC
319 fg = TERM_EXEC_BG;
320 #endif
322 if (term->master) {
323 if (!*path) {
324 dispatch_special(delete);
325 return;
328 /* TODO: Should this be changed to allow TERM_EXEC_NEWWIN
329 * in a blocked terminal? There is similar code in
330 * in_sock(). --KON, 2007 */
331 if (fg != TERM_EXEC_BG && is_blocked()) {
332 unlink(delete);
333 return;
336 exec_on_master_terminal(term,
337 path, strlen(path),
338 delete, strlen(delete),
339 fg);
340 } else {
341 exec_on_slave_terminal( term,
342 path, strlen(path),
343 delete, strlen(delete),
344 fg);
348 void
349 exec_shell(struct terminal *term)
351 unsigned char *sh;
353 if (!can_open_os_shell(term->environment)) return;
355 sh = get_shell();
356 if (sh && *sh)
357 exec_on_terminal(term, sh, "", TERM_EXEC_FG);
361 void
362 do_terminal_function(struct terminal *term, unsigned char code,
363 unsigned char *data)
365 int data_len = strlen(data);
366 unsigned char *x_data = fmem_alloc(data_len + 1 /* code */ + 1 /* null char */);
368 if (!x_data) return;
369 x_data[0] = code;
370 memcpy(x_data + 1, data, data_len + 1);
371 exec_on_terminal(term, NULL, x_data, TERM_EXEC_BG);
372 fmem_free(x_data);
375 void
376 set_terminal_title(struct terminal *term, unsigned char *title)
378 if (term->title && !strcmp(title, term->title)) return;
379 mem_free_set(&term->title, stracpy(title));
380 do_terminal_function(term, TERM_FN_TITLE, title);
383 static int terminal_pipe[2];
386 check_terminal_pipes(void)
388 return c_pipe(terminal_pipe);
391 void
392 close_terminal_pipes(void)
394 close(terminal_pipe[0]);
395 close(terminal_pipe[1]);
398 struct terminal *
399 attach_terminal(int in, int out, int ctl, void *info, int len)
401 struct terminal *term;
403 if (set_nonblocking_fd(terminal_pipe[0]) < 0) return NULL;
404 if (set_nonblocking_fd(terminal_pipe[1]) < 0) return NULL;
405 handle_trm(in, out, out, terminal_pipe[1], ctl, info, len, 0);
407 term = init_term(terminal_pipe[0], out);
408 if (!term) {
409 close_terminal_pipes();
410 return NULL;
413 return term;
416 static struct module *terminal_submodules[] = {
417 &terminal_screen_module,
418 NULL
421 struct module terminal_module = struct_module(
422 /* Because this module is listed in main_modules rather than
423 * in builtin_modules, its name does not appear in the user
424 * interface and so need not be translatable. */
425 /* name: */ "Terminal",
426 /* options: */ NULL,
427 /* hooks: */ NULL,
428 /* submodules: */ terminal_submodules,
429 /* data: */ NULL,
430 /* init: */ NULL,
431 /* done: */ NULL