Comment the UTF-8 decoding in handle_interlink_event
[elinks.git] / src / terminal / event.c
blobac7794b74e58be860002c7d1829e57503b40a92b
1 /* Event system support routines. */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <errno.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #ifdef HAVE_UNISTD_H
11 #include <unistd.h>
12 #endif
14 #include "elinks.h"
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"
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;
42 /* UTF8 input key value decoding data. */
43 struct {
44 unicode_val_T ucs;
45 int len;
46 int min;
47 /* Modifier keys from the key event that carried the
48 * first byte of the character. We need this because
49 * ELinks sees e.g. ESC U+00F6 as 0x1B 0xC3 0xB6 and
50 * converts it to Alt-0xC3 0xB6, attaching the
51 * modifier to the first byte only. */
52 int modifier;
53 } utf_8;
55 /* This is the queue of events as coming from the other ELinks instance
56 * owning the hosting terminal. */
57 unsigned char input_queue[1];
61 void
62 term_send_event(struct terminal *term, struct term_event *ev)
64 struct window *win;
66 assert(ev && term);
67 if_assert_failed return;
69 switch (ev->ev) {
70 case EVENT_INIT:
71 case EVENT_RESIZE:
73 int width = ev->info.size.width;
74 int height = ev->info.size.height;
76 if (width < 0 || height < 0) {
77 ERROR(gettext("Bad terminal size: %d, %d"),
78 width, height);
79 break;
82 resize_screen(term, width, height);
83 erase_screen(term);
84 /* Fall through */
86 case EVENT_REDRAW:
87 /* Nasty hack to avoid assertion failures when doing -remote
88 * stuff and the client exits right away */
89 if (!term->screen->image) break;
91 clear_terminal(term);
92 term->redrawing = TREDRAW_DELAYED;
93 /* Note that you do NOT want to ever go and create new
94 * window inside EVENT_INIT handler (it'll get second
95 * EVENT_INIT here). Perhaps the best thing you could do
96 * is registering a bottom-half handler which will open
97 * additional windows.
98 * --pasky */
99 if (ev->ev == EVENT_RESIZE) {
100 /* We want to propagate EVENT_RESIZE even to inactive
101 * tabs! Nothing wrong will get drawn (in the final
102 * result) as the active tab is always the first one,
103 * thus will be drawn last here. Thanks, Witek!
104 * --pasky */
105 foreachback (win, term->windows)
106 win->handler(win, ev);
108 } else {
109 foreachback (win, term->windows)
110 if (!inactive_tab(win))
111 win->handler(win, ev);
113 term->redrawing = TREDRAW_READY;
114 break;
116 case EVENT_MOUSE:
117 case EVENT_KBD:
118 case EVENT_ABORT:
119 assert(!list_empty(term->windows));
120 if_assert_failed break;
122 /* We need to send event to correct tab, not to the first one. --karpov */
123 /* ...if we want to send it to a tab at all. --pasky */
124 win = term->windows.next;
125 if (win->type == WINDOW_TAB) {
126 win = get_current_tab(term);
127 assertm(win, "No tab to send the event to!");
128 if_assert_failed return;
131 win->handler(win, ev);
135 static void
136 term_send_ucs(struct terminal *term, unicode_val_T u, int modifier)
138 #ifdef CONFIG_UTF_8
139 struct term_event ev;
141 set_kbd_term_event(&ev, u, modifier);
142 term_send_event(term, &ev);
143 #else /* !CONFIG_UTF_8 */
144 struct term_event ev;
145 unsigned char *recoded;
147 set_kbd_term_event(&ev, KBD_UNDEF, modifier);
148 recoded = u2cp_no_nbsp(u, get_opt_codepage_tree(term->spec, "charset"));
149 if (!recoded) recoded = "*";
150 while (*recoded) {
151 ev.info.keyboard.key = *recoded;
152 term_send_event(term, &ev);
153 recoded++;
155 #endif /* !CONFIG_UTF_8 */
158 static void
159 check_terminal_name(struct terminal *term, struct terminal_info *info)
161 unsigned char name[MAX_TERM_LEN + 10];
162 int i;
164 /* We check TERM env. var for sanity, and fallback to _template_ if
165 * needed. This way we prevent elinks.conf potential corruption. */
166 for (i = 0; info->name[i]; i++) {
167 if (isident(info->name[i])) continue;
169 usrerror(_("Warning: terminal name contains illicit chars.", term));
170 return;
173 snprintf(name, sizeof(name), "terminal.%s", info->name);
175 /* Unlock the default _template_ option tree that was asigned by
176 * init_term() and get the correct one. */
177 object_unlock(term->spec);
178 term->spec = get_opt_rec(config_options, name);
179 object_lock(term->spec);
180 #ifdef CONFIG_UTF_8
181 /* Probably not best place for set this. But now we finally have
182 * term->spec and term->utf8 should be set before decode session info.
183 * --Scrool */
184 term->utf8 = get_opt_bool_tree(term->spec, "utf_8_io");
185 #endif /* CONFIG_UTF_8 */
188 #ifdef CONFIG_MOUSE
189 static int
190 ignore_mouse_event(struct terminal *term, struct term_event *ev)
192 struct term_event_mouse *prev = &term->prev_mouse_event;
193 struct term_event_mouse *current = &ev->info.mouse;
195 if (check_mouse_action(ev, B_UP)
196 && current->y == prev->y
197 && (current->button & ~BM_ACT) == (prev->button & ~BM_ACT)) {
198 do_not_ignore_next_mouse_event(term);
200 return 1;
203 copy_struct(prev, current);
205 return 0;
207 #endif
209 static int
210 handle_interlink_event(struct terminal *term, struct interlink_event *ilev)
212 struct terminal_info *info = NULL;
213 struct terminal_interlink *interlink = term->interlink;
214 struct term_event tev;
216 switch (ilev->ev) {
217 case EVENT_INIT:
218 if (interlink->qlen < TERMINAL_INFO_SIZE)
219 return 0;
221 info = (struct terminal_info *) ilev;
223 if (interlink->qlen < TERMINAL_INFO_SIZE + info->length)
224 return 0;
226 info->name[MAX_TERM_LEN - 1] = 0;
227 check_terminal_name(term, info);
229 memcpy(term->cwd, info->cwd, MAX_CWD_LEN);
230 term->cwd[MAX_CWD_LEN - 1] = 0;
232 term->environment = info->system_env;
234 /* We need to make sure that it is possible to draw on the
235 * terminal screen before decoding the session info so that
236 * handling of bad URL syntax by openning msg_box() will be
237 * possible. */
238 set_init_term_event(&tev,
239 ilev->info.size.width,
240 ilev->info.size.height);
241 term_send_event(term, &tev);
243 /* Either the initialization of the first session failed or we
244 * are doing a remote session so quit.*/
245 if (!decode_session_info(term, info)) {
246 destroy_terminal(term);
247 /* Make sure the user is notified if the initialization
248 * of the first session fails. */
249 if (program.terminate) {
250 usrerror(_("Failed to create session.", term));
251 program.retval = RET_FATAL;
253 return 0;
256 ilev->ev = EVENT_REDRAW;
257 /* Fall through */
258 case EVENT_REDRAW:
259 case EVENT_RESIZE:
260 set_wh_term_event(&tev, ilev->ev,
261 ilev->info.size.width,
262 ilev->info.size.height);
263 term_send_event(term, &tev);
264 break;
266 case EVENT_MOUSE:
267 #ifdef CONFIG_MOUSE
268 reset_timer();
269 tev.ev = ilev->ev;
270 copy_struct(&tev.info.mouse, &ilev->info.mouse);
271 if (!ignore_mouse_event(term, &tev))
272 term_send_event(term, &tev);
273 #endif
274 break;
276 case EVENT_KBD:
278 int utf8_io = -1;
279 int key = ilev->info.keyboard.key;
280 int modifier = ilev->info.keyboard.modifier;
282 if (key >= 0x100)
283 key = -key;
285 reset_timer();
287 if (modifier == KBD_MOD_CTRL && (key == 'l' || key == 'L')) {
288 redraw_terminal_cls(term);
289 break;
291 } else if (key == KBD_CTRL_C) {
292 destroy_terminal(term);
293 return 0;
296 /* Character Conversions. */
297 #ifdef CONFIG_UTF_8
298 /* struct term_event_keyboard carries UCS-4.
299 * - If the "utf_8_io" option (i.e. term->utf8) is
300 * true or the "charset" option refers to UTF-8,
301 * then handle_interlink_event() converts from UTF-8
302 * to UCS-4.
303 * - Otherwise, handle_interlink_event() converts from
304 * the codepage specified with the "charset" option
305 * to UCS-4. */
306 utf8_io = term->utf8
307 || is_cp_utf8(get_opt_codepage_tree(term->spec, "charset"));
308 #else
309 /* struct term_event_keyboard carries bytes in the
310 * charset of the terminal.
311 * - If the "utf_8_io" option is true, then
312 * handle_interlink_event() converts from UTF-8 to
313 * UCS-4, and term_send_ucs() converts from UCS-4 to
314 * the codepage specified with the "charset" option;
315 * this codepage cannot be UTF-8.
316 * - Otherwise, handle_interlink_event() passes the
317 * bytes straight through. */
318 utf8_io = get_opt_bool_tree(term->spec, "utf_8_io");
319 #endif /* CONFIG_UTF_8 */
321 /* In UTF-8 byte sequences that have more than one byte, the
322 * first byte is between 0xC0 and 0xFF and the remaining bytes
323 * are between 0x80 and 0xBF. If there is just one byte, then
324 * it is between 0x00 and 0x7F and it is straight ASCII.
325 * (All 'betweens' are inclusive.) */
327 if (interlink->utf_8.len) {
328 /* A previous call to handle_interlink_event
329 * got a UTF-8 start byte. */
331 if (key >= 0x80 && key <= 0xBF && utf8_io) {
332 /* This is a UTF-8 continuation byte. */
334 interlink->utf_8.ucs <<= 6;
335 interlink->utf_8.ucs |= key & 0x3F;
336 if (! --interlink->utf_8.len) {
337 unicode_val_T u = interlink->utf_8.ucs;
339 if (u < interlink->utf_8.min)
340 u = UCS_NO_CHAR;
341 term_send_ucs(term, u,
342 term->interlink->utf_8.modifier);
344 break;
346 } else {
347 /* The byte sequence for this character is
348 * ending prematurely. Send UCS_NO_CHAR for the
349 * terminated character, but don't break; let
350 * this byte be handled below. */
352 interlink->utf_8.len = 0;
353 term_send_ucs(term, UCS_NO_CHAR,
354 term->interlink->utf_8.modifier);
358 if (key < 0x80 || key > 0xFF || !utf8_io) {
359 /* This byte is not part of a multibyte character
360 * encoding: either it is outside of the ranges for
361 * UTF-8 start and continuation bytes or UTF-8 I/O mode
362 * is disabled. */
364 #ifdef CONFIG_UTF_8
365 if (key >= 0 && key <= 0xFF && !utf8_io) {
366 /* Not special and UTF-8 mode is disabled:
367 * recode from the terminal charset to UCS-4. */
369 key = cp2u(get_opt_codepage_tree(term->spec,
370 "charset"),
371 key);
372 term_send_ucs(term, key, modifier);
373 break;
375 #endif /* !CONFIG_UTF_8 */
377 /* It must be special (e.g., F1 or Enter)
378 * or a single-byte UTF-8 character. */
379 set_kbd_term_event(&tev, key, modifier);
380 term_send_event(term, &tev);
381 break;
383 } else if ((key & 0xC0) == 0xC0 && (key & 0xFE) != 0xFE) {
384 /* This is a UTF-8 start byte. */
386 unsigned int mask, cov = 0x80;
387 int len = 0;
389 /* The number of 1's between the first bit and first
390 * 0 bit (exclusive) is the number of remaining
391 * continuation bytes in the encoding of the character
392 * that is now being processed, which number will be
393 * stored in interlink->utf_8.len. */
394 for (mask = 0x80; key & mask; mask >>= 1) {
395 len++;
396 interlink->utf_8.min = cov;
397 cov = 1 << (1 + 5 * len);
400 interlink->utf_8.len = len - 1;
401 interlink->utf_8.ucs = key & (mask - 1);
402 interlink->utf_8.modifier = modifier;
403 break;
406 term_send_ucs(term, UCS_NO_CHAR, modifier);
407 break;
410 case EVENT_ABORT:
411 destroy_terminal(term);
412 return 0;
414 default:
415 ERROR(gettext("Bad event %d"), ilev->ev);
418 /* For EVENT_INIT we read a liitle more */
419 if (info) return TERMINAL_INFO_SIZE + info->length;
420 return sizeof(*ilev);
423 void
424 in_term(struct terminal *term)
426 struct terminal_interlink *interlink = term->interlink;
427 ssize_t r;
428 unsigned char *iq;
430 if (!interlink
431 || !interlink->qfreespace
432 || interlink->qfreespace - interlink->qlen > ALLOC_GR) {
433 int qlen = interlink ? interlink->qlen : 0;
434 int queuesize = ((qlen + ALLOC_GR) & ~(ALLOC_GR - 1));
435 int newsize = sizeof(*interlink) + queuesize;
437 interlink = mem_realloc(interlink, newsize);
438 if (!interlink) {
439 destroy_terminal(term);
440 return;
443 /* Blank the members for the first allocation */
444 if (!term->interlink)
445 memset(interlink, 0, sizeof(*interlink));
447 term->interlink = interlink;
448 interlink->qfreespace = queuesize - interlink->qlen;
451 iq = interlink->input_queue;
452 r = safe_read(term->fdin, iq + interlink->qlen, interlink->qfreespace);
453 if (r <= 0) {
454 if (r == -1 && errno != ECONNRESET)
455 ERROR(gettext("Could not read event: %d (%s)"),
456 errno, (unsigned char *) strerror(errno));
458 destroy_terminal(term);
459 return;
462 interlink->qlen += r;
463 interlink->qfreespace -= r;
465 while (interlink->qlen >= sizeof(struct interlink_event)) {
466 struct interlink_event *ev = (struct interlink_event *) iq;
467 int event_size = handle_interlink_event(term, ev);
469 /* If the event was not handled save the bytes in the queue for
470 * later in case more stuff is read later. */
471 if (!event_size) break;
473 /* Acount for the handled bytes */
474 interlink->qlen -= event_size;
475 interlink->qfreespace += event_size;
477 /* If there are no more bytes to handle stop else move next
478 * event bytes to the front of the queue. */
479 if (!interlink->qlen) break;
480 memmove(iq, iq + event_size, interlink->qlen);