Fix assertion failure when closing a terminal that has more than one tab.
[elinks.git] / src / terminal / terminal.c
blob26943835e9abb5494752ec515a8b954cd5f6a324
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/object.h"
21 #include "main/select.h"
22 #include "osdep/osdep.h"
23 #include "osdep/signals.h"
24 #include "session/session.h"
25 #include "terminal/draw.h"
26 #include "terminal/event.h"
27 #include "terminal/hardio.h"
28 #include "terminal/kbd.h"
29 #include "terminal/screen.h"
30 #include "terminal/terminal.h"
31 #include "terminal/window.h"
32 #include "util/error.h"
33 #include "util/memory.h"
34 #include "util/string.h"
35 #include "viewer/text/textarea.h"
38 INIT_LIST_HEAD(terminals);
41 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 struct terminal *term = mem_calloc(1, sizeof(*term));
76 if (!term) {
77 check_if_no_terminal();
78 return NULL;
81 term->screen = init_screen();
82 if (!term->screen) {
83 mem_free(term);
84 return NULL;
87 init_list(term->windows);
89 term->fdin = fdin;
90 term->fdout = fdout;
91 term->master = (term->fdout == get_output_handle());
92 term->blocked = -1;
93 term->spec = get_opt_rec(config_options, "terminal._template_");
94 object_lock(term->spec);
96 add_to_list(terminals, term);
98 set_handlers(fdin, (select_handler_T) in_term, NULL,
99 (select_handler_T) destroy_terminal, term);
100 return term;
103 void
104 redraw_all_terminals(void)
106 struct terminal *term;
108 foreach (term, terminals)
109 redraw_screen(term);
112 void
113 destroy_terminal(struct terminal *term)
115 #ifdef CONFIG_BOOKMARKS
116 bookmark_auto_save_tabs(term);
117 #endif
119 /* delete_window doesn't update term->current_tab, but it
120 calls redraw_terminal, which requires term->current_tab
121 to be valid if there are any tabs left. So set a value
122 that will be valid for that long. */
123 term->current_tab = 0;
125 while (!list_empty(term->windows))
126 delete_window(term->windows.next);
128 /* mem_free_if(term->cwd); */
129 mem_free_if(term->title);
130 if (term->screen) done_screen(term->screen);
132 clear_handlers(term->fdin);
133 mem_free_if(term->interlink);
135 if (term->blocked != -1) {
136 close(term->blocked);
137 clear_handlers(term->blocked);
140 del_from_list(term);
141 close(term->fdin);
143 if (term->fdout != 1) {
144 if (term->fdout != term->fdin) close(term->fdout);
145 } else {
146 unhandle_terminal_signals(term);
147 free_all_itrms();
148 #ifndef NO_FORK_ON_EXIT
149 if (!list_empty(terminals)) {
150 if (fork()) exit(0);
152 #endif
155 object_unlock(term->spec);
156 mem_free(term);
157 check_if_no_terminal();
160 void
161 destroy_all_terminals(void)
163 while (!list_empty(terminals))
164 destroy_terminal(terminals.next);
167 static void
168 check_if_no_terminal(void)
170 program.terminate = list_empty(terminals)
171 && !get_opt_bool("ui.sessions.keep_session_active");
174 void
175 exec_thread(unsigned char *path, int p)
177 int plen = strlen(path + 1) + 2;
179 #if defined(HAVE_SETPGID) && !defined(CONFIG_OS_BEOS) && !defined(HAVE_BEGINTHREAD)
180 if (path[0] == 2) setpgid(0, 0);
181 #endif
182 exe(path + 1);
183 if (path[plen]) unlink(path + plen);
186 void
187 close_handle(void *h)
189 close((long) h);
190 clear_handlers((long) h);
193 static void
194 unblock_terminal(struct terminal *term)
196 close_handle((void *) (long) term->blocked);
197 term->blocked = -1;
198 set_handlers(term->fdin, (select_handler_T) in_term, NULL,
199 (select_handler_T) destroy_terminal, term);
200 unblock_itrm(term->fdin);
201 redraw_terminal_cls(term);
202 if (textarea_editor) /* XXX */
203 textarea_edit(1, NULL, NULL, NULL, NULL);
207 static void
208 exec_on_master_terminal(struct terminal *term,
209 unsigned char *path, int plen,
210 unsigned char *delete, int dlen,
211 int fg)
213 int blockh;
214 int param_size = plen + dlen + 2 /* 2 null char */ + 1 /* fg */;
215 unsigned char *param = fmem_alloc(param_size);
217 if (!param) return;
219 param[0] = fg;
220 memcpy(param + 1, path, plen + 1);
221 memcpy(param + 1 + plen + 1, delete, dlen + 1);
223 if (fg == 1) block_itrm(term->fdin);
225 blockh = start_thread((void (*)(void *, int)) exec_thread,
226 param, param_size);
227 fmem_free(param);
228 if (blockh == -1) {
229 if (fg == 1) unblock_itrm(term->fdin);
230 return;
233 if (fg == 1) {
234 term->blocked = blockh;
235 set_handlers(blockh,
236 (select_handler_T) unblock_terminal,
237 NULL,
238 (select_handler_T) unblock_terminal,
239 term);
240 set_handlers(term->fdin, NULL, NULL,
241 (select_handler_T) destroy_terminal,
242 term);
244 } else {
245 set_handlers(blockh, close_handle, NULL,
246 close_handle, (void *) (long) blockh);
250 static void
251 exec_on_slave_terminal( struct terminal *term,
252 unsigned char *path, int plen,
253 unsigned char *delete, int dlen,
254 int fg)
256 int data_size = plen + dlen + 1 /* 0 */ + 1 /* fg */ + 2 /* 2 null char */;
257 unsigned char *data = fmem_alloc(data_size);
259 if (!data) return;
261 data[0] = 0;
262 data[1] = fg;
263 memcpy(data + 2, path, plen + 1);
264 memcpy(data + 2 + plen + 1, delete, dlen + 1);
265 hard_write(term->fdout, data, data_size);
266 fmem_free(data);
269 void
270 exec_on_terminal(struct terminal *term, unsigned char *path,
271 unsigned char *delete, int fg)
273 if (path) {
274 if (!*path) return;
275 } else {
276 path = "";
279 #ifdef NO_FG_EXEC
280 fg = 0;
281 #endif
283 if (term->master) {
284 if (!*path) {
285 dispatch_special(delete);
286 return;
289 if (fg && is_blocked()) {
290 unlink(delete);
291 return;
294 exec_on_master_terminal(term,
295 path, strlen(path),
296 delete, strlen(delete),
297 fg);
298 } else {
299 exec_on_slave_terminal( term,
300 path, strlen(path),
301 delete, strlen(delete),
302 fg);
306 void
307 exec_shell(struct terminal *term)
309 unsigned char *sh;
311 if (!can_open_os_shell(term->environment)) return;
313 sh = get_shell();
314 if (sh && *sh)
315 exec_on_terminal(term, sh, "", 1);
319 void
320 do_terminal_function(struct terminal *term, unsigned char code,
321 unsigned char *data)
323 int data_len = strlen(data);
324 unsigned char *x_data = fmem_alloc(data_len + 1 /* code */ + 1 /* null char */);
326 if (!x_data) return;
327 x_data[0] = code;
328 memcpy(x_data + 1, data, data_len + 1);
329 exec_on_terminal(term, NULL, x_data, 0);
330 fmem_free(x_data);
333 void
334 set_terminal_title(struct terminal *term, unsigned char *title)
336 if (term->title && !strcmp(title, term->title)) return;
337 mem_free_set(&term->title, stracpy(title));
338 do_terminal_function(term, TERM_FN_TITLE, title);
341 static int terminal_pipe[2];
344 check_terminal_pipes(void)
346 return c_pipe(terminal_pipe);
349 void
350 close_terminal_pipes(void)
352 close(terminal_pipe[0]);
353 close(terminal_pipe[1]);
356 struct terminal *
357 attach_terminal(int in, int out, int ctl, void *info, int len)
359 struct terminal *term;
361 if (set_nonblocking_fd(terminal_pipe[0]) < 0) return NULL;
362 if (set_nonblocking_fd(terminal_pipe[1]) < 0) return NULL;
363 handle_trm(in, out, out, terminal_pipe[1], ctl, info, len, 0);
365 term = init_term(terminal_pipe[0], out);
366 if (!term) {
367 close_terminal_pipes();
368 return NULL;
371 return term;