1 /* Event system support routines. */
2 
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
6 
7 #include <errno.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #ifdef HAVE_UNISTD_H
11 #include <unistd.h>
12 #endif
13 
14 #include "elinks.h"
15 
16 #include "intl/gettext/libintl.h"
17 #include "main/main.h"			/* terminate */
18 #include "main/object.h"
19 #include "session/session.h"
20 #include "terminal/draw.h"
21 #include "terminal/event.h"
22 #include "terminal/kbd.h"
23 #include "terminal/mouse.h"
24 #include "terminal/tab.h"
25 #include "terminal/terminal.h"
26 #include "terminal/screen.h"
27 #include "terminal/window.h"
28 #include "util/conv.h"
29 #include "util/error.h"
30 #include "util/memory.h"
31 #include "util/snprintf.h"
32 #include "util/string.h"
33 #include "viewer/timer.h"
34 
35 
36 /* Information used for communication between ELinks instances */
37 struct terminal_interlink {
38 	/* How big the input queue is and how much is free */
39 	int qlen;
40 	int qfreespace;
41 
42 	/* UTF8 input key value decoding data. */
43 	struct {
44 		unicode_val_T ucs;
45 		int len;
46 		int min;
47 	} utf_8;
48 
49 	/* This is the queue of events as coming from the other ELinks instance
50 	 * owning the hosting terminal. */
51 	unsigned char input_queue[1];
52 };
53 
54 
55 void
term_send_event(struct terminal * term,struct term_event * ev)56 term_send_event(struct terminal *term, struct term_event *ev)
57 {
58 	struct window *win;
59 
60 	assert(ev && term);
61 	if_assert_failed return;
62 
63 	switch (ev->ev) {
64 	case EVENT_INIT:
65 	case EVENT_RESIZE:
66 	{
67 		int width = ev->info.size.width;
68 		int height = ev->info.size.height;
69 
70 		if (width < 0 || height < 0) {
71 			ERROR(gettext("Bad terminal size: %d, %d"),
72 			      width, height);
73 			break;
74 		}
75 
76 		resize_screen(term, width, height);
77 		erase_screen(term);
78 		/* Fall through */
79 	}
80 	case EVENT_REDRAW:
81 		/* Nasty hack to avoid assertion failures when doing -remote
82 		 * stuff and the client exits right away */
83 		if (!term->screen->image) break;
84 
85 		clear_terminal(term);
86 		term->redrawing = TREDRAW_DELAYED;
87 		/* Note that you do NOT want to ever go and create new
88 		 * window inside EVENT_INIT handler (it'll get second
89 		 * EVENT_INIT here). Perhaps the best thing you could do
90 		 * is registering a bottom-half handler which will open
91 		 * additional windows.
92 		 * --pasky */
93 		if (ev->ev == EVENT_RESIZE) {
94 			/* We want to propagate EVENT_RESIZE even to inactive
95 			 * tabs! Nothing wrong will get drawn (in the final
96 			 * result) as the active tab is always the first one,
97 			 * thus will be drawn last here. Thanks, Witek!
98 			 * --pasky */
99 			foreachback (win, term->windows)
100 				win->handler(win, ev);
101 
102 		} else {
103 			foreachback (win, term->windows)
104 				if (!inactive_tab(win))
105 					win->handler(win, ev);
106 		}
107 		term->redrawing = TREDRAW_READY;
108 		break;
109 
110 	case EVENT_MOUSE:
111 	case EVENT_KBD:
112 	case EVENT_ABORT:
113 		assert(!list_empty(term->windows));
114 		if_assert_failed break;
115 
116 		/* We need to send event to correct tab, not to the first one. --karpov */
117 		/* ...if we want to send it to a tab at all. --pasky */
118 		win = term->windows.next;
119 		if (win->type == WINDOW_TAB) {
120 			win = get_current_tab(term);
121 			assertm(win, "No tab to send the event to!");
122 			if_assert_failed return;
123 		}
124 
125 		win->handler(win, ev);
126 	}
127 }
128 
129 static void
term_send_ucs(struct terminal * term,struct term_event * ev,unicode_val_T u)130 term_send_ucs(struct terminal *term, struct term_event *ev, unicode_val_T u)
131 {
132 	unsigned char *recoded;
133 
134 	recoded = u2cp_no_nbsp(u, get_opt_codepage_tree(term->spec, "charset"));
135 	if (!recoded) recoded = "*";
136 	while (*recoded) {
137 		ev->info.keyboard.key = *recoded;
138 		term_send_event(term, ev);
139 		recoded++;
140 	}
141 }
142 
143 static void
check_terminal_name(struct terminal * term,struct terminal_info * info)144 check_terminal_name(struct terminal *term, struct terminal_info *info)
145 {
146 	unsigned char name[MAX_TERM_LEN + 10];
147 	int i;
148 
149 	/* We check TERM env. var for sanity, and fallback to _template_ if
150 	 * needed. This way we prevent elinks.conf potential corruption. */
151 	for (i = 0; info->name[i]; i++) {
152 		if (isident(info->name[i])) continue;
153 
154 		usrerror(_("Warning: terminal name contains illicit chars.", term));
155 		return;
156 	}
157 
158 	snprintf(name, sizeof(name), "terminal.%s", info->name);
159 
160 	/* Unlock the default _template_ option tree that was asigned by
161 	 * init_term() and get the correct one. */
162 	object_unlock(term->spec);
163 	term->spec = get_opt_rec(config_options, name);
164 	object_lock(term->spec);
165 }
166 
167 #ifdef CONFIG_MOUSE
168 static int
ignore_mouse_event(struct terminal * term,struct term_event * ev)169 ignore_mouse_event(struct terminal *term, struct term_event *ev)
170 {
171 	struct term_event_mouse *prev = &term->prev_mouse_event;
172 	struct term_event_mouse *current = &ev->info.mouse;
173 
174 	if (check_mouse_action(ev, B_UP)
175 	    && current->y == prev->y
176 	    && (current->button & ~BM_ACT) == (prev->button & ~BM_ACT)) {
177 		do_not_ignore_next_mouse_event(term);
178 
179 		return 1;
180 	}
181 
182 	copy_struct(prev, current);
183 
184 	return 0;
185 }
186 #endif
187 
188 static int
handle_interlink_event(struct terminal * term,struct term_event * ev)189 handle_interlink_event(struct terminal *term, struct term_event *ev)
190 {
191 	struct terminal_info *info = NULL;
192 	struct terminal_interlink *interlink = term->interlink;
193 
194 	switch (ev->ev) {
195 	case EVENT_INIT:
196 		if (interlink->qlen < TERMINAL_INFO_SIZE)
197 			return 0;
198 
199 		info = (struct terminal_info *) ev;
200 
201 		if (interlink->qlen < TERMINAL_INFO_SIZE + info->length)
202 			return 0;
203 
204 		info->name[MAX_TERM_LEN - 1] = 0;
205 		check_terminal_name(term, info);
206 
207 		memcpy(term->cwd, info->cwd, MAX_CWD_LEN);
208 		term->cwd[MAX_CWD_LEN - 1] = 0;
209 
210 		term->environment = info->system_env;
211 
212 		/* We need to make sure that it is possible to draw on the
213 		 * terminal screen before decoding the session info so that
214 		 * handling of bad URL syntax by openning msg_box() will be
215 		 * possible. */
216 		term_send_event(term, ev);
217 
218 		/* Either the initialization of the first session failed or we
219 		 * are doing a remote session so quit.*/
220 		if (!decode_session_info(term, info)) {
221 			destroy_terminal(term);
222 			/* Make sure the user is notified if the initialization
223 			 * of the first session fails. */
224 			if (program.terminate) {
225 				usrerror(_("Failed to create session.", term));
226 				program.retval = RET_FATAL;
227 			}
228 			return 0;
229 		}
230 
231 		ev->ev = EVENT_REDRAW;
232 		/* Fall through */
233 	case EVENT_REDRAW:
234 	case EVENT_RESIZE:
235 		term_send_event(term, ev);
236 		break;
237 
238 	case EVENT_MOUSE:
239 #ifdef CONFIG_MOUSE
240 		reset_timer();
241 		if (!ignore_mouse_event(term, ev))
242 			term_send_event(term, ev);
243 #endif
244 		break;
245 
246 	case EVENT_KBD:
247 	{
248 		int utf8_io = -1;
249 		int key = get_kbd_key(ev);
250 
251 		reset_timer();
252 
253 		if (check_kbd_modifier(ev, KBD_MOD_CTRL) && c_toupper(key) == 'L') {
254 			redraw_terminal_cls(term);
255 			break;
256 
257 		} else if (key == KBD_CTRL_C) {
258 			destroy_terminal(term);
259 			return 0;
260 		}
261 
262 		if (interlink->utf_8.len) {
263 			utf8_io = get_opt_bool_tree(term->spec, "utf_8_io");
264 
265 			if ((key & 0xC0) == 0x80 && utf8_io) {
266 				interlink->utf_8.ucs <<= 6;
267 				interlink->utf_8.ucs |= key & 0x3F;
268 				if (! --interlink->utf_8.len) {
269 					unicode_val_T u = interlink->utf_8.ucs;
270 
271 					if (u < interlink->utf_8.min)
272 						u = UCS_NO_CHAR;
273 					term_send_ucs(term, ev, u);
274 				}
275 				break;
276 
277 			} else {
278 				interlink->utf_8.len = 0;
279 				term_send_ucs(term, ev, UCS_NO_CHAR);
280 			}
281 		}
282 
283 		if (key < 0x80 || key > 0xFF
284 		    || (utf8_io == -1
285 			? !get_opt_bool_tree(term->spec, "utf_8_io")
286 			: !utf8_io)) {
287 
288 			term_send_event(term, ev);
289 			break;
290 
291 		} else if ((key & 0xC0) == 0xC0 && (key & 0xFE) != 0xFE) {
292 			unsigned int mask, cov = 0x80;
293 			int len = 0;
294 
295 			for (mask = 0x80; key & mask; mask >>= 1) {
296 				len++;
297 				interlink->utf_8.min = cov;
298 				cov = 1 << (1 + 5 * len);
299 			}
300 
301 			interlink->utf_8.len = len - 1;
302 			interlink->utf_8.ucs = key & (mask - 1);
303 			break;
304 		}
305 
306 		term_send_ucs(term, ev, UCS_NO_CHAR);
307 		break;
308 	}
309 
310 	case EVENT_ABORT:
311 		destroy_terminal(term);
312 		return 0;
313 
314 	default:
315 		ERROR(gettext("Bad event %d"), ev->ev);
316 	}
317 
318 	/* For EVENT_INIT we read a liitle more */
319 	if (info) return TERMINAL_INFO_SIZE + info->length;
320 	return sizeof(*ev);
321 }
322 
323 void
in_term(struct terminal * term)324 in_term(struct terminal *term)
325 {
326 	struct terminal_interlink *interlink = term->interlink;
327 	ssize_t r;
328 	unsigned char *iq;
329 
330 	if (!interlink
331 	    || !interlink->qfreespace
332 	    || interlink->qfreespace - interlink->qlen > ALLOC_GR) {
333 		int qlen = interlink ? interlink->qlen : 0;
334 		int queuesize = ((qlen + ALLOC_GR) & ~(ALLOC_GR - 1));
335 		int newsize = sizeof(*interlink) + queuesize;
336 
337 		interlink = mem_realloc(interlink, newsize);
338 		if (!interlink) {
339 			destroy_terminal(term);
340 			return;
341 		}
342 
343 		/* Blank the members for the first allocation */
344 		if (!term->interlink)
345 			memset(interlink, 0, sizeof(*interlink));
346 
347 		term->interlink = interlink;
348 		interlink->qfreespace = queuesize - interlink->qlen;
349 	}
350 
351 	iq = interlink->input_queue;
352 	r = safe_read(term->fdin, iq + interlink->qlen, interlink->qfreespace);
353 	if (r <= 0) {
354 		if (r == -1 && errno != ECONNRESET)
355 			ERROR(gettext("Could not read event: %d (%s)"),
356 			      errno, (unsigned char *) strerror(errno));
357 
358 		destroy_terminal(term);
359 		return;
360 	}
361 
362 	interlink->qlen += r;
363 	interlink->qfreespace -= r;
364 
365 	while (interlink->qlen >= sizeof(struct term_event)) {
366 		struct term_event *ev = (struct term_event *) iq;
367 		int event_size = handle_interlink_event(term, ev);
368 
369 		/* If the event was not handled save the bytes in the queue for
370 		 * later in case more stuff is read later. */
371 		if (!event_size) break;
372 
373 		/* Acount for the handled bytes */
374 		interlink->qlen -= event_size;
375 		interlink->qfreespace += event_size;
376 
377 		/* If there are no more bytes to handle stop else move next
378 		 * event bytes to the front of the queue. */
379 		if (!interlink->qlen) break;
380 		memmove(iq, iq + event_size, interlink->qlen);
381 	}
382 }
383