Detection of UTF-8 mode doesn't work well on slave terminals.
[elinks.git] / src / terminal / terminal.c
blob3f872934c04c7d020cf14ce9105156537c85f651
1 /* Terminal interface - low-level displaying implementation. */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <stdlib.h>
8 #include <string.h>
9 #include <sys/types.h>
10 #ifdef HAVE_UNISTD_H
11 #include <unistd.h>
12 #endif
14 #include "elinks.h"
16 #include "bookmarks/bookmarks.h"
17 #include "config/options.h"
18 #include "intl/gettext/libintl.h"
19 #include "main/main.h"
20 #include "main/module.h"
21 #include "main/object.h"
22 #include "main/select.h"
23 #include "osdep/osdep.h"
24 #include "osdep/signals.h"
25 #include "session/session.h"
26 #include "terminal/draw.h"
27 #include "terminal/event.h"
28 #include "terminal/hardio.h"
29 #include "terminal/kbd.h"
30 #include "terminal/screen.h"
31 #include "terminal/terminal.h"
32 #include "terminal/window.h"
33 #include "util/error.h"
34 #include "util/memory.h"
35 #include "util/string.h"
36 #include "viewer/text/textarea.h"
39 INIT_LIST_HEAD(terminals);
41 static void check_if_no_terminal(void);
43 #if 0
44 static int
45 was_utf8(int in, int out)
47 /* Taken from setedit.
48 * Set cursor in the up left corner. Write "\357\200\240" == U+F020.
49 * Read cursor position. For UTF-8 x will be 2.
50 * For normal mode it will be 4. */
51 static unsigned char *str = "\033[1;1H\357\200\240\033[6n";
52 unsigned char buf[20];
53 int x, y;
55 hard_write(out, str, strlen(str));
56 buf[0] = '\0';
57 read(in, buf, 6);
58 if (sscanf(buf,"\033[%d;%dR",&y,&x)==2) {
59 if (x > 2) return 0;
61 return 1;
63 #endif
65 void
66 redraw_terminal(struct terminal *term)
68 struct term_event ev;
70 set_redraw_term_event(&ev, term->width, term->height);
71 term_send_event(term, &ev);
74 void
75 redraw_terminal_cls(struct terminal *term)
77 struct term_event ev;
79 set_resize_term_event(&ev, term->width, term->height);
80 term_send_event(term, &ev);
83 void
84 cls_redraw_all_terminals(void)
86 struct terminal *term;
88 foreach (term, terminals)
89 redraw_terminal_cls(term);
92 struct terminal *
93 init_term(int fdin, int fdout)
95 unsigned char name[MAX_TERM_LEN + 9] = "terminal.";
96 struct terminal *term = mem_calloc(1, sizeof(*term));
98 if (!term) {
99 check_if_no_terminal();
100 return NULL;
103 term->screen = init_screen();
104 if (!term->screen) {
105 mem_free(term);
106 return NULL;
109 init_list(term->windows);
111 term->fdin = fdin;
112 term->fdout = fdout;
113 term->master = (term->fdout == get_output_handle());
114 term->blocked = -1;
116 get_terminal_name(name + 9);
117 term->spec = get_opt_rec(config_options, name);
118 object_lock(term->spec);
120 #if 0
121 /* The hack to restore console in the right mode */
122 if (get_opt_int_tree(term->spec, "type") == TERM_LINUX) {
123 term->linux_was_utf8 = was_utf8(get_input_handle(), term->fdout);
125 #endif
127 add_to_list(terminals, term);
129 set_handlers(fdin, (select_handler_T) in_term, NULL,
130 (select_handler_T) destroy_terminal, term);
131 return term;
134 void
135 redraw_all_terminals(void)
137 struct terminal *term;
139 foreach (term, terminals)
140 redraw_screen(term);
143 void
144 destroy_terminal(struct terminal *term)
146 #ifdef CONFIG_BOOKMARKS
147 bookmark_auto_save_tabs(term);
148 #endif
150 /* delete_window doesn't update term->current_tab, but it
151 calls redraw_terminal, which requires term->current_tab
152 to be valid if there are any tabs left. So set a value
153 that will be valid for that long. */
154 term->current_tab = 0;
156 while (!list_empty(term->windows))
157 delete_window(term->windows.next);
159 /* mem_free_if(term->cwd); */
160 mem_free_if(term->title);
161 if (term->screen) done_screen(term->screen);
163 clear_handlers(term->fdin);
164 mem_free_if(term->interlink);
166 if (term->blocked != -1) {
167 close(term->blocked);
168 clear_handlers(term->blocked);
171 del_from_list(term);
172 close(term->fdin);
174 #if 0
175 /* This code doesn't work with slave terminals. */
176 if (get_opt_int_tree(term->spec, "type") == TERM_LINUX) {
177 if (term->linux_was_utf8) {
178 hard_write(term->fdout, "\033%G", 3);
179 } else {
180 hard_write(term->fdout, "\033%@", 3);
183 #endif
185 if (term->fdout != 1) {
186 if (term->fdout != term->fdin) close(term->fdout);
187 } else {
188 unhandle_terminal_signals(term);
189 free_all_itrms();
190 #ifndef NO_FORK_ON_EXIT
191 if (!list_empty(terminals)) {
192 if (fork()) exit(0);
194 #endif
197 object_unlock(term->spec);
198 mem_free(term);
199 check_if_no_terminal();
202 void
203 destroy_all_terminals(void)
205 while (!list_empty(terminals))
206 destroy_terminal(terminals.next);
209 static void
210 check_if_no_terminal(void)
212 program.terminate = list_empty(terminals)
213 && !get_opt_bool("ui.sessions.keep_session_active");
216 void
217 exec_thread(unsigned char *path, int p)
219 int plen = strlen(path + 1) + 2;
221 #if defined(HAVE_SETPGID) && !defined(CONFIG_OS_BEOS) && !defined(HAVE_BEGINTHREAD)
222 if (path[0] == 2) setpgid(0, 0);
223 #endif
224 exe(path + 1);
225 if (path[plen]) unlink(path + plen);
228 void
229 close_handle(void *h)
231 close((long) h);
232 clear_handlers((long) h);
235 static void
236 unblock_terminal(struct terminal *term)
238 close_handle((void *) (long) term->blocked);
239 term->blocked = -1;
240 set_handlers(term->fdin, (select_handler_T) in_term, NULL,
241 (select_handler_T) destroy_terminal, term);
242 unblock_itrm(term->fdin);
243 redraw_terminal_cls(term);
244 if (textarea_editor) /* XXX */
245 textarea_edit(1, NULL, NULL, NULL, NULL);
249 static void
250 exec_on_master_terminal(struct terminal *term,
251 unsigned char *path, int plen,
252 unsigned char *delete, int dlen,
253 int fg)
255 int blockh;
256 int param_size = plen + dlen + 2 /* 2 null char */ + 1 /* fg */;
257 unsigned char *param = fmem_alloc(param_size);
259 if (!param) return;
261 param[0] = fg;
262 memcpy(param + 1, path, plen + 1);
263 memcpy(param + 1 + plen + 1, delete, dlen + 1);
265 if (fg == 1) block_itrm(term->fdin);
267 blockh = start_thread((void (*)(void *, int)) exec_thread,
268 param, param_size);
269 fmem_free(param);
270 if (blockh == -1) {
271 if (fg == 1) unblock_itrm(term->fdin);
272 return;
275 if (fg == 1) {
276 term->blocked = blockh;
277 set_handlers(blockh,
278 (select_handler_T) unblock_terminal,
279 NULL,
280 (select_handler_T) unblock_terminal,
281 term);
282 set_handlers(term->fdin, NULL, NULL,
283 (select_handler_T) destroy_terminal,
284 term);
286 } else {
287 set_handlers(blockh, close_handle, NULL,
288 close_handle, (void *) (long) blockh);
292 static void
293 exec_on_slave_terminal( struct terminal *term,
294 unsigned char *path, int plen,
295 unsigned char *delete, int dlen,
296 int fg)
298 int data_size = plen + dlen + 1 /* 0 */ + 1 /* fg */ + 2 /* 2 null char */;
299 unsigned char *data = fmem_alloc(data_size);
301 if (!data) return;
303 data[0] = 0;
304 data[1] = fg;
305 memcpy(data + 2, path, plen + 1);
306 memcpy(data + 2 + plen + 1, delete, dlen + 1);
307 hard_write(term->fdout, data, data_size);
308 fmem_free(data);
311 void
312 exec_on_terminal(struct terminal *term, unsigned char *path,
313 unsigned char *delete, int fg)
315 if (path) {
316 if (!*path) return;
317 } else {
318 path = "";
321 #ifdef NO_FG_EXEC
322 fg = 0;
323 #endif
325 if (term->master) {
326 if (!*path) {
327 dispatch_special(delete);
328 return;
331 if (fg && 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, "", 1);
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, 0);
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 /* name: */ "Terminal",
423 /* options: */ NULL,
424 /* hooks: */ NULL,
425 /* submodules: */ terminal_submodules,
426 /* data: */ NULL,
427 /* init: */ NULL,
428 /* done: */ NULL