Doxygenate src/terminal/
[elinks/kon.git] / src / terminal / kbd.c
blobf6bc226ffe4917c8bd34b2d360dd036d00ac93dd
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 "terminal/hardio.h"
35 #include "terminal/itrm.h"
36 #include "terminal/kbd.h"
37 #include "terminal/mouse.h"
38 #include "terminal/terminal.h"
39 #include "util/error.h"
40 #include "util/memory.h"
41 #include "util/string.h"
42 #include "util/time.h"
44 struct itrm *ditrm = NULL;
46 static void free_itrm(struct itrm *);
47 static void in_kbd(struct itrm *);
48 static void in_sock(struct itrm *);
49 static int process_queue(struct itrm *);
50 static void handle_itrm_stdin(struct itrm *);
51 static void unhandle_itrm_stdin(struct itrm *);
53 #ifdef CONFIG_DEBUG
54 /** This hack makes GCC put enum term_event_special_key in the debug
55 * information even though it is not otherwise used. The const
56 * prevents an unused-variable warning. */
57 static const enum term_event_special_key dummy_term_event_special_key;
58 #endif
61 int
62 is_blocked(void)
64 return ditrm && ditrm->blocked;
68 void
69 free_all_itrms(void)
71 if (ditrm) free_itrm(ditrm);
75 /** A select_handler_T write_func for itrm_out.sock. This is called
76 * when there is data in \c itrm->out.queue and it is possible to write
77 * it to \c itrm->out.sock. When \c itrm->out.queue becomes empty, this
78 * handler is temporarily removed. */
79 static void
80 itrm_queue_write(struct itrm *itrm)
82 int written;
83 int qlen = int_min(itrm->out.queue.len, 128);
85 assertm(qlen, "event queue empty");
86 if_assert_failed return;
88 written = safe_write(itrm->out.sock, itrm->out.queue.data, qlen);
89 if (written <= 0) {
90 if (written < 0) free_itrm(itrm); /* write error */
91 return;
94 itrm->out.queue.len -= written;
96 if (itrm->out.queue.len == 0) {
97 set_handlers(itrm->out.sock,
98 get_handler(itrm->out.sock, SELECT_HANDLER_READ),
99 NULL,
100 get_handler(itrm->out.sock, SELECT_HANDLER_ERROR),
101 get_handler(itrm->out.sock, SELECT_HANDLER_DATA));
102 } else {
103 assert(itrm->out.queue.len > 0);
104 memmove(itrm->out.queue.data, itrm->out.queue.data + written, itrm->out.queue.len);
109 void
110 itrm_queue_event(struct itrm *itrm, unsigned char *data, int len)
112 int w = 0;
114 if (!len) return;
116 if (!itrm->out.queue.len && can_write(itrm->out.sock)) {
117 w = safe_write(itrm->out.sock, data, len);
118 if (w <= 0 && HPUX_PIPE) {
119 register_bottom_half(free_itrm, itrm);
120 return;
124 if (w < len) {
125 int left = len - w;
126 unsigned char *c = mem_realloc(itrm->out.queue.data,
127 itrm->out.queue.len + left);
129 if (!c) {
130 free_itrm(itrm);
131 return;
134 itrm->out.queue.data = c;
135 memcpy(itrm->out.queue.data + itrm->out.queue.len, data + w, left);
136 itrm->out.queue.len += left;
137 set_handlers(itrm->out.sock,
138 get_handler(itrm->out.sock, SELECT_HANDLER_READ),
139 (select_handler_T) itrm_queue_write,
140 (select_handler_T) free_itrm, itrm);
145 void
146 kbd_ctrl_c(void)
148 struct interlink_event ev;
150 if (!ditrm) return;
151 set_kbd_interlink_event(&ev, KBD_CTRL_C, KBD_MOD_NONE);
152 itrm_queue_event(ditrm, (unsigned char *) &ev, sizeof(ev));
155 #define write_sequence(fd, seq) \
156 hard_write(fd, seq, sizeof(seq) - 1)
159 #define INIT_TERMINAL_SEQ "\033)0\0337" /**< Special Character and Line Drawing Set, Save Cursor */
160 #define INIT_ALT_SCREEN_SEQ "\033[?47h" /**< Use Alternate Screen Buffer */
162 static void
163 send_init_sequence(int h, int altscreen)
165 write_sequence(h, INIT_TERMINAL_SEQ);
167 /* If alternate screen is supported switch to it. */
168 if (altscreen) {
169 write_sequence(h, INIT_ALT_SCREEN_SEQ);
171 #ifdef CONFIG_MOUSE
172 send_mouse_init_sequence(h);
173 #endif
176 #define DONE_CLS_SEQ "\033[2J" /**< Erase in Display, Clear All */
177 #define DONE_TERMINAL_SEQ "\0338\r \b" /**< Restore Cursor (DECRC) + ??? */
178 #define DONE_ALT_SCREEN_SEQ "\033[?47l" /**< Use Normal Screen Buffer */
180 static void
181 send_done_sequence(int h, int altscreen)
183 write_sequence(h, DONE_CLS_SEQ);
185 #ifdef CONFIG_MOUSE
186 send_mouse_done_sequence(h);
187 #endif
189 /* Switch from alternate screen. */
190 if (altscreen) {
191 write_sequence(h, DONE_ALT_SCREEN_SEQ);
194 write_sequence(h, DONE_TERMINAL_SEQ);
198 #undef write_sequence
200 void
201 resize_terminal(void)
203 struct interlink_event ev;
204 int width, height;
206 get_terminal_size(ditrm->out.std, &width, &height);
207 set_resize_interlink_event(&ev, width, height);
208 itrm_queue_event(ditrm, (char *) &ev, sizeof(ev));
211 void
212 get_terminal_name(unsigned char name[MAX_TERM_LEN])
214 unsigned char *term = getenv("TERM");
215 int i;
217 memset(name, 0, MAX_TERM_LEN);
219 if (!term) return;
221 for (i = 0; term[i] != 0 && i < MAX_TERM_LEN - 1; i++)
222 name[i] = isident(term[i]) ? term[i] : '-';
226 static int
227 setraw(int fd, struct termios *p)
229 struct termios t;
231 memset(&t, 0, sizeof(t));
232 if (tcgetattr(fd, &t)) return -1;
234 if (p) copy_struct(p, &t);
236 elinks_cfmakeraw(&t);
237 t.c_lflag |= ISIG;
238 #ifdef TOSTOP
239 t.c_lflag |= TOSTOP;
240 #endif
241 t.c_oflag |= OPOST;
242 if (tcsetattr(fd, TCSANOW, &t)) return -1;
244 return 0;
247 /** Construct the struct itrm of this process, make ditrm point to it,
248 * set up select() handlers, and send the initial interlink packet.
250 * The first five parameters are file descriptors that this function
251 * saves in submembers of struct itrm, and for which this function may
252 * set select() handlers. Please see the definitions of struct
253 * itrm_in and struct itrm_out for further explanations.
255 * @param std_in itrm_in.std: read tty device (or pipe)
256 * @param std_out itrm_out.std: write tty device (or pipe)
257 * @param sock_in itrm_in.sock
258 * - If master: == @a std_out (masterhood flag)
259 * - If slave: read socket from master
260 * @param sock_out itrm_out.sock
261 * - If master: write pipe to same process
262 * - If slave: write socket to master
263 * @param ctl_in itrm_in.ctl: control tty device
265 * The remaining three parameters control the initial interlink packet.
267 * @param init_string A string to be passed to the master process. Need
268 * not be null-terminated. If @a remote == 0, this is
269 * a URI. Otherwise, this is a remote command.
270 * @param init_len The length of init_string, in bytes.
271 * @param remote = 0 if asking the master to start a new session
272 * and display it via this process. Otherwise,
273 * enum remote_session_flags. */
274 void
275 handle_trm(int std_in, int std_out, int sock_in, int sock_out, int ctl_in,
276 void *init_string, int init_len, int remote)
278 struct itrm *itrm;
279 struct terminal_info info;
280 struct interlink_event_size *size = &info.event.info.size;
281 unsigned char *ts;
283 memset(&info, 0, sizeof(info));
285 get_terminal_size(ctl_in, &size->width, &size->height);
286 info.event.ev = EVENT_INIT;
287 info.system_env = get_system_env();
288 info.length = init_len;
290 if (remote) {
291 info.session_info = remote;
292 info.magic = INTERLINK_REMOTE_MAGIC;
293 } else {
294 info.session_info = get_cmd_opt_int("base-session");
295 info.magic = INTERLINK_NORMAL_MAGIC;
298 itrm = mem_calloc(1, sizeof(*itrm));
299 if (!itrm) return;
301 itrm->in.queue.data = mem_calloc(1, ITRM_IN_QUEUE_SIZE);
302 if (!itrm->in.queue.data) {
303 mem_free(itrm);
304 return;
307 ditrm = itrm;
308 itrm->in.std = std_in;
309 itrm->out.std = std_out;
310 itrm->in.sock = sock_in;
311 itrm->out.sock = sock_out;
312 itrm->in.ctl = ctl_in;
313 itrm->timer = TIMER_ID_UNDEF;
314 itrm->remote = !!remote;
316 /* FIXME: Combination altscreen + xwin does not work as it should,
317 * mouse clicks are reportedly partially ignored. */
318 if (info.system_env & (ENV_SCREEN | ENV_XWIN))
319 itrm->altscreen = 1;
321 if (!remote) {
322 if (ctl_in >= 0) setraw(ctl_in, &itrm->t);
323 send_init_sequence(std_out, itrm->altscreen);
324 handle_terminal_resize(ctl_in, resize_terminal);
325 #ifdef CONFIG_MOUSE
326 enable_mouse();
327 #endif
330 handle_itrm_stdin(itrm);
332 if (sock_in != std_out)
333 set_handlers(sock_in, (select_handler_T) in_sock,
334 NULL, (select_handler_T) free_itrm, itrm);
336 get_terminal_name(info.name);
338 ts = get_cwd();
339 if (ts) {
340 memcpy(info.cwd, ts, int_min(strlen(ts), MAX_CWD_LEN));
341 mem_free(ts);
344 itrm_queue_event(itrm, (char *) &info, TERMINAL_INFO_SIZE);
345 itrm_queue_event(itrm, (char *) init_string, init_len);
349 /** A select_handler_T read_func and error_func for the pipe (long) @a h.
350 * This is called when the subprocess started on the terminal of this
351 * ELinks process exits. ELinks then resumes using the terminal. */
352 static void
353 unblock_itrm_x(void *h)
355 close_handle(h);
356 if (!ditrm) return;
357 unblock_itrm();
358 resize_terminal();
363 unblock_itrm(void)
365 if (!ditrm) return -1;
367 if (ditrm->in.ctl >= 0 && setraw(ditrm->in.ctl, NULL)) return -1;
368 ditrm->blocked = 0;
369 send_init_sequence(ditrm->out.std, ditrm->altscreen);
371 handle_itrm_stdin(ditrm);
372 resume_mouse(ditrm->mouse_h);
374 handle_terminal_resize(ditrm->in.ctl, resize_terminal);
375 unblock_stdin();
377 return 0;
381 void
382 block_itrm(void)
384 if (!ditrm) return;
386 ditrm->blocked = 1;
387 block_stdin();
388 kill_timer(&ditrm->timer);
389 ditrm->in.queue.len = 0;
390 unhandle_terminal_resize(ditrm->in.ctl);
391 send_done_sequence(ditrm->out.std, ditrm->altscreen);
392 tcsetattr(ditrm->in.ctl, TCSANOW, &ditrm->t);
393 unhandle_itrm_stdin(ditrm);
394 suspend_mouse(ditrm->mouse_h);
398 static void
399 free_itrm(struct itrm *itrm)
401 if (!itrm) return;
403 if (!itrm->remote) {
404 if (itrm->orig_title && *itrm->orig_title) {
405 set_window_title(itrm->orig_title);
407 } else if (itrm->touched_title) {
408 /* Set the window title to the value of $TERM if X11
409 * wasn't compiled in. Should hopefully make at least
410 * half the users happy. (debian bug #312955) */
411 unsigned char title[MAX_TERM_LEN];
413 get_terminal_name(title);
414 if (*title)
415 set_window_title(title);
419 unhandle_terminal_resize(itrm->in.ctl);
420 #ifdef CONFIG_MOUSE
421 disable_mouse();
422 #endif
423 send_done_sequence(itrm->out.std, itrm->altscreen);
424 tcsetattr(itrm->in.ctl, TCSANOW, &itrm->t);
427 mem_free_set(&itrm->orig_title, NULL);
429 clear_handlers(itrm->in.std);
430 clear_handlers(itrm->in.sock);
431 clear_handlers(itrm->out.std);
432 clear_handlers(itrm->out.sock);
434 kill_timer(&itrm->timer);
436 if (itrm == ditrm) ditrm = NULL;
437 mem_free_if(itrm->out.queue.data);
438 mem_free_if(itrm->in.queue.data);
439 mem_free(itrm);
442 /** Resize terminal to dimensions specified by @a text string.
443 * @a text should look like "width,height,old-width,old-height"
444 * where width and height are integers. */
445 static inline void
446 resize_terminal_from_str(unsigned char *text)
448 enum { NEW_WIDTH = 0, NEW_HEIGHT, OLD_WIDTH, OLD_HEIGHT, NUMBERS } i;
449 int numbers[NUMBERS];
451 assert(text && *text);
452 if_assert_failed return;
454 for (i = 0; i < NUMBERS; i++) {
455 unsigned char *p = strchr(text, ',');
457 if (p) {
458 *p++ = '\0';
460 } else if (i < OLD_HEIGHT) {
461 return;
464 numbers[i] = atoi(text);
466 if (p) text = p;
469 resize_window(numbers[NEW_WIDTH], numbers[NEW_HEIGHT],
470 numbers[OLD_WIDTH], numbers[OLD_HEIGHT]);
471 resize_terminal();
474 void
475 dispatch_special(unsigned char *text)
477 switch (text[0]) {
478 case TERM_FN_TITLE:
479 if (ditrm) {
480 if (ditrm->remote)
481 break;
483 if (!ditrm->orig_title)
484 ditrm->orig_title = get_window_title();
485 ditrm->touched_title = 1;
487 set_window_title(text + 1);
488 break;
489 case TERM_FN_RESIZE:
490 if (ditrm && ditrm->remote)
491 break;
493 resize_terminal_from_str(text + 1);
494 break;
498 static void inline
499 safe_hard_write(int fd, unsigned char *buf, int len)
501 if (is_blocked()) return;
503 want_draw();
504 hard_write(fd, buf, len);
505 done_draw();
508 /** A select_handler_T read_func for itrm_in.sock. A slave process
509 * calls this when the master sends it data to be displayed. The
510 * master process never calls this. */
511 static void
512 in_sock(struct itrm *itrm)
514 struct string path;
515 struct string delete;
516 char ch;
517 int fg; /* enum term_exec */
518 ssize_t bytes_read, i, p;
519 unsigned char buf[ITRM_OUT_QUEUE_SIZE];
521 bytes_read = safe_read(itrm->in.sock, buf, ITRM_OUT_QUEUE_SIZE);
522 if (bytes_read <= 0) goto free_and_return;
524 qwerty:
525 for (i = 0; i < bytes_read; i++)
526 if (!buf[i])
527 goto has_nul_byte;
529 safe_hard_write(itrm->out.std, buf, bytes_read);
530 return;
532 has_nul_byte:
533 if (i) safe_hard_write(itrm->out.std, buf, i);
535 i++;
536 assert(ITRM_OUT_QUEUE_SIZE - i > 0);
537 memmove(buf, buf + i, ITRM_OUT_QUEUE_SIZE - i);
538 bytes_read -= i;
539 p = 0;
541 #define RD(xx) { \
542 unsigned char cc; \
544 if (p < bytes_read) \
545 cc = buf[p++]; \
546 else if ((hard_read(itrm->in.sock, &cc, 1)) <= 0) \
547 goto free_and_return; \
548 xx = cc; \
551 RD(fg);
553 if (!init_string(&path)) goto free_and_return;
555 while (1) {
556 RD(ch);
557 if (!ch) break;
558 add_char_to_string(&path, ch);
561 if (!init_string(&delete)) {
562 done_string(&path);
563 goto free_and_return;
566 while (1) {
567 RD(ch);
568 if (!ch) break;
569 add_char_to_string(&delete, ch);
572 #undef RD
574 if (!*path.source) {
575 dispatch_special(delete.source);
577 } else {
578 int blockh;
579 unsigned char *param;
580 int path_len, del_len, param_len;
582 /* TODO: Should this be changed to allow TERM_EXEC_NEWWIN
583 * in a blocked terminal? There is similar code in
584 * exec_on_terminal(). --KON, 2007 */
585 if (is_blocked() && fg != TERM_EXEC_BG) {
586 if (*delete.source) unlink(delete.source);
587 goto nasty_thing;
590 path_len = path.length;
591 del_len = delete.length;
592 param_len = path_len + del_len + 3;
594 param = mem_alloc(param_len);
595 if (!param) goto nasty_thing;
597 param[0] = fg;
598 memcpy(param + 1, path.source, path_len + 1);
599 memcpy(param + 1 + path_len + 1, delete.source, del_len + 1);
601 if (fg == TERM_EXEC_FG) block_itrm();
603 blockh = start_thread((void (*)(void *, int)) exec_thread,
604 param, param_len);
605 mem_free(param);
607 if (blockh == -1) {
608 if (fg == TERM_EXEC_FG)
609 unblock_itrm();
611 goto nasty_thing;
614 if (fg == TERM_EXEC_FG) {
615 set_handlers(blockh, (select_handler_T) unblock_itrm_x,
616 NULL, (select_handler_T) unblock_itrm_x,
617 (void *) (long) blockh);
619 } else {
620 set_handlers(blockh, close_handle, NULL, close_handle,
621 (void *) (long) blockh);
625 nasty_thing:
626 done_string(&path);
627 done_string(&delete);
628 assert(ITRM_OUT_QUEUE_SIZE - p > 0);
629 memmove(buf, buf + p, ITRM_OUT_QUEUE_SIZE - p);
630 bytes_read -= p;
632 goto qwerty;
634 free_and_return:
635 free_itrm(itrm);
639 /** Parse an ECMA-48 control sequence that was received from a
640 * terminal. Extract the Final Byte (if there are no Intermediate
641 * Bytes) and the value of the first parameter (if it is an integer).
643 * This function assumes the control sequence begins with a CSI -
644 * CONTROL SEQUENCE INTRODUCER encoded as ESC [. (ECMA-48 also allows
645 * 0x9B as a single-byte CSI, but we don't support that here.)
647 * @returns one of:
648 * - -1 if the control sequence is not yet complete; the caller sets a timer.
649 * - 0 if the control sequence does not comply with ECMA-48.
650 * - The length of the control sequence otherwise. */
651 static inline int
652 get_esc_code(unsigned char *str, int len, unsigned char *final_byte,
653 int *first_param_value)
655 const int parameter_pos = 2;
656 int intermediate_pos;
657 int final_pos;
658 int pos;
660 *final_byte = '\0';
661 *first_param_value = 0;
663 /* Parameter Bytes */
664 pos = parameter_pos;
665 while (pos < len && str[pos] >= 0x30 && str[pos] <= 0x3F)
666 ++pos;
668 /* Intermediate Bytes */
669 intermediate_pos = pos;
670 while (pos < len && str[pos] >= 0x20 && str[pos] <= 0x2F)
671 ++pos;
673 /* Final Byte */
674 final_pos = pos;
675 if (pos >= len)
676 return -1;
677 if (!(str[pos] >= 0x40 && str[pos] <= 0x7E))
678 return 0;
680 /* The control sequence seems OK. If the first Parameter
681 * Byte indicates that the parameter string is formatted
682 * as specified in clause 5.4.2 of ECMA-48, and the first
683 * parameter is an integer, then compute its value.
684 * (We need not check @len here because the loop cannot get
685 * past the Final Byte.) */
686 for (pos = parameter_pos; str[pos] >= 0x30 && str[pos] <= 0x39; ++pos)
687 *first_param_value = *first_param_value * 10 + str[pos] - 0x30;
688 /* If the first parameter contains an embedded separator, then
689 * the value is not an integer, so discard what we computed. */
690 if (str[pos] == 0x3A)
691 *first_param_value = 0;
693 /* The meaning of the Final Byte depends on the Intermediate
694 * Bytes. Because we don't currently need to recognize any
695 * control sequences that use Intermediate Bytes, we just
696 * discard the Final Byte if there are any Intermediate
697 * Bytes. */
698 if (intermediate_pos == final_pos)
699 *final_byte = str[final_pos];
701 return final_pos + 1;
704 /* Define it to dump queue content in a readable form,
705 * it may help to determine terminal sequences, and see what goes on. --Zas */
706 /* #define DEBUG_ITRM_QUEUE */
708 #ifdef DEBUG_ITRM_QUEUE
709 #include <stdio.h>
710 #include <ctype.h> /* isprint() isspace() */
711 #endif
713 /** Decode a control sequence that begins with CSI (CONTROL SEQUENCE
714 * INTRODUCER) encoded as ESC [, and set @a *ev accordingly.
715 * (ECMA-48 also allows 0x9B as a single-byte CSI, but we don't
716 * support that here.)
718 * @returns one of:
719 * - -1 if the control sequence is not yet complete; the caller sets a timer.
720 * - 0 if the control sequence should be parsed by some other function.
721 * - The length of the control sequence otherwise.
722 * Returning >0 does not imply this function has altered @a *ev. */
723 static int
724 decode_terminal_escape_sequence(struct itrm *itrm, struct interlink_event *ev)
726 struct term_event_keyboard kbd = { KBD_UNDEF, KBD_MOD_NONE };
727 unsigned char c;
728 int v;
729 int el;
731 if (itrm->in.queue.len < 3) return -1;
733 if (itrm->in.queue.data[2] == '[') {
734 /* The terminfo entry for linux has "kf1=\E[[A", etc.
735 * These are not control sequences compliant with
736 * clause 5.4 of ECMA-48. (According to ECMA-48,
737 * "\E[[" is SRS - START REVERSED STRING.) */
738 if (itrm->in.queue.len >= 4
739 && itrm->in.queue.data[3] >= 'A'
740 && itrm->in.queue.data[3] <= 'L') {
741 kbd.key = number_to_kbd_fkey(itrm->in.queue.data[3] - 'A' + 1);
742 set_kbd_interlink_event(ev, kbd.key, kbd.modifier);
743 return 4;
746 return -1;
749 el = get_esc_code(itrm->in.queue.data, itrm->in.queue.len, &c, &v);
750 if (el == -1) {
751 /* If the control sequence is incomplete but itrm->in.queue
752 * is already full, then we must not wait for more input:
753 * kbd_timeout might call in_kbd and thus process_input
754 * and come right back here. Better just reject the whole
755 * thing and let the initial CSI be handled as Alt-[. */
756 if (itrm->in.queue.len == ITRM_IN_QUEUE_SIZE)
757 return 0;
758 else
759 return -1;
761 #ifdef DEBUG_ITRM_QUEUE
762 fprintf(stderr, "esc code: %c v=%d c=%c el=%d\n", itrm->in.queue.data[1], v, c, el);
763 fflush(stderr);
764 #endif
766 /* The following information should be listed for each escape
767 * sequence recognized here:
769 * 1. Which control function ECMA-48 assigns to the sequence.
770 * Put parentheses around this if the control function
771 * seems unrelated to how ELinks actually treats the
772 * sequence. Write "private" if it is a control sequence
773 * reserved for private or experimental use in ECMA-48.
774 * (Those have a Final Byte in the range 0x70 to 0x7F,
775 * optionally preceded by a single Intermediate Byte 0x20.)
777 * 2. The capname used by Terminfo, if any. These should help
778 * when ELinks is eventually changed to read escape
779 * sequences from Terminfo (bug 96).
781 * 3. The $TERM identifier of some terminal that generates
782 * this escape sequence with the meaning expected by
783 * ELinks. Escape sequences with no known terminal may end
784 * up being removed from ELinks when bug 96 is fixed.
785 */ /* ECMA-48 Terminfo $TERM */
786 switch (c) { /* ------- -------- ----- */
787 case 'A': kbd.key = KBD_UP; break; /* CUU kcuu1 vt200 */
788 case 'B': kbd.key = KBD_DOWN; break; /* CUD kcud1 vt200 */
789 case 'C': kbd.key = KBD_RIGHT; break; /* CUF kcuf1 vt200 */
790 case 'D': kbd.key = KBD_LEFT; break; /* CUB kcub1 vt200 */
791 case 'F': /* (CPL) kend cons25 */
792 case 'e': kbd.key = KBD_END; break; /* (VPR) kend */
793 case 'H': kbd.key = KBD_HOME; break; /* CUP khome cons25 */
794 case 'I': kbd.key = KBD_PAGE_UP; break; /* (CHT) kpp cons25 */
795 case 'G': kbd.key = KBD_PAGE_DOWN; break; /* (CHA) knp cons25 */
796 /* Free BSD (TERM=cons25 etc.) */
797 /* case 'M': kbd.key = KBD_F1; break;*/ /* (DL) kf1 cons25 */
798 case 'N': kbd.key = KBD_F2; break; /* (EF) kf2 cons25 */
799 case 'O': kbd.key = KBD_F3; break; /* (EA) kf3 cons25 */
800 case 'P': kbd.key = KBD_F4; break; /* (DCH) kf4 cons25 */
801 case 'Q': kbd.key = KBD_F5; break; /* (SEE) kf5 cons25 */
802 /* case 'R': kbd.key = KBD_F6; break;*/ /* (CPR) kf6 cons25 */
803 case 'S': kbd.key = KBD_F7; break; /* (SU) kf7 cons25 */
804 case 'T': kbd.key = KBD_F8; break; /* (SD) kf8 cons25 */
805 case 'U': kbd.key = KBD_F9; break; /* (NP) kf9 cons25 */
806 case 'V': kbd.key = KBD_F10; break; /* (PP) kf10 cons25 */
807 case 'W': kbd.key = KBD_F11; break; /* (CTC) kf11 cons25 */
808 case 'X': kbd.key = KBD_F12; break; /* (ECH) kf12 cons25 */
810 case 'Z': /* CBT kcbt cons25 */
811 kbd.key = KBD_TAB; kbd.modifier = KBD_MOD_SHIFT; break;
813 case 'z': switch (v) { /* private */
814 case 247: kbd.key = KBD_INS; break; /* kich1 */
815 case 214: kbd.key = KBD_HOME; break; /* khome sun */
816 case 220: kbd.key = KBD_END; break; /* kend sun */
817 case 216: kbd.key = KBD_PAGE_UP; break; /* kpp sun */
818 case 222: kbd.key = KBD_PAGE_DOWN; break; /* knp sun */
819 case 249: kbd.key = KBD_DEL; break; /* kdch1 */
820 } break;
822 case '~': switch (v) { /* private */
823 case 1: kbd.key = KBD_HOME; break; /* khome linux */
824 case 2: kbd.key = KBD_INS; break; /* kich1 linux */
825 case 3: kbd.key = KBD_DEL; break; /* kdch1 linux */
826 case 4: kbd.key = KBD_END; break; /* kend linux */
827 case 5: kbd.key = KBD_PAGE_UP; break; /* kpp linux */
828 case 6: kbd.key = KBD_PAGE_DOWN; break; /* knp linux */
829 case 7: kbd.key = KBD_HOME; break; /* khome rxvt */
830 case 8: kbd.key = KBD_END; break; /* kend rxvt */
832 case 11: kbd.key = KBD_F1; break; /* kf1 rxvt */
833 case 12: kbd.key = KBD_F2; break; /* kf2 rxvt */
834 case 13: kbd.key = KBD_F3; break; /* kf3 rxvt */
835 case 14: kbd.key = KBD_F4; break; /* kf4 rxvt */
836 case 15: kbd.key = KBD_F5; break; /* kf5 rxvt */
838 case 17: kbd.key = KBD_F6; break; /* kf6 vt200 */
839 case 18: kbd.key = KBD_F7; break; /* kf7 vt200 */
840 case 19: kbd.key = KBD_F8; break; /* kf8 vt200 */
841 case 20: kbd.key = KBD_F9; break; /* kf9 vt200 */
842 case 21: kbd.key = KBD_F10; break; /* kf10 vt200 */
844 case 23: kbd.key = KBD_F11; break; /* kf11 vt200 */
845 case 24: kbd.key = KBD_F12; break; /* kf12 vt200 */
847 /* Give preference to F11 and F12 over shifted F1 and F2. */
849 case 23: kbd.key = KBD_F1; kbd.modifier = KBD_MOD_SHIFT; break;
850 case 24: kbd.key = KBD_F2; kbd.modifier = KBD_MOD_SHIFT; break;
853 case 25: kbd.key = KBD_F3; kbd.modifier = KBD_MOD_SHIFT; break;
854 case 26: kbd.key = KBD_F4; kbd.modifier = KBD_MOD_SHIFT; break;
856 case 28: kbd.key = KBD_F5; kbd.modifier = KBD_MOD_SHIFT; break;
857 case 29: kbd.key = KBD_F6; kbd.modifier = KBD_MOD_SHIFT; break;
859 case 31: kbd.key = KBD_F7; kbd.modifier = KBD_MOD_SHIFT; break;
860 case 32: kbd.key = KBD_F8; kbd.modifier = KBD_MOD_SHIFT; break;
861 case 33: kbd.key = KBD_F9; kbd.modifier = KBD_MOD_SHIFT; break;
862 case 34: kbd.key = KBD_F10; kbd.modifier = KBD_MOD_SHIFT; break;
864 } break;
866 case 'R': resize_terminal(); break; /* CPR u6 */
867 case 'M': /* (DL) kmous xterm */
868 #ifdef CONFIG_MOUSE
869 el = decode_terminal_mouse_escape_sequence(itrm, ev, el, v);
870 #endif /* CONFIG_MOUSE */
871 break;
874 /* KBD_UNDEF here means it was unrecognized or a mouse event. */
875 if (kbd.key != KBD_UNDEF)
876 set_kbd_interlink_event(ev, kbd.key, kbd.modifier);
878 return el;
881 /** Decode an escape sequence that begins with SS3 (SINGLE SHIFT 3).
882 * These are used for application cursor keys and the application keypad.
883 * @returns one of:
884 * - -1 if the escape sequence is not yet complete; the caller sets a timer.
885 * - 0 if the escape sequence should be parsed by some other function.
886 * - The length of the escape sequence otherwise.
887 * Returning >0 does not imply this function has altered @a *ev. */
888 static int
889 decode_terminal_application_key(struct itrm *itrm, struct interlink_event *ev)
891 unsigned char c;
892 struct interlink_event_keyboard kbd = { KBD_UNDEF, KBD_MOD_NONE };
894 assert(itrm->in.queue.len >= 2);
895 assert(itrm->in.queue.data[0] == ASCII_ESC);
896 assert(itrm->in.queue.data[1] == 0x4F); /* == 'O', incidentally */
897 if_assert_failed return 0;
899 if (itrm->in.queue.len < 3) return -1;
900 /* According to ECMA-35 section 8.4, a single (possibly multibyte)
901 * character follows the SS3. We now assume the code identifies
902 * GL as the single-shift area and the designated set has 94
903 * characters. */
904 c = itrm->in.queue.data[2];
905 if (c < 0x21 || c > 0x7E) return 0;
907 switch (c) { /* Terminfo $TERM */
908 case ' ': kbd.key = ' '; break; /* xterm */
909 case 'A': kbd.key = KBD_UP; break; /* kcuu1 vt100 */
910 case 'B': kbd.key = KBD_DOWN; break; /* kcud1 vt100 */
911 case 'C': kbd.key = KBD_RIGHT; break; /* kcuf1 vt100 */
912 case 'D': kbd.key = KBD_LEFT; break; /* kcub1 vt100 */
913 case 'F': kbd.key = KBD_END; break; /* kend xterm */
914 case 'H': kbd.key = KBD_HOME; break; /* khome xterm */
915 case 'I': kbd.key = KBD_TAB; break; /* xterm */
916 case 'M': kbd.key = KBD_ENTER; break; /* kent vt100 */
917 /* FIXME: xterm generates ESC O 2 P for Shift-PF1 */
918 case 'P': kbd.key = KBD_F1; break; /* kf1 vt100 */
919 case 'Q': kbd.key = KBD_F2; break; /* kf2 vt100 */
920 case 'R': kbd.key = KBD_F3; break; /* kf3 vt100 */
921 case 'S': kbd.key = KBD_F4; break; /* kf4 vt100 */
922 case 'X': kbd.key = '='; break; /* xterm */
924 case 'j': case 'k': case 'l': case 'm': /* *+,- xterm */
925 case 'n': case 'o': case 'p': case 'q': /* ./01 xterm */
926 case 'r': case 's': case 't': case 'u': /* 2345 xterm */
927 case 'v': case 'w': case 'x': case 'y': /* 6789 xterm */
928 kbd.key = c - 'p' + '0'; break;
930 if (kbd.key != KBD_UNDEF)
931 copy_struct(&ev->info.keyboard, &kbd);
933 return 3; /* even if we didn't recognize it */
937 /** Initialize @a *ev to match the byte @a key received from the terminal.
938 * @a key must not be a value from enum term_event_special_key. */
939 static void
940 set_kbd_event(struct interlink_event *ev,
941 int key, term_event_modifier_T modifier)
943 switch (key) {
944 case ASCII_TAB:
945 key = KBD_TAB;
946 break;
947 #if defined(HAVE_SYS_CONSIO_H) || defined(HAVE_MACHINE_CONSOLE_H) /* BSD */
948 case ASCII_BS:
949 key = KBD_BS;
950 break;
951 case ASCII_DEL:
952 key = KBD_DEL;
953 break;
954 #else
955 case ASCII_BS:
956 case ASCII_DEL:
957 key = KBD_BS;
958 break;
959 #endif
960 case ASCII_LF:
961 case ASCII_CR:
962 key = KBD_ENTER;
963 break;
965 case ASCII_ESC:
966 key = KBD_ESC;
967 break;
969 default:
970 if (key < ' ') {
971 key += 'A' - 1;
972 modifier |= KBD_MOD_CTRL;
976 set_kbd_interlink_event(ev, key, modifier);
979 /** Timer callback for itrm.timer. As explained in install_timer(),
980 * this function must erase the expired timer ID from all variables. */
981 static void
982 kbd_timeout(struct itrm *itrm)
984 struct interlink_event ev;
985 int el;
987 itrm->timer = TIMER_ID_UNDEF;
988 /* The expired timer ID has now been erased. */
990 assertm(itrm->in.queue.len, "timeout on empty queue");
991 assert(!itrm->blocked); /* block_itrm should have killed itrm->timer */
992 if_assert_failed return;
994 if (can_read(itrm->in.std)) {
995 in_kbd(itrm);
996 return;
999 if (itrm->in.queue.len >= 2 && itrm->in.queue.data[0] == ASCII_ESC) {
1000 /* This is used for ESC [ and ESC O. */
1001 set_kbd_event(&ev, itrm->in.queue.data[1], KBD_MOD_ALT);
1002 el = 2;
1003 } else {
1004 set_kbd_event(&ev, itrm->in.queue.data[0], KBD_MOD_NONE);
1005 el = 1;
1007 itrm_queue_event(itrm, (char *) &ev, sizeof(ev));
1009 itrm->in.queue.len -= el;
1010 if (itrm->in.queue.len)
1011 memmove(itrm->in.queue.data, itrm->in.queue.data + el, itrm->in.queue.len);
1013 while (process_queue(itrm));
1016 /** Parse one event from itrm_in.queue and append to itrm_out.queue.
1017 * @pre On entry, @a *itrm must not be blocked.
1018 * @returns the number of bytes removed from itrm->in.queue; at least 0.
1019 * @post If this function leaves the queue not full, it also reenables
1020 * reading from itrm->in.std. (Because it does not add to the queue,
1021 * it never need disable reading.) */
1022 static int
1023 process_queue(struct itrm *itrm)
1025 struct interlink_event ev;
1026 int el = 0;
1028 if (!itrm->in.queue.len) goto return_without_event;
1029 assert(!itrm->blocked);
1030 if_assert_failed return 0; /* unlike goto, don't enable reading */
1032 set_kbd_interlink_event(&ev, KBD_UNDEF, KBD_MOD_NONE);
1034 #ifdef DEBUG_ITRM_QUEUE
1036 int i;
1038 /* Dump current queue in a readable form to stderr. */
1039 for (i = 0; i < itrm->in.queue.len; i++)
1040 if (itrm->in.queue.data[i] == ASCII_ESC)
1041 fprintf(stderr, "ESC ");
1042 else if (isprint(itrm->in.queue.data[i]) && !isspace(itrm->in.queue.data[i]))
1043 fprintf(stderr, "%c ", itrm->in.queue.data[i]);
1044 else
1045 fprintf(stderr, "0x%02x ", itrm->in.queue.data[i]);
1047 fprintf(stderr, "\n");
1048 fflush(stderr);
1050 #endif /* DEBUG_ITRM_QUEUE */
1052 /* el == -1 means itrm->in.queue appears to be the beginning of an
1053 * escape sequence but it is not yet complete. Set a timer;
1054 * if it times out, then assume it wasn't an escape sequence
1055 * after all.
1056 * el == 0 means this function has not yet figured out what the data
1057 * in itrm->in.queue is, but some possibilities remain.
1058 * One of them will be chosen before returning.
1059 * el > 0 means some bytes were successfully parsed from the beginning
1060 * of itrm->in.queue and should now be removed from there.
1061 * However, this does not always imply an event will be queued.
1064 /* ELinks should also recognize U+009B CONTROL SEQUENCE INTRODUCER
1065 * as meaning the same as ESC 0x5B, and U+008F SINGLE SHIFT THREE as
1066 * meaning the same as ESC 0x4F, but those cannot yet be implemented
1067 * because of bug 777: the UTF-8 decoder is run too late. */
1068 if (itrm->in.queue.data[0] == ASCII_ESC) {
1069 if (itrm->in.queue.len < 2) {
1070 el = -1;
1071 } else if (itrm->in.queue.data[1] == 0x5B /* CSI */) {
1072 el = decode_terminal_escape_sequence(itrm, &ev);
1073 } else if (itrm->in.queue.data[1] == 0x4F /* SS3 */) {
1074 el = decode_terminal_application_key(itrm, &ev);
1075 } else if (itrm->in.queue.data[1] == ASCII_ESC) {
1076 /* ESC ESC can be either Alt-Esc or the
1077 * beginning of e.g. ESC ESC 0x5B 0x41,
1078 * which we should parse as Esc Up. */
1079 if (itrm->in.queue.len < 3) {
1080 /* Need more data to figure it out. */
1081 el = -1;
1082 } else if (itrm->in.queue.data[2] == 0x5B
1083 || itrm->in.queue.data[2] == 0x4F) {
1084 /* The first ESC appears to be followed
1085 * by an escape sequence. Treat it as
1086 * a standalone Esc. */
1087 el = 1;
1088 set_kbd_event(&ev, itrm->in.queue.data[0],
1089 KBD_MOD_NONE);
1090 } else {
1091 /* The second ESC of ESC ESC is not the
1092 * beginning of any known escape sequence.
1093 * This must be Alt-Esc, then. */
1094 el = 2;
1095 set_kbd_event(&ev, itrm->in.queue.data[1],
1096 KBD_MOD_ALT);
1099 if (el == 0) { /* Begins with ESC, but none of the above */
1100 el = 2;
1101 set_kbd_event(&ev, itrm->in.queue.data[1],
1102 KBD_MOD_ALT);
1105 } else if (itrm->in.queue.data[0] == 0) {
1106 static const struct term_event_keyboard os2xtd[256] = {
1107 #include "terminal/key.inc"
1110 if (itrm->in.queue.len < 2)
1111 el = -1;
1112 else {
1113 el = 2;
1114 set_kbd_interlink_event(&ev,
1115 os2xtd[itrm->in.queue.data[1]].key,
1116 os2xtd[itrm->in.queue.data[1]].modifier);
1120 if (el == 0) {
1121 el = 1;
1122 set_kbd_event(&ev, itrm->in.queue.data[0], KBD_MOD_NONE);
1125 /* The call to decode_terminal_escape_sequence() might have changed the
1126 * keyboard event to a mouse event. */
1127 if (ev.ev == EVENT_MOUSE || ev.info.keyboard.key != KBD_UNDEF)
1128 itrm_queue_event(itrm, (char *) &ev, sizeof(ev));
1130 return_without_event:
1131 if (el == -1) {
1132 install_timer(&itrm->timer, ESC_TIMEOUT, (void (*)(void *)) kbd_timeout,
1133 itrm);
1134 return 0;
1135 } else {
1136 assertm(itrm->in.queue.len >= el, "event queue underflow");
1137 if_assert_failed { itrm->in.queue.len = el; }
1139 itrm->in.queue.len -= el;
1140 if (itrm->in.queue.len)
1141 memmove(itrm->in.queue.data, itrm->in.queue.data + el, itrm->in.queue.len);
1143 if (itrm->in.queue.len < ITRM_IN_QUEUE_SIZE)
1144 handle_itrm_stdin(itrm);
1146 return el;
1151 /** A select_handler_T read_func for itrm_in.std. This is called when
1152 * characters typed by the user arrive from the terminal. */
1153 static void
1154 in_kbd(struct itrm *itrm)
1156 int r;
1158 if (!can_read(itrm->in.std)) return;
1160 kill_timer(&itrm->timer);
1162 if (itrm->in.queue.len >= ITRM_IN_QUEUE_SIZE) {
1163 unhandle_itrm_stdin(itrm);
1164 while (process_queue(itrm));
1165 return;
1168 r = safe_read(itrm->in.std, itrm->in.queue.data + itrm->in.queue.len,
1169 ITRM_IN_QUEUE_SIZE - itrm->in.queue.len);
1170 if (r <= 0) {
1171 free_itrm(itrm);
1172 return;
1175 itrm->in.queue.len += r;
1176 if (itrm->in.queue.len > ITRM_IN_QUEUE_SIZE) {
1177 ERROR(gettext("Too many bytes read from the itrm!"));
1178 itrm->in.queue.len = ITRM_IN_QUEUE_SIZE;
1181 while (process_queue(itrm));
1184 /** Enable reading from itrm_in.std. ELinks will read any available
1185 * bytes from the tty into itrm->in.queue and then parse them.
1186 * Reading should be enabled whenever itrm->in.queue is not full and
1187 * itrm->blocked is 0. */
1188 static void
1189 handle_itrm_stdin(struct itrm *itrm)
1191 set_handlers(itrm->in.std, (select_handler_T) in_kbd, NULL,
1192 (select_handler_T) free_itrm, itrm);
1195 /** Disable reading from itrm_in.std. Reading should be disabled
1196 * whenever itrm->in.queue is full (there is no room for the data)
1197 * or itrm->blocked is 1 (other processes may read the data). */
1198 static void
1199 unhandle_itrm_stdin(struct itrm *itrm)
1201 set_handlers(itrm->in.std, (select_handler_T) NULL, NULL,
1202 (select_handler_T) free_itrm, itrm);