[wip] draw_images(): w, h, spos -> box screen_img; img_box -> pixel_box
[elinks/images.git] / src / terminal / kbd.c
blob91d049a16e0ee599ef2bb5eeac77af3ae78af875
1 /** Support for keyboard interface
2 * @file
4 * @todo TODO: move stuff from here to itrm.{c,h} and mouse.{c,h}
5 */
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif
11 #include <stdlib.h>
12 #include <string.h>
13 #ifdef HAVE_TERMIOS_H
14 #include <termios.h>
15 #endif
16 #ifdef HAVE_UNISTD_H
17 #include <unistd.h>
18 #endif
19 #ifdef __hpux__
20 #include <limits.h>
21 #define HPUX_PIPE (len > PIPE_BUF || errno != EAGAIN)
22 #else
23 #define HPUX_PIPE 1
24 #endif
26 #include "elinks.h"
28 #include "config/options.h"
29 #include "intl/gettext/libintl.h"
30 #include "main/select.h"
31 #include "main/timer.h"
32 #include "osdep/ascii.h"
33 #include "osdep/osdep.h"
34 #include "osdep/image.h"
35 #include "terminal/hardio.h"
36 #include "terminal/itrm.h"
37 #include "terminal/kbd.h"
38 #include "terminal/mouse.h"
39 #include "terminal/terminal.h"
40 #include "util/error.h"
41 #include "util/memory.h"
42 #include "util/string.h"
43 #include "util/time.h"
45 struct itrm *ditrm = NULL;
47 static void free_itrm(struct itrm *);
48 static void in_kbd(struct itrm *);
49 static void in_sock(struct itrm *);
50 static int process_queue(struct itrm *);
51 static void handle_itrm_stdin(struct itrm *);
52 static void unhandle_itrm_stdin(struct itrm *);
54 #ifdef CONFIG_DEBUG
55 /** This hack makes GCC put enum term_event_special_key in the debug
56 * information even though it is not otherwise used. The const
57 * prevents an unused-variable warning. */
58 static const enum term_event_special_key dummy_term_event_special_key;
59 #endif
62 int
63 is_blocked(void)
65 return ditrm && ditrm->blocked;
69 void
70 free_all_itrms(void)
72 if (ditrm) free_itrm(ditrm);
76 /** A select_handler_T write_func for itrm_out.sock. This is called
77 * when there is data in @c itrm->out.queue and it is possible to write
78 * it to @c itrm->out.sock. When @c itrm->out.queue becomes empty, this
79 * handler is temporarily removed. */
80 static void
81 itrm_queue_write(struct itrm *itrm)
83 int written;
84 int qlen = int_min(itrm->out.queue.len, 128);
86 assertm(qlen, "event queue empty");
87 if_assert_failed return;
89 written = safe_write(itrm->out.sock, itrm->out.queue.data, qlen);
90 if (written <= 0) {
91 if (written < 0) free_itrm(itrm); /* write error */
92 return;
95 itrm->out.queue.len -= written;
97 if (itrm->out.queue.len == 0) {
98 set_handlers(itrm->out.sock,
99 get_handler(itrm->out.sock, SELECT_HANDLER_READ),
100 NULL,
101 get_handler(itrm->out.sock, SELECT_HANDLER_ERROR),
102 get_handler(itrm->out.sock, SELECT_HANDLER_DATA));
103 } else {
104 assert(itrm->out.queue.len > 0);
105 memmove(itrm->out.queue.data, itrm->out.queue.data + written, itrm->out.queue.len);
110 void
111 itrm_queue_event(struct itrm *itrm, unsigned char *data, int len)
113 int w = 0;
115 if (!len) return;
117 if (!itrm->out.queue.len && can_write(itrm->out.sock)) {
118 w = safe_write(itrm->out.sock, data, len);
119 if (w <= 0 && HPUX_PIPE) {
120 register_bottom_half(free_itrm, itrm);
121 return;
125 if (w < len) {
126 int left = len - w;
127 unsigned char *c = mem_realloc(itrm->out.queue.data,
128 itrm->out.queue.len + left);
130 if (!c) {
131 free_itrm(itrm);
132 return;
135 itrm->out.queue.data = c;
136 memcpy(itrm->out.queue.data + itrm->out.queue.len, data + w, left);
137 itrm->out.queue.len += left;
138 set_handlers(itrm->out.sock,
139 get_handler(itrm->out.sock, SELECT_HANDLER_READ),
140 (select_handler_T) itrm_queue_write,
141 (select_handler_T) free_itrm, itrm);
146 void
147 kbd_ctrl_c(void)
149 struct interlink_event ev;
151 if (!ditrm) return;
152 /* This is called because of a signal, and there may be input
153 * pending from the terminal, so do not reset
154 * ditrm->bracketed_pasting. */
155 set_kbd_interlink_event(&ev, KBD_CTRL_C, KBD_MOD_NONE);
156 itrm_queue_event(ditrm, (unsigned char *) &ev, sizeof(ev));
159 #define write_sequence(fd, seq) \
160 hard_write(fd, seq, sizeof(seq) - 1)
163 #define INIT_TERMINAL_SEQ "\033)0\0337" /**< Special Character and Line Drawing Set, Save Cursor */
164 #define INIT_ALT_SCREEN_SEQ "\033[?47h" /**< Use Alternate Screen Buffer */
165 #define INIT_BRACKETED_PASTE_SEQ "\033[?2004h" /**< Enable XTerm bracketed paste mode */
167 static void
168 send_init_sequence(int h, int altscreen)
170 write_sequence(h, INIT_TERMINAL_SEQ);
172 /* If alternate screen is supported switch to it. */
173 if (altscreen) {
174 write_sequence(h, INIT_ALT_SCREEN_SEQ);
176 #ifdef CONFIG_MOUSE
177 send_mouse_init_sequence(h);
178 #endif
179 write_sequence(h, INIT_BRACKETED_PASTE_SEQ);
182 #define DONE_CLS_SEQ "\033[2J" /**< Erase in Display, Clear All */
183 #define DONE_TERMINAL_SEQ "\0338\r \b" /**< Restore Cursor (DECRC) + ??? */
184 #define DONE_ALT_SCREEN_SEQ "\033[?47l" /**< Use Normal Screen Buffer */
185 #define DONE_BRACKETED_PASTE_SEQ "\033[?2004l" /**< Disable XTerm bracketed paste mode */
187 static void
188 send_done_sequence(int h, int altscreen)
190 write_sequence(h, DONE_BRACKETED_PASTE_SEQ);
191 write_sequence(h, DONE_CLS_SEQ);
193 #ifdef CONFIG_MOUSE
194 send_mouse_done_sequence(h);
195 #endif
197 /* Switch from alternate screen. */
198 if (altscreen) {
199 write_sequence(h, DONE_ALT_SCREEN_SEQ);
202 write_sequence(h, DONE_TERMINAL_SEQ);
206 #undef write_sequence
208 void
209 resize_terminal(void)
211 struct interlink_event ev;
212 int width, height;
213 int xres, yres;
215 get_terminal_size(ditrm->out.std, &width, &height);
216 #ifdef CONFIG_IMAGES
217 if (ditrm->image_h)
218 slave_images_getres(ditrm->image_h, &xres, &yres);
219 #endif
220 set_resize_interlink_event(&ev, width, height, xres, yres);
221 itrm_queue_event(ditrm, (char *) &ev, sizeof(ev));
224 void
225 get_terminal_name(unsigned char name[MAX_TERM_LEN])
227 unsigned char *term = getenv("TERM");
228 int i;
230 memset(name, 0, MAX_TERM_LEN);
232 if (!term) return;
234 for (i = 0; term[i] != 0 && i < MAX_TERM_LEN - 1; i++)
235 name[i] = isident(term[i]) ? term[i] : '-';
239 static int
240 setraw(struct itrm *itrm, int save_orig)
242 struct termios t;
243 long vdisable = -1;
245 memset(&t, 0, sizeof(t));
246 if (tcgetattr(itrm->in.ctl, &t)) return -1;
248 if (save_orig) copy_struct(&itrm->t, &t);
250 #ifdef _POSIX_VDISABLE
251 vdisable = _POSIX_VDISABLE;
252 #elif defined(HAVE_FPATHCONF)
253 vdisable = fpathconf(itrm->in.ctl, _PC_VDISABLE);
254 #endif
256 #ifdef VERASE
257 /* Is VERASE defined on Windows? */
258 if (vdisable != -1 && t.c_cc[VERASE] == vdisable)
259 itrm->verase = -1;
260 else
261 itrm->verase = (unsigned char) t.c_cc[VERASE];
262 #else
263 itrm->verase = -1;
264 #endif
266 elinks_cfmakeraw(&t);
267 t.c_lflag |= ISIG;
268 #ifdef TOSTOP
269 t.c_lflag |= TOSTOP;
270 #endif
271 t.c_oflag |= OPOST;
272 if (tcsetattr(itrm->in.ctl, TCSANOW, &t)) return -1;
274 return 0;
277 /** Construct the struct itrm of this process, make ::ditrm point to it,
278 * set up select() handlers, and send the initial interlink packet.
280 * The first five parameters are file descriptors that this function
281 * saves in submembers of struct itrm, and for which this function may
282 * set select() handlers. Please see the definitions of struct
283 * itrm_in and struct itrm_out for further explanations.
285 * @param std_in itrm_in.std: read tty device (or pipe)
286 * @param std_out itrm_out.std: write tty device (or pipe)
287 * @param sock_in itrm_in.sock
288 * - If master: == @a std_out (masterhood flag)
289 * - If slave: read socket from master
290 * @param sock_out itrm_out.sock
291 * - If master: write pipe to same process
292 * - If slave: write socket to master
293 * @param ctl_in itrm_in.ctl: control tty device
295 * The remaining three parameters control the initial interlink packet.
297 * @param init_string A string to be passed to the master process. Need
298 * not be null-terminated. If @a remote == 0, this is
299 * a URI. Otherwise, this is a remote command.
300 * @param init_len The length of init_string, in bytes.
301 * @param remote = 0 if asking the master to start a new session
302 * and display it via this process. Otherwise,
303 * enum ::remote_session_flags. */
304 void
305 handle_trm(int std_in, int std_out, int sock_in, int sock_out, int ctl_in,
306 void *init_string, int init_len, int remote)
308 struct itrm *itrm;
309 struct terminal_info info;
310 struct interlink_event_size *size = &info.event.info.size;
311 unsigned char *ts;
313 memset(&info, 0, sizeof(info));
315 get_terminal_size(ctl_in, &size->width, &size->height);
316 info.event.ev = EVENT_INIT;
317 info.system_env = get_system_env();
318 info.length = init_len;
320 if (remote) {
321 info.session_info = remote;
322 info.magic = INTERLINK_REMOTE_MAGIC;
323 } else {
324 info.session_info = get_cmd_opt_int("base-session");
325 info.magic = INTERLINK_NORMAL_MAGIC;
328 itrm = mem_calloc(1, sizeof(*itrm));
329 if (!itrm) return;
331 itrm->in.queue.data = mem_calloc(1, ITRM_IN_QUEUE_SIZE);
332 if (!itrm->in.queue.data) {
333 mem_free(itrm);
334 return;
337 ditrm = itrm;
338 itrm->in.std = std_in;
339 itrm->out.std = std_out;
340 itrm->in.sock = sock_in;
341 itrm->out.sock = sock_out;
342 itrm->in.ctl = ctl_in;
343 itrm->timer = TIMER_ID_UNDEF;
344 itrm->remote = !!remote;
346 /* If the master does not tell which charset it's using in
347 * this terminal, assume it's some ISO 8859. Because that's
348 * what older versions of ELinks did. */
349 itrm->title_codepage = get_cp_index("ISO-8859-1");
351 /* FIXME: Combination altscreen + xwin does not work as it should,
352 * mouse clicks are reportedly partially ignored. */
353 if (info.system_env & (ENV_SCREEN | ENV_XWIN))
354 itrm->altscreen = 1;
356 if (!remote) {
357 if (ctl_in >= 0) setraw(itrm, 1);
358 send_init_sequence(std_out, itrm->altscreen);
359 handle_terminal_resize(ctl_in, resize_terminal);
360 #ifdef CONFIG_MOUSE
361 enable_mouse();
362 #endif
363 #ifdef CONFIG_IMAGES
364 itrm->image_h = init_images();
365 if (itrm->image_h)
366 slave_images_getres(itrm->image_h, &size->xres, &size->yres);
367 #endif
368 handle_itrm_stdin(itrm);
369 } else {
370 /* elinks -remote may not have a valid stdin if not run from a tty (bug 938) */
371 if (std_in >= 0) handle_itrm_stdin(itrm);
374 if (sock_in != std_out)
375 set_handlers(sock_in, (select_handler_T) in_sock,
376 NULL, (select_handler_T) free_itrm, itrm);
378 get_terminal_name(info.name);
380 ts = get_cwd();
381 if (ts) {
382 memcpy(info.cwd, ts, int_min(strlen(ts), MAX_CWD_LEN));
383 mem_free(ts);
386 itrm_queue_event(itrm, (char *) &info, TERMINAL_INFO_SIZE);
387 itrm_queue_event(itrm, (char *) init_string, init_len);
391 /** A select_handler_T read_func and error_func for the pipe (long) @a h.
392 * This is called when the subprocess started on the terminal of this
393 * ELinks process exits. ELinks then resumes using the terminal. */
394 static void
395 unblock_itrm_x(void *h)
397 close_handle(h);
398 if (!ditrm) return;
399 unblock_itrm();
401 /* Note: External editor support depends on this resize event. */
402 resize_terminal();
407 unblock_itrm(void)
409 if (!ditrm) return -1;
411 if (ditrm->in.ctl >= 0 && setraw(ditrm, 0)) return -1;
412 ditrm->blocked = 0;
413 send_init_sequence(ditrm->out.std, ditrm->altscreen);
415 handle_itrm_stdin(ditrm);
416 resume_mouse(ditrm->mouse_h);
418 handle_terminal_resize(ditrm->in.ctl, resize_terminal);
419 unblock_stdin();
421 return 0;
425 void
426 block_itrm(void)
428 if (!ditrm) return;
430 ditrm->blocked = 1;
431 block_stdin();
432 kill_timer(&ditrm->timer);
433 ditrm->in.queue.len = 0;
434 unhandle_terminal_resize(ditrm->in.ctl);
435 send_done_sequence(ditrm->out.std, ditrm->altscreen);
436 tcsetattr(ditrm->in.ctl, TCSANOW, &ditrm->t);
437 unhandle_itrm_stdin(ditrm);
438 suspend_mouse(ditrm->mouse_h);
442 static void
443 free_itrm(struct itrm *itrm)
445 if (!itrm) return;
447 if (!itrm->remote) {
448 if (itrm->orig_title && *itrm->orig_title) {
449 set_window_title(itrm->orig_title, itrm->title_codepage);
451 } else if (itrm->touched_title) {
452 /* Set the window title to the value of $TERM if X11
453 * wasn't compiled in. Should hopefully make at least
454 * half the users happy. (debian bug #312955) */
455 unsigned char title[MAX_TERM_LEN];
457 get_terminal_name(title);
458 if (*title)
459 set_window_title(title,
460 get_cp_index("US-ASCII"));
464 unhandle_terminal_resize(itrm->in.ctl);
465 #ifdef CONFIG_MOUSE
466 disable_mouse();
467 #endif
468 send_done_sequence(itrm->out.std, itrm->altscreen);
469 tcsetattr(itrm->in.ctl, TCSANOW, &itrm->t);
472 mem_free_set(&itrm->orig_title, NULL);
474 /* elinks -remote may not have a valid stdin if not run from a tty (bug 938) */
475 if (!itrm->remote || itrm->in.std >= 0) clear_handlers(itrm->in.std);
476 clear_handlers(itrm->in.sock);
477 clear_handlers(itrm->out.std);
478 clear_handlers(itrm->out.sock);
480 kill_timer(&itrm->timer);
482 if (itrm == ditrm) ditrm = NULL;
483 mem_free_if(itrm->out.queue.data);
484 mem_free_if(itrm->in.queue.data);
485 mem_free(itrm);
488 /** Resize terminal to dimensions specified by @a text string.
489 * @a text should look like "width,height,old-width,old-height"
490 * where width and height are integers. */
491 static inline void
492 resize_terminal_from_str(unsigned char *text)
494 enum { NEW_WIDTH = 0, NEW_HEIGHT, OLD_WIDTH, OLD_HEIGHT, NUMBERS } i;
495 int numbers[NUMBERS];
497 assert(text && *text);
498 if_assert_failed return;
500 for (i = 0; i < NUMBERS; i++) {
501 unsigned char *p = strchr(text, ',');
503 if (p) {
504 *p++ = '\0';
506 } else if (i < OLD_HEIGHT) {
507 return;
510 numbers[i] = atoi(text);
512 if (p) text = p;
515 resize_window(numbers[NEW_WIDTH], numbers[NEW_HEIGHT],
516 numbers[OLD_WIDTH], numbers[OLD_HEIGHT]);
517 resize_terminal();
520 void
521 dispatch_special(unsigned char *text)
523 switch (text[0]) {
524 case TERM_FN_TITLE:
525 if (ditrm) {
526 if (ditrm->remote)
527 break;
529 if (!ditrm->orig_title)
530 ditrm->orig_title = get_window_title(
531 ditrm->title_codepage);
532 ditrm->touched_title = 1;
534 /* TODO: Is it really possible to get here with
535 * ditrm == NULL, and which charset would then
536 * be most appropriate? */
537 set_window_title(text + 1,
538 ditrm ? ditrm->title_codepage
539 : get_cp_index("US-ASCII"));
540 break;
541 case TERM_FN_RESIZE:
542 if (ditrm && ditrm->remote)
543 break;
545 resize_terminal_from_str(text + 1);
546 break;
547 case TERM_FN_TITLE_CODEPAGE:
548 if (ditrm) {
549 int cp = get_cp_index(text + 1);
551 /* If the master sends the name of an
552 * unrecognized charset, assume only
553 * that it's ASCII compatible. */
554 if (cp == -1)
555 cp = get_cp_index("US-ASCII");
556 ditrm->title_codepage = cp;
558 break;
560 #ifdef CONFIG_IMAGES
561 case TERM_FN_IMG_DRAW:
562 if (ditrm && ditrm->image_h) {
563 int imgid;
564 struct box b;
565 unsigned char file[4096];
567 sscanf(text + 1, "%d %d %d %s %d %d", &imgid, &b.x, &b.y, file, &b.width, &b.height);
568 slave_render_image(ditrm->image_h, imgid, &b, file);
570 break;
571 case TERM_FN_IMG_HIDE_REG:
572 if (ditrm && ditrm->image_h) {
573 struct box b;
575 sscanf(text + 1, "%d %d %d %d", &b.x, &b.y, &b.width, &b.height);
576 slave_hide_image_region(ditrm->image_h, &b);
578 break;
579 case TERM_FN_IMG_SYNC:
580 if (ditrm && ditrm->image_h) {
581 slave_sync_images(ditrm->image_h);
583 break;
584 #endif
588 static void inline
589 safe_hard_write(int fd, unsigned char *buf, int len)
591 if (is_blocked()) return;
593 want_draw();
594 hard_write(fd, buf, len);
595 done_draw();
598 /** A select_handler_T read_func for itrm_in.sock. A slave process
599 * calls this when the master sends it data to be displayed. The
600 * master process never calls this. */
601 static void
602 in_sock(struct itrm *itrm)
604 struct string path;
605 struct string delete;
606 char ch;
607 int fg; /* enum term_exec */
608 ssize_t bytes_read, i, p;
609 unsigned char buf[ITRM_OUT_QUEUE_SIZE];
611 bytes_read = safe_read(itrm->in.sock, buf, ITRM_OUT_QUEUE_SIZE);
612 if (bytes_read <= 0) goto free_and_return;
614 qwerty:
615 for (i = 0; i < bytes_read; i++)
616 if (!buf[i])
617 goto has_nul_byte;
619 safe_hard_write(itrm->out.std, buf, bytes_read);
620 return;
622 has_nul_byte:
623 if (i) safe_hard_write(itrm->out.std, buf, i);
625 i++;
626 assert(ITRM_OUT_QUEUE_SIZE - i > 0);
627 memmove(buf, buf + i, ITRM_OUT_QUEUE_SIZE - i);
628 bytes_read -= i;
629 p = 0;
631 #define RD(xx) { \
632 unsigned char cc; \
634 if (p < bytes_read) \
635 cc = buf[p++]; \
636 else if ((hard_read(itrm->in.sock, &cc, 1)) <= 0) \
637 goto free_and_return; \
638 xx = cc; \
641 RD(fg);
643 if (!init_string(&path)) goto free_and_return;
645 while (1) {
646 RD(ch);
647 if (!ch) break;
648 add_char_to_string(&path, ch);
651 if (!init_string(&delete)) {
652 done_string(&path);
653 goto free_and_return;
656 while (1) {
657 RD(ch);
658 if (!ch) break;
659 add_char_to_string(&delete, ch);
662 #undef RD
664 if (!*path.source) {
665 dispatch_special(delete.source);
667 } else {
668 int blockh;
669 unsigned char *param;
670 int path_len, del_len, param_len;
672 /* TODO: Should this be changed to allow TERM_EXEC_NEWWIN
673 * in a blocked terminal? There is similar code in
674 * exec_on_terminal(). --KON, 2007 */
675 if (is_blocked() && fg != TERM_EXEC_BG) {
676 if (*delete.source) unlink(delete.source);
677 goto nasty_thing;
680 path_len = path.length;
681 del_len = delete.length;
682 param_len = path_len + del_len + 3;
684 param = mem_alloc(param_len);
685 if (!param) goto nasty_thing;
687 param[0] = fg;
688 memcpy(param + 1, path.source, path_len + 1);
689 memcpy(param + 1 + path_len + 1, delete.source, del_len + 1);
691 if (fg == TERM_EXEC_FG) block_itrm();
693 blockh = start_thread((void (*)(void *, int)) exec_thread,
694 param, param_len);
695 mem_free(param);
697 if (blockh == -1) {
698 if (fg == TERM_EXEC_FG)
699 unblock_itrm();
701 goto nasty_thing;
704 if (fg == TERM_EXEC_FG) {
705 set_handlers(blockh, (select_handler_T) unblock_itrm_x,
706 NULL, (select_handler_T) unblock_itrm_x,
707 (void *) (long) blockh);
709 } else {
710 set_handlers(blockh, close_handle, NULL, close_handle,
711 (void *) (long) blockh);
715 nasty_thing:
716 done_string(&path);
717 done_string(&delete);
718 assert(ITRM_OUT_QUEUE_SIZE - p > 0);
719 memmove(buf, buf + p, ITRM_OUT_QUEUE_SIZE - p);
720 bytes_read -= p;
722 goto qwerty;
724 free_and_return:
725 free_itrm(itrm);
729 /** Parse an ECMA-48 control sequence that was received from a
730 * terminal. Extract the Final Byte (if there are no Intermediate
731 * Bytes) and the value of the first parameter (if it is an integer).
733 * This function assumes the control sequence begins with a CSI -
734 * CONTROL SEQUENCE INTRODUCER encoded as ESC [. (ECMA-48 also allows
735 * 0x9B as a single-byte CSI, but we don't support that here.)
737 * @returns one of:
738 * - -1 if the control sequence is not yet complete; the caller sets a timer.
739 * - 0 if the control sequence does not comply with ECMA-48.
740 * - The length of the control sequence otherwise. */
741 static inline int
742 get_esc_code(unsigned char *str, int len, unsigned char *final_byte,
743 int *first_param_value)
745 const int parameter_pos = 2;
746 int intermediate_pos;
747 int final_pos;
748 int pos;
750 *final_byte = '\0';
751 *first_param_value = 0;
753 /* Parameter Bytes */
754 pos = parameter_pos;
755 while (pos < len && str[pos] >= 0x30 && str[pos] <= 0x3F)
756 ++pos;
758 /* Intermediate Bytes */
759 intermediate_pos = pos;
760 while (pos < len && str[pos] >= 0x20 && str[pos] <= 0x2F)
761 ++pos;
763 /* Final Byte */
764 final_pos = pos;
765 if (pos >= len)
766 return -1;
767 if (!(str[pos] >= 0x40 && str[pos] <= 0x7E))
768 return 0;
770 /* The control sequence seems OK. If the first Parameter
771 * Byte indicates that the parameter string is formatted
772 * as specified in clause 5.4.2 of ECMA-48, and the first
773 * parameter is an integer, then compute its value.
774 * (We need not check @len here because the loop cannot get
775 * past the Final Byte.) */
776 for (pos = parameter_pos; str[pos] >= 0x30 && str[pos] <= 0x39; ++pos)
777 *first_param_value = *first_param_value * 10 + str[pos] - 0x30;
778 /* If the first parameter contains an embedded separator, then
779 * the value is not an integer, so discard what we computed. */
780 if (str[pos] == 0x3A)
781 *first_param_value = 0;
783 /* The meaning of the Final Byte depends on the Intermediate
784 * Bytes. Because we don't currently need to recognize any
785 * control sequences that use Intermediate Bytes, we just
786 * discard the Final Byte if there are any Intermediate
787 * Bytes. */
788 if (intermediate_pos == final_pos)
789 *final_byte = str[final_pos];
791 return final_pos + 1;
794 /* Define it to dump queue content in a readable form,
795 * it may help to determine terminal sequences, and see what goes on. --Zas */
796 /* #define DEBUG_ITRM_QUEUE */
798 #ifdef DEBUG_ITRM_QUEUE
799 #include <stdio.h>
800 #include <ctype.h> /* isprint() isspace() */
801 #endif
803 /** Decode a control sequence that begins with CSI (CONTROL SEQUENCE
804 * INTRODUCER) encoded as ESC [, and set @a *ev accordingly.
805 * (ECMA-48 also allows 0x9B as a single-byte CSI, but we don't
806 * support that here.)
808 * @returns one of:
809 * - -1 if the control sequence is not yet complete; the caller sets a timer.
810 * - 0 if the control sequence should be parsed by some other function.
811 * - The length of the control sequence otherwise.
812 * Returning >0 does not imply this function has altered @a *ev. */
813 static int
814 decode_terminal_escape_sequence(struct itrm *itrm, struct interlink_event *ev)
816 struct term_event_keyboard kbd = { KBD_UNDEF, KBD_MOD_NONE };
817 unsigned char c;
818 int v;
819 int el;
821 if (itrm->in.queue.len < 3) return -1;
823 if (itrm->in.queue.data[2] == '[') {
824 /* The terminfo entry for linux has "kf1=\E[[A", etc.
825 * These are not control sequences compliant with
826 * clause 5.4 of ECMA-48. (According to ECMA-48,
827 * "\E[[" is SRS - START REVERSED STRING.) */
828 if (itrm->in.queue.len >= 4
829 && itrm->in.queue.data[3] >= 'A'
830 && itrm->in.queue.data[3] <= 'L') {
831 kbd.key = number_to_kbd_fkey(itrm->in.queue.data[3] - 'A' + 1);
832 set_kbd_interlink_event(ev, kbd.key, kbd.modifier);
833 return 4;
836 return -1;
839 el = get_esc_code(itrm->in.queue.data, itrm->in.queue.len, &c, &v);
840 if (el == -1) {
841 /* If the control sequence is incomplete but itrm->in.queue
842 * is already full, then we must not wait for more input:
843 * kbd_timeout might call in_kbd and thus process_input
844 * and come right back here. Better just reject the whole
845 * thing and let the initial CSI be handled as Alt-[. */
846 if (itrm->in.queue.len == ITRM_IN_QUEUE_SIZE)
847 return 0;
848 else
849 return -1;
851 #ifdef DEBUG_ITRM_QUEUE
852 fprintf(stderr, "esc code: %c v=%d c=%c el=%d\n", itrm->in.queue.data[1], v, c, el);
853 fflush(stderr);
854 #endif
856 /* The following information should be listed for each escape
857 * sequence recognized here:
859 * 1. Which control function ECMA-48 assigns to the sequence.
860 * Put parentheses around this if the control function
861 * seems unrelated to how ELinks actually treats the
862 * sequence. Write "private" if it is a control sequence
863 * reserved for private or experimental use in ECMA-48.
864 * (Those have a Final Byte in the range 0x70 to 0x7F,
865 * optionally preceded by a single Intermediate Byte 0x20.)
867 * 2. The capname used by Terminfo, if any. These should help
868 * when ELinks is eventually changed to read escape
869 * sequences from Terminfo (bug 96).
871 * 3. The $TERM identifier of some terminal that generates
872 * this escape sequence with the meaning expected by
873 * ELinks. Escape sequences with no known terminal may end
874 * up being removed from ELinks when bug 96 is fixed.
875 */ /* ECMA-48 Terminfo $TERM */
876 switch (c) { /* ------- -------- ----- */
877 case 'A': kbd.key = KBD_UP; break; /* CUU kcuu1 vt200 */
878 case 'B': kbd.key = KBD_DOWN; break; /* CUD kcud1 vt200 */
879 case 'C': kbd.key = KBD_RIGHT; break; /* CUF kcuf1 vt200 */
880 case 'D': kbd.key = KBD_LEFT; break; /* CUB kcub1 vt200 */
881 case 'F': /* (CPL) kend cons25 */
882 case 'e': kbd.key = KBD_END; break; /* (VPR) kend */
883 case 'H': kbd.key = KBD_HOME; break; /* CUP khome cons25 */
884 case 'I': kbd.key = KBD_PAGE_UP; break; /* (CHT) kpp cons25 */
885 case 'G': kbd.key = KBD_PAGE_DOWN; break; /* (CHA) knp cons25 */
886 case 'L': kbd.key = KBD_INS; break; /* (IL) kich1 cons25 */
887 /* Free BSD (TERM=cons25 etc.) */
888 /* case 'M': kbd.key = KBD_F1; break;*/ /* (DL) kf1 cons25 */
889 case 'N': kbd.key = KBD_F2; break; /* (EF) kf2 cons25 */
890 case 'O': kbd.key = KBD_F3; break; /* (EA) kf3 cons25 */
891 case 'P': kbd.key = KBD_F4; break; /* (DCH) kf4 cons25 */
892 case 'Q': kbd.key = KBD_F5; break; /* (SEE) kf5 cons25 */
893 /* case 'R': kbd.key = KBD_F6; break;*/ /* (CPR) kf6 cons25 */
894 case 'S': kbd.key = KBD_F7; break; /* (SU) kf7 cons25 */
895 case 'T': kbd.key = KBD_F8; break; /* (SD) kf8 cons25 */
896 case 'U': kbd.key = KBD_F9; break; /* (NP) kf9 cons25 */
897 case 'V': kbd.key = KBD_F10; break; /* (PP) kf10 cons25 */
898 case 'W': kbd.key = KBD_F11; break; /* (CTC) kf11 cons25 */
899 case 'X': kbd.key = KBD_F12; break; /* (ECH) kf12 cons25 */
901 case 'Z': /* CBT kcbt cons25 */
902 kbd.key = KBD_TAB; kbd.modifier = KBD_MOD_SHIFT; break;
904 case 'z': switch (v) { /* private */
905 case 247: kbd.key = KBD_INS; break; /* kich1 */
906 case 214: kbd.key = KBD_HOME; break; /* khome sun */
907 case 220: kbd.key = KBD_END; break; /* kend sun */
908 case 216: kbd.key = KBD_PAGE_UP; break; /* kpp sun */
909 case 222: kbd.key = KBD_PAGE_DOWN; break; /* knp sun */
910 case 249: kbd.key = KBD_DEL; break; /* kdch1 */
911 } break;
913 case '~': switch (v) { /* private */
914 case 1: kbd.key = KBD_HOME; break; /* khome linux */
915 case 2: kbd.key = KBD_INS; break; /* kich1 linux */
916 case 3: kbd.key = KBD_DEL; break; /* kdch1 linux */
917 case 4: kbd.key = KBD_END; break; /* kend linux */
918 case 5: kbd.key = KBD_PAGE_UP; break; /* kpp linux */
919 case 6: kbd.key = KBD_PAGE_DOWN; break; /* knp linux */
920 case 7: kbd.key = KBD_HOME; break; /* khome rxvt */
921 case 8: kbd.key = KBD_END; break; /* kend rxvt */
923 case 11: kbd.key = KBD_F1; break; /* kf1 rxvt */
924 case 12: kbd.key = KBD_F2; break; /* kf2 rxvt */
925 case 13: kbd.key = KBD_F3; break; /* kf3 rxvt */
926 case 14: kbd.key = KBD_F4; break; /* kf4 rxvt */
927 case 15: kbd.key = KBD_F5; break; /* kf5 rxvt */
929 case 17: kbd.key = KBD_F6; break; /* kf6 vt200 */
930 case 18: kbd.key = KBD_F7; break; /* kf7 vt200 */
931 case 19: kbd.key = KBD_F8; break; /* kf8 vt200 */
932 case 20: kbd.key = KBD_F9; break; /* kf9 vt200 */
933 case 21: kbd.key = KBD_F10; break; /* kf10 vt200 */
935 case 23: kbd.key = KBD_F11; break; /* kf11 vt200 */
936 case 24: kbd.key = KBD_F12; break; /* kf12 vt200 */
938 /* Give preference to F11 and F12 over shifted F1 and F2. */
940 case 23: kbd.key = KBD_F1; kbd.modifier = KBD_MOD_SHIFT; break;
941 case 24: kbd.key = KBD_F2; kbd.modifier = KBD_MOD_SHIFT; break;
944 case 25: kbd.key = KBD_F3; kbd.modifier = KBD_MOD_SHIFT; break;
945 case 26: kbd.key = KBD_F4; kbd.modifier = KBD_MOD_SHIFT; break;
947 case 28: kbd.key = KBD_F5; kbd.modifier = KBD_MOD_SHIFT; break;
948 case 29: kbd.key = KBD_F6; kbd.modifier = KBD_MOD_SHIFT; break;
950 case 31: kbd.key = KBD_F7; kbd.modifier = KBD_MOD_SHIFT; break;
951 case 32: kbd.key = KBD_F8; kbd.modifier = KBD_MOD_SHIFT; break;
952 case 33: kbd.key = KBD_F9; kbd.modifier = KBD_MOD_SHIFT; break;
953 case 34: kbd.key = KBD_F10; kbd.modifier = KBD_MOD_SHIFT; break;
955 case 200: itrm->bracketed_pasting = 1; break; /* xterm */
956 case 201: itrm->bracketed_pasting = 0; break; /* xterm */
958 } break;
960 case 'R': resize_terminal(); break; /* CPR u6 */
961 case 'M': /* (DL) kmous xterm */
962 #ifdef CONFIG_MOUSE
963 el = decode_terminal_mouse_escape_sequence(itrm, ev, el, v);
964 #endif /* CONFIG_MOUSE */
965 break;
968 /* KBD_UNDEF here means it was unrecognized or a mouse event. */
969 if (kbd.key != KBD_UNDEF)
970 set_kbd_interlink_event(ev, kbd.key, kbd.modifier);
972 return el;
975 /** Decode an escape sequence that begins with SS3 (SINGLE SHIFT 3).
976 * These are used for application cursor keys and the application keypad.
977 * @returns one of:
978 * - -1 if the escape sequence is not yet complete; the caller sets a timer.
979 * - 0 if the escape sequence should be parsed by some other function.
980 * - The length of the escape sequence otherwise.
981 * Returning >0 does not imply this function has altered @a *ev. */
982 static int
983 decode_terminal_application_key(struct itrm *itrm, struct interlink_event *ev)
985 unsigned char c;
986 struct interlink_event_keyboard kbd = { KBD_UNDEF, KBD_MOD_NONE };
988 assert(itrm->in.queue.len >= 2);
989 assert(itrm->in.queue.data[0] == ASCII_ESC);
990 assert(itrm->in.queue.data[1] == 0x4F); /* == 'O', incidentally */
991 if_assert_failed return 0;
993 if (itrm->in.queue.len < 3) return -1;
994 /* According to ECMA-35 section 8.4, a single (possibly multibyte)
995 * character follows the SS3. We now assume the code identifies
996 * GL as the single-shift area and the designated set has 94
997 * characters. */
998 c = itrm->in.queue.data[2];
999 if (c < 0x21 || c > 0x7E) return 0;
1001 switch (c) { /* Terminfo $TERM */
1002 case ' ': kbd.key = ' '; break; /* xterm */
1003 case 'A': kbd.key = KBD_UP; break; /* kcuu1 vt100 */
1004 case 'B': kbd.key = KBD_DOWN; break; /* kcud1 vt100 */
1005 case 'C': kbd.key = KBD_RIGHT; break; /* kcuf1 vt100 */
1006 case 'D': kbd.key = KBD_LEFT; break; /* kcub1 vt100 */
1007 case 'F': kbd.key = KBD_END; break; /* kend xterm */
1008 case 'H': kbd.key = KBD_HOME; break; /* khome xterm */
1009 case 'I': kbd.key = KBD_TAB; break; /* xterm */
1010 case 'M': kbd.key = KBD_ENTER; break; /* kent vt100 */
1011 /* FIXME: xterm generates ESC O 2 P for Shift-PF1 */
1012 case 'P': kbd.key = KBD_F1; break; /* kf1 vt100 */
1013 case 'Q': kbd.key = KBD_F2; break; /* kf2 vt100 */
1014 case 'R': kbd.key = KBD_F3; break; /* kf3 vt100 */
1015 case 'S': kbd.key = KBD_F4; break; /* kf4 vt100 */
1016 case 'X': kbd.key = '='; break; /* xterm */
1018 case 'j': case 'k': case 'l': case 'm': /* *+,- xterm */
1019 case 'n': case 'o': case 'p': case 'q': /* ./01 xterm */
1020 case 'r': case 's': case 't': case 'u': /* 2345 xterm */
1021 case 'v': case 'w': case 'x': case 'y': /* 6789 xterm */
1022 kbd.key = c - 'p' + '0'; break;
1024 if (kbd.key != KBD_UNDEF)
1025 copy_struct(&ev->info.keyboard, &kbd);
1027 return 3; /* even if we didn't recognize it */
1031 /** Initialize @a *ev to match the byte @a key received from the terminal.
1032 * @a key must not be a value from enum term_event_special_key. */
1033 static void
1034 set_kbd_event(const struct itrm *itrm, struct interlink_event *ev,
1035 int key, term_event_modifier_T modifier)
1037 if (key == itrm->verase)
1038 key = KBD_BS;
1039 else switch (key) {
1040 case ASCII_TAB:
1041 key = KBD_TAB;
1042 break;
1043 case ASCII_DEL: /* often overridden by itrm->verase above */
1044 key = KBD_DEL;
1045 break;
1046 case ASCII_LF:
1047 case ASCII_CR:
1048 key = KBD_ENTER;
1049 break;
1051 case ASCII_ESC:
1052 key = KBD_ESC;
1053 break;
1055 case ASCII_BS: /* often overridden by itrm->verase above */
1056 default:
1057 if (key < ' ') {
1058 key += 'A' - 1;
1059 modifier |= KBD_MOD_CTRL;
1063 set_kbd_interlink_event(ev, key, modifier);
1066 /** Timer callback for itrm.timer. As explained in install_timer(),
1067 * this function must erase the expired timer ID from all variables. */
1068 static void
1069 kbd_timeout(struct itrm *itrm)
1071 struct interlink_event ev;
1072 int el;
1074 itrm->timer = TIMER_ID_UNDEF;
1075 /* The expired timer ID has now been erased. */
1077 assertm(itrm->in.queue.len, "timeout on empty queue");
1078 assert(!itrm->blocked); /* block_itrm should have killed itrm->timer */
1079 if_assert_failed return;
1081 if (can_read(itrm->in.std)) {
1082 in_kbd(itrm);
1083 return;
1086 if (itrm->in.queue.len >= 2 && itrm->in.queue.data[0] == ASCII_ESC) {
1087 /* This is used for ESC [ and ESC O. */
1088 set_kbd_event(itrm, &ev, itrm->in.queue.data[1], KBD_MOD_ALT);
1089 el = 2;
1090 } else {
1091 set_kbd_event(itrm, &ev, itrm->in.queue.data[0], KBD_MOD_NONE);
1092 el = 1;
1094 itrm->bracketed_pasting = 0;
1095 itrm_queue_event(itrm, (char *) &ev, sizeof(ev));
1097 itrm->in.queue.len -= el;
1098 if (itrm->in.queue.len)
1099 memmove(itrm->in.queue.data, itrm->in.queue.data + el, itrm->in.queue.len);
1101 while (process_queue(itrm));
1104 /** Parse one event from itrm_in.queue and append to itrm_out.queue.
1105 * @pre On entry, @a *itrm must not be blocked.
1106 * @returns the number of bytes removed from itrm->in.queue; at least 0.
1107 * @post If this function leaves the queue not full, it also reenables
1108 * reading from itrm->in.std. (Because it does not add to the queue,
1109 * it never need disable reading.) */
1110 static int
1111 process_queue(struct itrm *itrm)
1113 struct interlink_event ev;
1114 int el = 0;
1116 if (!itrm->in.queue.len) goto return_without_event;
1117 assert(!itrm->blocked);
1118 if_assert_failed return 0; /* unlike goto, don't enable reading */
1120 set_kbd_interlink_event(&ev, KBD_UNDEF, KBD_MOD_NONE);
1122 #ifdef DEBUG_ITRM_QUEUE
1124 int i;
1126 /* Dump current queue in a readable form to stderr. */
1127 for (i = 0; i < itrm->in.queue.len; i++)
1128 if (itrm->in.queue.data[i] == ASCII_ESC)
1129 fprintf(stderr, "ESC ");
1130 else if (isprint(itrm->in.queue.data[i]) && !isspace(itrm->in.queue.data[i]))
1131 fprintf(stderr, "%c ", itrm->in.queue.data[i]);
1132 else
1133 fprintf(stderr, "0x%02x ", itrm->in.queue.data[i]);
1135 fprintf(stderr, "\n");
1136 fflush(stderr);
1138 #endif /* DEBUG_ITRM_QUEUE */
1140 /* el == -1 means itrm->in.queue appears to be the beginning of an
1141 * escape sequence but it is not yet complete. Set a timer;
1142 * if it times out, then assume it wasn't an escape sequence
1143 * after all.
1144 * el == 0 means this function has not yet figured out what the data
1145 * in itrm->in.queue is, but some possibilities remain.
1146 * One of them will be chosen before returning.
1147 * el > 0 means some bytes were successfully parsed from the beginning
1148 * of itrm->in.queue and should now be removed from there.
1149 * However, this does not always imply an event will be queued.
1152 /* ELinks should also recognize U+009B CONTROL SEQUENCE INTRODUCER
1153 * as meaning the same as ESC 0x5B, and U+008F SINGLE SHIFT THREE as
1154 * meaning the same as ESC 0x4F, but those cannot yet be implemented
1155 * because of bug 777: the UTF-8 decoder is run too late. */
1156 if (itrm->in.queue.data[0] == ASCII_ESC) {
1157 if (itrm->in.queue.len < 2) {
1158 el = -1;
1159 } else if (itrm->in.queue.data[1] == 0x5B /* CSI */) {
1160 el = decode_terminal_escape_sequence(itrm, &ev);
1161 } else if (itrm->in.queue.data[1] == 0x4F /* SS3 */) {
1162 el = decode_terminal_application_key(itrm, &ev);
1163 } else if (itrm->in.queue.data[1] == ASCII_ESC) {
1164 /* ESC ESC can be either Alt-Esc or the
1165 * beginning of e.g. ESC ESC 0x5B 0x41,
1166 * which we should parse as Esc Up. */
1167 if (itrm->in.queue.len < 3) {
1168 /* Need more data to figure it out. */
1169 el = -1;
1170 } else if (itrm->in.queue.data[2] == 0x5B
1171 || itrm->in.queue.data[2] == 0x4F) {
1172 /* The first ESC appears to be followed
1173 * by an escape sequence. Treat it as
1174 * a standalone Esc. */
1175 el = 1;
1176 set_kbd_event(itrm, &ev,
1177 itrm->in.queue.data[0],
1178 KBD_MOD_NONE);
1179 } else {
1180 /* The second ESC of ESC ESC is not the
1181 * beginning of any known escape sequence.
1182 * This must be Alt-Esc, then. */
1183 el = 2;
1184 set_kbd_event(itrm, &ev,
1185 itrm->in.queue.data[1],
1186 KBD_MOD_ALT);
1189 if (el == 0) { /* Begins with ESC, but none of the above */
1190 el = 2;
1191 set_kbd_event(itrm, &ev, itrm->in.queue.data[1],
1192 KBD_MOD_ALT);
1195 } else if (itrm->in.queue.data[0] == 0) {
1196 static const struct term_event_keyboard os2xtd[256] = {
1197 #include "terminal/key.inc"
1200 if (itrm->in.queue.len < 2)
1201 el = -1;
1202 else {
1203 el = 2;
1204 set_kbd_interlink_event(&ev,
1205 os2xtd[itrm->in.queue.data[1]].key,
1206 os2xtd[itrm->in.queue.data[1]].modifier);
1210 if (el == 0) {
1211 el = 1;
1212 set_kbd_event(itrm, &ev, itrm->in.queue.data[0],
1213 itrm->bracketed_pasting ? KBD_MOD_PASTE : KBD_MOD_NONE);
1216 /* The call to decode_terminal_escape_sequence() might have changed the
1217 * keyboard event to a mouse event. */
1218 if (ev.ev == EVENT_MOUSE || ev.info.keyboard.key != KBD_UNDEF) {
1219 itrm_queue_event(itrm, (char *) &ev, sizeof(ev));
1220 itrm->bracketed_pasting =
1221 (ev.ev == EVENT_KBD
1222 && (ev.info.keyboard.modifier & KBD_MOD_PASTE));
1225 return_without_event:
1226 if (el == -1) {
1227 install_timer(&itrm->timer, ESC_TIMEOUT, (void (*)(void *)) kbd_timeout,
1228 itrm);
1229 return 0;
1230 } else {
1231 assertm(itrm->in.queue.len >= el, "event queue underflow");
1232 if_assert_failed { itrm->in.queue.len = el; }
1234 itrm->in.queue.len -= el;
1235 if (itrm->in.queue.len)
1236 memmove(itrm->in.queue.data, itrm->in.queue.data + el, itrm->in.queue.len);
1238 if (itrm->in.queue.len < ITRM_IN_QUEUE_SIZE)
1239 handle_itrm_stdin(itrm);
1241 return el;
1246 /** A select_handler_T read_func for itrm_in.std. This is called when
1247 * characters typed by the user arrive from the terminal. */
1248 static void
1249 in_kbd(struct itrm *itrm)
1251 int r;
1253 if (!can_read(itrm->in.std)) return;
1255 kill_timer(&itrm->timer);
1257 if (itrm->in.queue.len >= ITRM_IN_QUEUE_SIZE) {
1258 unhandle_itrm_stdin(itrm);
1259 while (process_queue(itrm));
1260 return;
1263 r = safe_read(itrm->in.std, itrm->in.queue.data + itrm->in.queue.len,
1264 ITRM_IN_QUEUE_SIZE - itrm->in.queue.len);
1265 if (r <= 0) {
1266 free_itrm(itrm);
1267 return;
1270 itrm->in.queue.len += r;
1271 if (itrm->in.queue.len > ITRM_IN_QUEUE_SIZE) {
1272 ERROR(gettext("Too many bytes read from the itrm!"));
1273 itrm->in.queue.len = ITRM_IN_QUEUE_SIZE;
1276 while (process_queue(itrm));
1279 /** Enable reading from itrm_in.std. ELinks will read any available
1280 * bytes from the tty into itrm->in.queue and then parse them.
1281 * Reading should be enabled whenever itrm->in.queue is not full and
1282 * itrm->blocked is 0. */
1283 static void
1284 handle_itrm_stdin(struct itrm *itrm)
1286 assert(itrm->in.std >= 0);
1287 if_assert_failed return;
1289 set_handlers(itrm->in.std, (select_handler_T) in_kbd, NULL,
1290 (select_handler_T) free_itrm, itrm);
1293 /** Disable reading from itrm_in.std. Reading should be disabled
1294 * whenever itrm->in.queue is full (there is no room for the data)
1295 * or itrm->blocked is 1 (other processes may read the data). */
1296 static void
1297 unhandle_itrm_stdin(struct itrm *itrm)
1299 assert(itrm->in.std >= 0);
1300 if_assert_failed return;
1302 set_handlers(itrm->in.std, (select_handler_T) NULL, NULL,
1303 (select_handler_T) free_itrm, itrm);