Bug 772: Recognize ESC [ Z as Shift-Tab, and bind it like Alt-Tab by default.
[elinks.git] / src / terminal / kbd.c
blob4e0dfd9d990dc9c16d8f5cb747749ba1eb571f1e
1 /* Support for keyboard interface */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <stdlib.h>
8 #include <string.h>
9 #ifdef HAVE_TERMIOS_H
10 #include <termios.h>
11 #endif
12 #ifdef HAVE_UNISTD_H
13 #include <unistd.h>
14 #endif
15 #ifdef __hpux__
16 #include <limits.h>
17 #define HPUX_PIPE (len > PIPE_BUF || errno != EAGAIN)
18 #else
19 #define HPUX_PIPE 1
20 #endif
22 #include "elinks.h"
24 #include "config/options.h"
25 #include "intl/gettext/libintl.h"
26 #include "main/select.h"
27 #include "main/timer.h"
28 #include "osdep/ascii.h"
29 #include "osdep/osdep.h"
30 #include "terminal/hardio.h"
31 #include "terminal/itrm.h"
32 #include "terminal/kbd.h"
33 #include "terminal/mouse.h"
34 #include "terminal/terminal.h"
35 #include "util/error.h"
36 #include "util/memory.h"
37 #include "util/string.h"
38 #include "util/time.h"
40 /* TODO: move stuff from here to itrm.{c,h} and mouse.{c,h} */
42 struct itrm *ditrm = NULL;
44 static void free_itrm(struct itrm *);
45 static void in_kbd(struct itrm *);
46 static void in_sock(struct itrm *);
47 static int process_queue(struct itrm *);
48 static void handle_itrm_stdin(struct itrm *);
49 static void unhandle_itrm_stdin(struct itrm *);
51 #ifdef CONFIG_DEBUG
52 /* This hack makes GCC put enum term_event_special_key in the debug
53 * information even though it is not otherwise used. The const
54 * prevents an unused-variable warning. */
55 static const enum term_event_special_key dummy_term_event_special_key;
56 #endif
59 int
60 is_blocked(void)
62 return ditrm && ditrm->blocked;
66 void
67 free_all_itrms(void)
69 if (ditrm) free_itrm(ditrm);
73 /* A select_handler_T write_func for itrm->out.sock. This is called
74 * when there is data in itrm->out.queue and it is possible to write
75 * it to itrm->out.sock. When itrm->out.queue becomes empty, this
76 * handler is temporarily removed. */
77 static void
78 itrm_queue_write(struct itrm *itrm)
80 int written;
81 int qlen = int_min(itrm->out.queue.len, 128);
83 assertm(qlen, "event queue empty");
84 if_assert_failed return;
86 written = safe_write(itrm->out.sock, itrm->out.queue.data, qlen);
87 if (written <= 0) {
88 if (written < 0) free_itrm(itrm); /* write error */
89 return;
92 itrm->out.queue.len -= written;
94 if (itrm->out.queue.len == 0) {
95 set_handlers(itrm->out.sock,
96 get_handler(itrm->out.sock, SELECT_HANDLER_READ),
97 NULL,
98 get_handler(itrm->out.sock, SELECT_HANDLER_ERROR),
99 get_handler(itrm->out.sock, SELECT_HANDLER_DATA));
100 } else {
101 assert(itrm->out.queue.len > 0);
102 memmove(itrm->out.queue.data, itrm->out.queue.data + written, itrm->out.queue.len);
107 void
108 itrm_queue_event(struct itrm *itrm, unsigned char *data, int len)
110 int w = 0;
112 if (!len) return;
114 if (!itrm->out.queue.len && can_write(itrm->out.sock)) {
115 w = safe_write(itrm->out.sock, data, len);
116 if (w <= 0 && HPUX_PIPE) {
117 register_bottom_half(free_itrm, itrm);
118 return;
122 if (w < len) {
123 int left = len - w;
124 unsigned char *c = mem_realloc(itrm->out.queue.data,
125 itrm->out.queue.len + left);
127 if (!c) {
128 free_itrm(itrm);
129 return;
132 itrm->out.queue.data = c;
133 memcpy(itrm->out.queue.data + itrm->out.queue.len, data + w, left);
134 itrm->out.queue.len += left;
135 set_handlers(itrm->out.sock,
136 get_handler(itrm->out.sock, SELECT_HANDLER_READ),
137 (select_handler_T) itrm_queue_write,
138 (select_handler_T) free_itrm, itrm);
143 void
144 kbd_ctrl_c(void)
146 struct interlink_event ev;
148 if (!ditrm) return;
149 set_kbd_interlink_event(&ev, KBD_CTRL_C, KBD_MOD_NONE);
150 itrm_queue_event(ditrm, (unsigned char *) &ev, sizeof(ev));
153 #define write_sequence(fd, seq) \
154 hard_write(fd, seq, sizeof(seq) - 1)
157 #define INIT_TERMINAL_SEQ "\033)0\0337" /* Special Character and Line Drawing Set, Save Cursor */
158 #define INIT_ALT_SCREEN_SEQ "\033[?47h" /* Use Alternate Screen Buffer */
160 static void
161 send_init_sequence(int h, int altscreen)
163 write_sequence(h, INIT_TERMINAL_SEQ);
165 /* If alternate screen is supported switch to it. */
166 if (altscreen) {
167 write_sequence(h, INIT_ALT_SCREEN_SEQ);
169 #ifdef CONFIG_MOUSE
170 send_mouse_init_sequence(h);
171 #endif
174 #define DONE_CLS_SEQ "\033[2J" /* Erase in Display, Clear All */
175 #define DONE_TERMINAL_SEQ "\0338\r \b" /* Restore Cursor (DECRC) + ??? */
176 #define DONE_ALT_SCREEN_SEQ "\033[?47l" /* Use Normal Screen Buffer */
178 static void
179 send_done_sequence(int h, int altscreen)
181 write_sequence(h, DONE_CLS_SEQ);
183 #ifdef CONFIG_MOUSE
184 send_mouse_done_sequence(h);
185 #endif
187 /* Switch from alternate screen. */
188 if (altscreen) {
189 write_sequence(h, DONE_ALT_SCREEN_SEQ);
192 write_sequence(h, DONE_TERMINAL_SEQ);
196 #undef write_sequence
198 void
199 resize_terminal(void)
201 struct interlink_event ev;
202 int width, height;
204 get_terminal_size(ditrm->out.std, &width, &height);
205 set_resize_interlink_event(&ev, width, height);
206 itrm_queue_event(ditrm, (char *) &ev, sizeof(ev));
209 void
210 get_terminal_name(unsigned char name[MAX_TERM_LEN])
212 unsigned char *term = getenv("TERM");
213 int i;
215 memset(name, 0, MAX_TERM_LEN);
217 if (!term) return;
219 for (i = 0; term[i] != 0 && i < MAX_TERM_LEN - 1; i++)
220 name[i] = isident(term[i]) ? term[i] : '-';
224 static int
225 setraw(int fd, struct termios *p)
227 struct termios t;
229 memset(&t, 0, sizeof(t));
230 if (tcgetattr(fd, &t)) return -1;
232 if (p) copy_struct(p, &t);
234 elinks_cfmakeraw(&t);
235 t.c_lflag |= ISIG;
236 #ifdef TOSTOP
237 t.c_lflag |= TOSTOP;
238 #endif
239 t.c_oflag |= OPOST;
240 if (tcsetattr(fd, TCSANOW, &t)) return -1;
242 return 0;
245 /* Construct the struct itrm of this process, make ditrm point to it,
246 * set up select() handlers, and send the initial interlink packet.
248 * The first five parameters are file descriptors that this function
249 * saves in submembers of struct itrm, and for which this function may
250 * set select() handlers. Please see the definitions of struct
251 * itrm_in and struct itrm_out for further explanations.
253 * param member file if process is master file if process is slave
254 * ------ ------ ------------------------- ------------------------
255 * std_in in.std read tty device (or pipe) read tty device (or pipe)
256 * std_out out.std write tty device (or pipe) write tty device (or pipe)
257 * sock_in in.sock ==std_out (masterhood flag) read socket from master
258 * sock_out out.sock write pipe to same process write socket to master
259 * ctl_in in.ctl control tty device control tty device
261 * The remaining three parameters control the initial interlink packet.
263 * init_string = A string to be passed to the master process. Need
264 * not be null-terminated. If remote==0, this is a URI.
265 * Otherwise, this is a remote command.
266 * init_len = The length of init_string, in bytes.
267 * remote = 0 if asking the master to start a new session
268 * and display it via this process. Otherwise,
269 * enum remote_session_flags. */
270 void
271 handle_trm(int std_in, int std_out, int sock_in, int sock_out, int ctl_in,
272 void *init_string, int init_len, int remote)
274 struct itrm *itrm;
275 struct terminal_info info;
276 struct interlink_event_size *size = &info.event.info.size;
277 unsigned char *ts;
279 memset(&info, 0, sizeof(info));
281 get_terminal_size(ctl_in, &size->width, &size->height);
282 info.event.ev = EVENT_INIT;
283 info.system_env = get_system_env();
284 info.length = init_len;
286 if (remote) {
287 info.session_info = remote;
288 info.magic = INTERLINK_REMOTE_MAGIC;
289 } else {
290 info.session_info = get_cmd_opt_int("base-session");
291 info.magic = INTERLINK_NORMAL_MAGIC;
294 itrm = mem_calloc(1, sizeof(*itrm));
295 if (!itrm) return;
297 itrm->in.queue.data = mem_calloc(1, ITRM_IN_QUEUE_SIZE);
298 if (!itrm->in.queue.data) {
299 mem_free(itrm);
300 return;
303 ditrm = itrm;
304 itrm->in.std = std_in;
305 itrm->out.std = std_out;
306 itrm->in.sock = sock_in;
307 itrm->out.sock = sock_out;
308 itrm->in.ctl = ctl_in;
309 itrm->timer = TIMER_ID_UNDEF;
310 itrm->remote = !!remote;
312 /* FIXME: Combination altscreen + xwin does not work as it should,
313 * mouse clicks are reportedly partially ignored. */
314 if (info.system_env & (ENV_SCREEN | ENV_XWIN))
315 itrm->altscreen = 1;
317 if (!remote) {
318 if (ctl_in >= 0) setraw(ctl_in, &itrm->t);
319 send_init_sequence(std_out, itrm->altscreen);
320 handle_terminal_resize(ctl_in, resize_terminal);
321 #ifdef CONFIG_MOUSE
322 enable_mouse();
323 #endif
326 handle_itrm_stdin(itrm);
328 if (sock_in != std_out)
329 set_handlers(sock_in, (select_handler_T) in_sock,
330 NULL, (select_handler_T) free_itrm, itrm);
332 get_terminal_name(info.name);
334 ts = get_cwd();
335 if (ts) {
336 memcpy(info.cwd, ts, int_min(strlen(ts), MAX_CWD_LEN));
337 mem_free(ts);
340 itrm_queue_event(itrm, (char *) &info, TERMINAL_INFO_SIZE);
341 itrm_queue_event(itrm, (char *) init_string, init_len);
345 /* A select_handler_T read_func and error_func for the pipe (long) h.
346 * This is called when the subprocess started on the terminal of this
347 * ELinks process exits. ELinks then resumes using the terminal. */
348 static void
349 unblock_itrm_x(void *h)
351 close_handle(h);
352 if (!ditrm) return;
353 unblock_itrm(0);
354 resize_terminal();
359 unblock_itrm(int fd)
361 if (!ditrm) return -1;
363 if (ditrm->in.ctl >= 0 && setraw(ditrm->in.ctl, NULL)) return -1;
364 ditrm->blocked = 0;
365 send_init_sequence(ditrm->out.std, ditrm->altscreen);
367 handle_itrm_stdin(ditrm);
368 resume_mouse(ditrm->mouse_h);
370 handle_terminal_resize(ditrm->in.ctl, resize_terminal);
371 unblock_stdin();
373 return 0;
377 void
378 block_itrm(int fd)
380 if (!ditrm) return;
382 ditrm->blocked = 1;
383 block_stdin();
384 kill_timer(&ditrm->timer);
385 ditrm->in.queue.len = 0;
386 unhandle_terminal_resize(ditrm->in.ctl);
387 send_done_sequence(ditrm->out.std, ditrm->altscreen);
388 tcsetattr(ditrm->in.ctl, TCSANOW, &ditrm->t);
389 unhandle_itrm_stdin(ditrm);
390 suspend_mouse(ditrm->mouse_h);
394 static void
395 free_itrm(struct itrm *itrm)
397 if (!itrm) return;
399 if (!itrm->remote) {
400 if (itrm->orig_title && *itrm->orig_title) {
401 set_window_title(itrm->orig_title);
403 } else if (itrm->touched_title) {
404 /* Set the window title to the value of $TERM if X11
405 * wasn't compiled in. Should hopefully make at least
406 * half the users happy. (debian bug #312955) */
407 unsigned char title[MAX_TERM_LEN];
409 get_terminal_name(title);
410 if (*title)
411 set_window_title(title);
415 unhandle_terminal_resize(itrm->in.ctl);
416 #ifdef CONFIG_MOUSE
417 disable_mouse();
418 #endif
419 send_done_sequence(itrm->out.std, itrm->altscreen);
420 tcsetattr(itrm->in.ctl, TCSANOW, &itrm->t);
423 mem_free_set(&itrm->orig_title, NULL);
425 clear_handlers(itrm->in.std);
426 clear_handlers(itrm->in.sock);
427 clear_handlers(itrm->out.std);
428 clear_handlers(itrm->out.sock);
430 kill_timer(&itrm->timer);
432 if (itrm == ditrm) ditrm = NULL;
433 mem_free_if(itrm->out.queue.data);
434 mem_free_if(itrm->in.queue.data);
435 mem_free(itrm);
438 /* Resize terminal to dimensions specified by @text string.
439 * @text should look like "width,height,old-width,old-height" where width and
440 * height are integers. */
441 static inline void
442 resize_terminal_from_str(unsigned char *text)
444 enum { NEW_WIDTH = 0, NEW_HEIGHT, OLD_WIDTH, OLD_HEIGHT, NUMBERS } i;
445 int numbers[NUMBERS];
447 assert(text && *text);
448 if_assert_failed return;
450 for (i = 0; i < NUMBERS; i++) {
451 unsigned char *p = strchr(text, ',');
453 if (p) {
454 *p++ = '\0';
456 } else if (i < OLD_HEIGHT) {
457 return;
460 numbers[i] = atoi(text);
462 if (p) text = p;
465 resize_window(numbers[NEW_WIDTH], numbers[NEW_HEIGHT],
466 numbers[OLD_WIDTH], numbers[OLD_HEIGHT]);
467 resize_terminal();
470 void
471 dispatch_special(unsigned char *text)
473 switch (text[0]) {
474 case TERM_FN_TITLE:
475 if (ditrm) {
476 if (ditrm->remote)
477 break;
479 if (!ditrm->orig_title)
480 ditrm->orig_title = get_window_title();
481 ditrm->touched_title = 1;
483 set_window_title(text + 1);
484 break;
485 case TERM_FN_RESIZE:
486 if (ditrm && ditrm->remote)
487 break;
489 resize_terminal_from_str(text + 1);
490 break;
494 static void inline
495 safe_hard_write(int fd, unsigned char *buf, int len)
497 if (is_blocked()) return;
499 want_draw();
500 hard_write(fd, buf, len);
501 done_draw();
504 /* A select_handler_T read_func for itrm->in.sock. A slave process
505 * calls this when the master sends it data to be displayed. The
506 * master process never calls this. */
507 static void
508 in_sock(struct itrm *itrm)
510 struct string path;
511 struct string delete;
512 char ch;
513 int fg;
514 ssize_t bytes_read, i, p;
515 unsigned char buf[ITRM_OUT_QUEUE_SIZE];
517 bytes_read = safe_read(itrm->in.sock, buf, ITRM_OUT_QUEUE_SIZE);
518 if (bytes_read <= 0) goto free_and_return;
520 qwerty:
521 for (i = 0; i < bytes_read; i++)
522 if (!buf[i])
523 goto has_nul_byte;
525 safe_hard_write(itrm->out.std, buf, bytes_read);
526 return;
528 has_nul_byte:
529 if (i) safe_hard_write(itrm->out.std, buf, i);
531 i++;
532 assert(ITRM_OUT_QUEUE_SIZE - i > 0);
533 memmove(buf, buf + i, ITRM_OUT_QUEUE_SIZE - i);
534 bytes_read -= i;
535 p = 0;
537 #define RD(xx) { \
538 unsigned char cc; \
540 if (p < bytes_read) \
541 cc = buf[p++]; \
542 else if ((hard_read(itrm->in.sock, &cc, 1)) <= 0) \
543 goto free_and_return; \
544 xx = cc; \
547 RD(fg);
549 if (!init_string(&path)) goto free_and_return;
551 while (1) {
552 RD(ch);
553 if (!ch) break;
554 add_char_to_string(&path, ch);
557 if (!init_string(&delete)) {
558 done_string(&path);
559 goto free_and_return;
562 while (1) {
563 RD(ch);
564 if (!ch) break;
565 add_char_to_string(&delete, ch);
568 #undef RD
570 if (!*path.source) {
571 dispatch_special(delete.source);
573 } else {
574 int blockh;
575 unsigned char *param;
576 int path_len, del_len, param_len;
578 if (is_blocked() && fg) {
579 if (*delete.source) unlink(delete.source);
580 goto nasty_thing;
583 path_len = path.length;
584 del_len = delete.length;
585 param_len = path_len + del_len + 3;
587 param = mem_alloc(param_len);
588 if (!param) goto nasty_thing;
590 param[0] = fg;
591 memcpy(param + 1, path.source, path_len + 1);
592 memcpy(param + 1 + path_len + 1, delete.source, del_len + 1);
594 if (fg == 1) block_itrm(itrm->in.ctl);
596 blockh = start_thread((void (*)(void *, int)) exec_thread,
597 param, param_len);
598 mem_free(param);
600 if (blockh == -1) {
601 if (fg == 1)
602 unblock_itrm(itrm->in.ctl);
604 goto nasty_thing;
607 if (fg == 1) {
608 set_handlers(blockh, (select_handler_T) unblock_itrm_x,
609 NULL, (select_handler_T) unblock_itrm_x,
610 (void *) (long) blockh);
612 } else {
613 set_handlers(blockh, close_handle, NULL, close_handle,
614 (void *) (long) blockh);
618 nasty_thing:
619 done_string(&path);
620 done_string(&delete);
621 assert(ITRM_OUT_QUEUE_SIZE - p > 0);
622 memmove(buf, buf + p, ITRM_OUT_QUEUE_SIZE - p);
623 bytes_read -= p;
625 goto qwerty;
627 free_and_return:
628 free_itrm(itrm);
632 /* Returns the length of the escape sequence */
633 static inline int
634 get_esc_code(unsigned char *str, int len, unsigned char *code, int *num)
636 int pos;
638 *num = 0;
639 *code = '\0';
641 for (pos = 2; pos < len; pos++) {
642 if (!isdigit(str[pos]) || pos > 7) {
643 *code = str[pos];
645 return pos + 1;
647 *num = *num * 10 + str[pos] - '0';
650 return 0;
653 /* Define it to dump queue content in a readable form,
654 * it may help to determine terminal sequences, and see what goes on. --Zas */
655 /* #define DEBUG_ITRM_QUEUE */
657 #ifdef DEBUG_ITRM_QUEUE
658 #include <stdio.h>
659 #include <ctype.h> /* isprint() isspace() */
660 #endif
662 /* Returns length of the escape sequence or -1 if the caller needs to set up
663 * the ESC delay timer. */
664 static int
665 decode_terminal_escape_sequence(struct itrm *itrm, struct interlink_event *ev)
667 struct term_event_keyboard kbd = { KBD_UNDEF, KBD_MOD_NONE };
668 unsigned char c;
669 int v;
670 int el;
672 if (itrm->in.queue.len < 3) return -1;
674 if (itrm->in.queue.data[2] == '[') {
675 /* The terminfo entry for linux has "kf1=\E[[A", etc.
676 * These are not control sequences compliant with
677 * clause 5.4 of ECMA-48. */
678 if (itrm->in.queue.len >= 4
679 && itrm->in.queue.data[3] >= 'A'
680 && itrm->in.queue.data[3] <= 'L') {
681 kbd.key = number_to_kbd_fkey(itrm->in.queue.data[3] - 'A' + 1);
682 set_kbd_interlink_event(ev, kbd.key, kbd.modifier);
683 return 4;
686 return -1;
689 el = get_esc_code(itrm->in.queue.data, itrm->in.queue.len, &c, &v);
690 #ifdef DEBUG_ITRM_QUEUE
691 fprintf(stderr, "esc code: %c v=%d c=%c el=%d\n", itrm->in.queue.data[1], v, c, el);
692 fflush(stderr);
693 #endif
695 /* The following information should be listed for each escape
696 * sequence recognized here:
698 * 1. Which control function ECMA-48 assigns to the sequence.
699 * Put parentheses around this if the control function
700 * seems unrelated to how ELinks actually treats the
701 * sequence. Write "private" if it is a control sequence
702 * reserved for private or experimental use in ECMA-48.
703 * (Those have a Final Byte in the range 0x70 to 0x7F,
704 * optionally preceded by a single Intermediate Byte 0x20.)
706 * 2. The capname used by Terminfo, if any. These should help
707 * when ELinks is eventually changed to read escape
708 * sequences from Terminfo (bug 96).
710 * 3. The $TERM identifier of some terminal that generates
711 * this escape sequence with the meaning expected by
712 * ELinks. Escape sequences with no known terminal may end
713 * up being removed from ELinks when bug 96 is fixed.
715 switch (c) { /* ECMA-48 Terminfo $TERM */
716 case 0: return -1; /* ------- -------- ----- */
717 case 'A': kbd.key = KBD_UP; break; /* CUU kcuu1 vt200 */
718 case 'B': kbd.key = KBD_DOWN; break; /* CUD kcud1 vt200 */
719 case 'C': kbd.key = KBD_RIGHT; break; /* CUF kcuf1 vt200 */
720 case 'D': kbd.key = KBD_LEFT; break; /* CUB kcub1 vt200 */
721 case 'F': /* (CPL) kend cons25 */
722 case 'e': kbd.key = KBD_END; break; /* (VPR) kend */
723 case 'H': kbd.key = KBD_HOME; break; /* CUP khome cons25 */
724 case 'I': kbd.key = KBD_PAGE_UP; break; /* (CHT) kpp cons25 */
725 case 'G': kbd.key = KBD_PAGE_DOWN; break; /* (CHA) knp cons25 */
726 /* Free BSD (TERM=cons25 etc.) */
727 /* case 'M': kbd.key = KBD_F1; break;*/ /* (DL) kf1 cons25 */
728 case 'N': kbd.key = KBD_F2; break; /* (EF) kf2 cons25 */
729 case 'O': kbd.key = KBD_F3; break; /* (EA) kf3 cons25 */
730 case 'P': kbd.key = KBD_F4; break; /* (DCH) kf4 cons25 */
731 case 'Q': kbd.key = KBD_F5; break; /* (SEE) kf5 cons25 */
732 /* case 'R': kbd.key = KBD_F6; break;*/ /* (CPR) kf6 cons25 */
733 case 'S': kbd.key = KBD_F7; break; /* (SU) kf7 cons25 */
734 case 'T': kbd.key = KBD_F8; break; /* (SD) kf8 cons25 */
735 case 'U': kbd.key = KBD_F9; break; /* (NP) kf9 cons25 */
736 case 'V': kbd.key = KBD_F10; break; /* (PP) kf10 cons25 */
737 case 'W': kbd.key = KBD_F11; break; /* (CTC) kf11 cons25 */
738 case 'X': kbd.key = KBD_F12; break; /* (ECH) kf12 cons25 */
740 case 'Z': /* CBT kcbt cons25 */
741 kbd.key = KBD_TAB; kbd.modifier = KBD_MOD_SHIFT; break;
743 case 'z': switch (v) { /* private */
744 case 247: kbd.key = KBD_INS; break; /* kich1 */
745 case 214: kbd.key = KBD_HOME; break; /* khome sun */
746 case 220: kbd.key = KBD_END; break; /* kend sun */
747 case 216: kbd.key = KBD_PAGE_UP; break; /* kpp sun */
748 case 222: kbd.key = KBD_PAGE_DOWN; break; /* knp sun */
749 case 249: kbd.key = KBD_DEL; break; /* kdch1 */
750 } break;
752 case '~': switch (v) { /* private */
753 case 1: kbd.key = KBD_HOME; break; /* khome linux */
754 case 2: kbd.key = KBD_INS; break; /* kich1 linux */
755 case 3: kbd.key = KBD_DEL; break; /* kdch1 linux */
756 case 4: kbd.key = KBD_END; break; /* kend linux */
757 case 5: kbd.key = KBD_PAGE_UP; break; /* kpp linux */
758 case 6: kbd.key = KBD_PAGE_DOWN; break; /* knp linux */
759 case 7: kbd.key = KBD_HOME; break; /* khome rxvt */
760 case 8: kbd.key = KBD_END; break; /* kend rxvt */
762 case 11: kbd.key = KBD_F1; break; /* kf1 rxvt */
763 case 12: kbd.key = KBD_F2; break; /* kf2 rxvt */
764 case 13: kbd.key = KBD_F3; break; /* kf3 rxvt */
765 case 14: kbd.key = KBD_F4; break; /* kf4 rxvt */
766 case 15: kbd.key = KBD_F5; break; /* kf5 rxvt */
768 case 17: kbd.key = KBD_F6; break; /* kf6 vt200 */
769 case 18: kbd.key = KBD_F7; break; /* kf7 vt200 */
770 case 19: kbd.key = KBD_F8; break; /* kf8 vt200 */
771 case 20: kbd.key = KBD_F9; break; /* kf9 vt200 */
772 case 21: kbd.key = KBD_F10; break; /* kf10 vt200 */
774 case 23: kbd.key = KBD_F11; break; /* kf11 vt200 */
775 case 24: kbd.key = KBD_F12; break; /* kf12 vt200 */
777 /* Give preference to F11 and F12 over shifted F1 and F2. */
779 case 23: kbd.key = KBD_F1; kbd.modifier = KBD_MOD_SHIFT; break;
780 case 24: kbd.key = KBD_F2; kbd.modifier = KBD_MOD_SHIFT; break;
783 case 25: kbd.key = KBD_F3; kbd.modifier = KBD_MOD_SHIFT; break;
784 case 26: kbd.key = KBD_F4; kbd.modifier = KBD_MOD_SHIFT; break;
786 case 28: kbd.key = KBD_F5; kbd.modifier = KBD_MOD_SHIFT; break;
787 case 29: kbd.key = KBD_F6; kbd.modifier = KBD_MOD_SHIFT; break;
789 case 31: kbd.key = KBD_F7; kbd.modifier = KBD_MOD_SHIFT; break;
790 case 32: kbd.key = KBD_F8; kbd.modifier = KBD_MOD_SHIFT; break;
791 case 33: kbd.key = KBD_F9; kbd.modifier = KBD_MOD_SHIFT; break;
792 case 34: kbd.key = KBD_F10; kbd.modifier = KBD_MOD_SHIFT; break;
794 } break;
796 case 'R': resize_terminal(); break; /* CPR u6 */
797 case 'M': /* (DL) kmous xterm */
798 #ifdef CONFIG_MOUSE
799 el = decode_terminal_mouse_escape_sequence(itrm, ev, el, v);
800 #endif /* CONFIG_MOUSE */
801 break;
804 /* KBD_UNDEF here means it was unrecognized or a mouse event. */
805 if (kbd.key != KBD_UNDEF)
806 set_kbd_interlink_event(ev, kbd.key, kbd.modifier);
808 return el;
811 /* Decode an escape sequence that begins with SS3 (SINGLE SHIFT 3).
812 * These are used for application cursor keys and the application keypad.
813 * Return one of:
814 * -1 if the escape sequence is not yet complete; the caller sets a timer.
815 * 0 if the escape sequence should be parsed by some other function.
816 * The length of the escape sequence otherwise.
817 * Returning >0 does not imply this function has altered *ev. */
818 static int
819 decode_terminal_application_key(struct itrm *itrm, struct interlink_event *ev)
821 unsigned char c;
822 struct interlink_event_keyboard kbd = { KBD_UNDEF, KBD_MOD_NONE };
824 assert(itrm->in.queue.len >= 2);
825 assert(itrm->in.queue.data[0] == ASCII_ESC);
826 assert(itrm->in.queue.data[1] == 0x4F); /* == 'O', incidentally */
827 if_assert_failed return 0;
829 if (itrm->in.queue.len < 3) return -1;
830 /* According to ECMA-35 section 8.4, a single (possibly multibyte)
831 * character follows the SS3. We now assume the code identifies
832 * GL as the single-shift area and the designated set has 94
833 * characters. */
834 c = itrm->in.queue.data[2];
835 if (c < 0x21 || c > 0x7E) return 0;
837 switch (c) { /* Terminfo $TERM */
838 case ' ': kbd.key = ' '; break; /* xterm */
839 case 'A': kbd.key = KBD_UP; break; /* kcuu1 vt100 */
840 case 'B': kbd.key = KBD_DOWN; break; /* kcud1 vt100 */
841 case 'C': kbd.key = KBD_RIGHT; break; /* kcuf1 vt100 */
842 case 'D': kbd.key = KBD_LEFT; break; /* kcub1 vt100 */
843 case 'F': kbd.key = KBD_END; break; /* kend xterm */
844 case 'H': kbd.key = KBD_HOME; break; /* khome xterm */
845 case 'I': kbd.key = KBD_TAB; break; /* xterm */
846 case 'M': kbd.key = KBD_ENTER; break; /* kent vt100 */
847 /* FIXME: xterm generates ESC O 2 P for Shift-PF1 */
848 case 'P': kbd.key = KBD_F1; break; /* kf1 vt100 */
849 case 'Q': kbd.key = KBD_F2; break; /* kf2 vt100 */
850 case 'R': kbd.key = KBD_F3; break; /* kf3 vt100 */
851 case 'S': kbd.key = KBD_F4; break; /* kf4 vt100 */
852 case 'X': kbd.key = '='; break; /* xterm */
854 case 'j': case 'k': case 'l': case 'm': /* *+,- xterm */
855 case 'n': case 'o': case 'p': case 'q': /* ./01 xterm */
856 case 'r': case 's': case 't': case 'u': /* 2345 xterm */
857 case 'v': case 'w': case 'x': case 'y': /* 6789 xterm */
858 kbd.key = c - 'p' + '0'; break;
860 if (kbd.key != KBD_UNDEF)
861 copy_struct(&ev->info.keyboard, &kbd);
863 return 3; /* even if we didn't recognize it */
867 /* Initialize *@ev to match the byte @key received from the terminal.
868 * Actually, @key could also be a value from enum term_event_special_key;
869 * but callers that use those values generally don't need the mapping
870 * provided by this function, so they call set_kbd_interlink_event()
871 * directly. */
872 static void
873 set_kbd_event(struct interlink_event *ev,
874 int key, term_event_modifier_T modifier)
876 switch (key) {
877 case ASCII_TAB:
878 key = KBD_TAB;
879 break;
880 #if defined(HAVE_SYS_CONSIO_H) || defined(HAVE_MACHINE_CONSOLE_H) /* BSD */
881 case ASCII_BS:
882 key = KBD_BS;
883 break;
884 case ASCII_DEL:
885 key = KBD_DEL;
886 break;
887 #else
888 case ASCII_BS:
889 case ASCII_DEL:
890 key = KBD_BS;
891 break;
892 #endif
893 case ASCII_LF:
894 case ASCII_CR:
895 key = KBD_ENTER;
896 break;
898 case ASCII_ESC:
899 key = KBD_ESC;
900 break;
902 default:
903 if (key < ' ') {
904 key += 'A' - 1;
905 modifier |= KBD_MOD_CTRL;
909 set_kbd_interlink_event(ev, key, modifier);
912 static void
913 kbd_timeout(struct itrm *itrm)
915 struct interlink_event ev;
916 int el;
918 itrm->timer = TIMER_ID_UNDEF;
920 assertm(itrm->in.queue.len, "timeout on empty queue");
921 assert(!itrm->blocked); /* block_itrm should have killed itrm->timer */
922 if_assert_failed return;
924 if (can_read(itrm->in.std)) {
925 in_kbd(itrm);
926 return;
929 if (itrm->in.queue.len >= 2 && itrm->in.queue.data[0] == ASCII_ESC) {
930 /* This is used for ESC [ and ESC O. */
931 set_kbd_event(&ev, itrm->in.queue.data[1], KBD_MOD_ALT);
932 el = 2;
933 } else {
934 set_kbd_event(&ev, itrm->in.queue.data[0], KBD_MOD_NONE);
935 el = 1;
937 itrm_queue_event(itrm, (char *) &ev, sizeof(ev));
939 itrm->in.queue.len -= el;
940 if (itrm->in.queue.len)
941 memmove(itrm->in.queue.data, itrm->in.queue.data + el, itrm->in.queue.len);
943 while (process_queue(itrm));
946 /* Parse one event from itrm->in.queue and append to itrm->out.queue.
947 * Return the number of bytes removed from itrm->in.queue; at least 0.
948 * If this function leaves the queue not full, it also reenables reading
949 * from itrm->in.std. (Because it does not add to the queue, it never
950 * need disable reading.) On entry, the itrm must not be blocked. */
951 static int
952 process_queue(struct itrm *itrm)
954 struct interlink_event ev;
955 int el = 0;
957 if (!itrm->in.queue.len) goto return_without_event;
958 assert(!itrm->blocked);
959 if_assert_failed return 0; /* unlike goto, don't enable reading */
961 set_kbd_interlink_event(&ev, KBD_UNDEF, KBD_MOD_NONE);
963 #ifdef DEBUG_ITRM_QUEUE
965 int i;
967 /* Dump current queue in a readable form to stderr. */
968 for (i = 0; i < itrm->in.queue.len; i++)
969 if (itrm->in.queue.data[i] == ASCII_ESC)
970 fprintf(stderr, "ESC ");
971 else if (isprint(itrm->in.queue.data[i]) && !isspace(itrm->in.queue.data[i]))
972 fprintf(stderr, "%c ", itrm->in.queue.data[i]);
973 else
974 fprintf(stderr, "0x%02x ", itrm->in.queue.data[i]);
976 fprintf(stderr, "\n");
977 fflush(stderr);
979 #endif /* DEBUG_ITRM_QUEUE */
981 /* el == -1 means itrm->in.queue appears to be the beginning of an
982 * escape sequence but it is not yet complete. Set a timer;
983 * if it times out, then assume it wasn't an escape sequence
984 * after all.
985 * el == 0 means this function has not yet figured out what the data
986 * in itrm->in.queue is, but some possibilities remain.
987 * One of them will be chosen before returning.
988 * el > 0 means some bytes were successfully parsed from the beginning
989 * of itrm->in.queue and should now be removed from there.
990 * However, this does not always imply an event will be queued.
993 /* ELinks should also recognize U+009B CONTROL SEQUENCE INTRODUCER
994 * as meaning the same as ESC 0x5B, and U+008F SINGLE SHIFT THREE as
995 * meaning the same as ESC 0x4F, but those cannot yet be implemented
996 * because of bug 777: the UTF-8 decoder is run too late. */
997 if (itrm->in.queue.data[0] == ASCII_ESC) {
998 if (itrm->in.queue.len < 2) {
999 el = -1;
1000 } else if (itrm->in.queue.data[1] == 0x5B /* CSI */) {
1001 el = decode_terminal_escape_sequence(itrm, &ev);
1002 } else if (itrm->in.queue.data[1] == 0x4F /* SS3 */) {
1003 el = decode_terminal_application_key(itrm, &ev);
1004 } else if (itrm->in.queue.data[1] == ASCII_ESC) {
1005 /* ESC ESC can be either Alt-Esc or the
1006 * beginning of e.g. ESC ESC 0x5B 0x41,
1007 * which we should parse as Esc Up. */
1008 if (itrm->in.queue.len < 3) {
1009 /* Need more data to figure it out. */
1010 el = -1;
1011 } else if (itrm->in.queue.data[2] == 0x5B
1012 || itrm->in.queue.data[2] == 0x4F) {
1013 /* The first ESC appears to be followed
1014 * by an escape sequence. Treat it as
1015 * a standalone Esc. */
1016 el = 1;
1017 set_kbd_event(&ev, itrm->in.queue.data[0],
1018 KBD_MOD_NONE);
1019 } else {
1020 /* The second ESC of ESC ESC is not the
1021 * beginning of any known escape sequence.
1022 * This must be Alt-Esc, then. */
1023 el = 2;
1024 set_kbd_event(&ev, itrm->in.queue.data[1],
1025 KBD_MOD_ALT);
1027 } else { /* ESC followed by something else */
1028 el = 2;
1029 set_kbd_event(&ev, itrm->in.queue.data[1],
1030 KBD_MOD_ALT);
1033 } else if (itrm->in.queue.data[0] == 0) {
1034 static const struct term_event_keyboard os2xtd[256] = {
1035 #include "terminal/key.inc"
1038 if (itrm->in.queue.len < 2)
1039 el = -1;
1040 else {
1041 el = 2;
1042 set_kbd_interlink_event(&ev,
1043 os2xtd[itrm->in.queue.data[1]].key,
1044 os2xtd[itrm->in.queue.data[1]].modifier);
1048 if (el == 0) {
1049 el = 1;
1050 set_kbd_event(&ev, itrm->in.queue.data[0], KBD_MOD_NONE);
1053 /* The call to decode_terminal_escape_sequence() might have changed the
1054 * keyboard event to a mouse event. */
1055 if (ev.ev == EVENT_MOUSE || ev.info.keyboard.key != KBD_UNDEF)
1056 itrm_queue_event(itrm, (char *) &ev, sizeof(ev));
1058 return_without_event:
1059 if (el == -1) {
1060 install_timer(&itrm->timer, ESC_TIMEOUT, (void (*)(void *)) kbd_timeout,
1061 itrm);
1062 return 0;
1063 } else {
1064 assertm(itrm->in.queue.len >= el, "event queue underflow");
1065 if_assert_failed { itrm->in.queue.len = el; }
1067 itrm->in.queue.len -= el;
1068 if (itrm->in.queue.len)
1069 memmove(itrm->in.queue.data, itrm->in.queue.data + el, itrm->in.queue.len);
1071 if (itrm->in.queue.len < ITRM_IN_QUEUE_SIZE)
1072 handle_itrm_stdin(itrm);
1074 return el;
1079 /* A select_handler_T read_func for itrm->in.std. This is called when
1080 * characters typed by the user arrive from the terminal. */
1081 static void
1082 in_kbd(struct itrm *itrm)
1084 int r;
1086 if (!can_read(itrm->in.std)) return;
1088 kill_timer(&itrm->timer);
1090 if (itrm->in.queue.len >= ITRM_IN_QUEUE_SIZE) {
1091 unhandle_itrm_stdin(itrm);
1092 while (process_queue(itrm));
1093 return;
1096 r = safe_read(itrm->in.std, itrm->in.queue.data + itrm->in.queue.len,
1097 ITRM_IN_QUEUE_SIZE - itrm->in.queue.len);
1098 if (r <= 0) {
1099 free_itrm(itrm);
1100 return;
1103 itrm->in.queue.len += r;
1104 if (itrm->in.queue.len > ITRM_IN_QUEUE_SIZE) {
1105 ERROR(gettext("Too many bytes read from the itrm!"));
1106 itrm->in.queue.len = ITRM_IN_QUEUE_SIZE;
1109 while (process_queue(itrm));
1112 /* Enable reading from itrm->in.std. ELinks will read any available
1113 * bytes from the tty into itrm->in.queue and then parse them.
1114 * Reading should be enabled whenever itrm->in.queue is not full and
1115 * itrm->blocked is 0. */
1116 static void
1117 handle_itrm_stdin(struct itrm *itrm)
1119 set_handlers(itrm->in.std, (select_handler_T) in_kbd, NULL,
1120 (select_handler_T) free_itrm, itrm);
1123 /* Disable reading from itrm->in.std. Reading should be disabled
1124 * whenever itrm->in.queue is full (there is no room for the data)
1125 * or itrm->blocked is 1 (other processes may read the data). */
1126 static void
1127 unhandle_itrm_stdin(struct itrm *itrm)
1129 set_handlers(itrm->in.std, (select_handler_T) NULL, NULL,
1130 (select_handler_T) free_itrm, itrm);