1 /** Support for keyboard interface
4 * @todo TODO: move stuff from here to itrm.{c,h} and mouse.{c,h}
21 #define HPUX_PIPE (len > PIPE_BUF || errno != EAGAIN)
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
*);
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
;
64 return ditrm
&& ditrm
->blocked
;
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. */
80 itrm_queue_write(struct itrm
*itrm
)
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
);
90 if (written
< 0) free_itrm(itrm
); /* write error */
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
),
100 get_handler(itrm
->out
.sock
, SELECT_HANDLER_ERROR
),
101 get_handler(itrm
->out
.sock
, SELECT_HANDLER_DATA
));
103 assert(itrm
->out
.queue
.len
> 0);
104 memmove(itrm
->out
.queue
.data
, itrm
->out
.queue
.data
+ written
, itrm
->out
.queue
.len
);
110 itrm_queue_event(struct itrm
*itrm
, unsigned char *data
, int len
)
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
);
126 unsigned char *c
= mem_realloc(itrm
->out
.queue
.data
,
127 itrm
->out
.queue
.len
+ left
);
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
);
148 struct interlink_event ev
;
151 /* This is called because of a signal, and there may be input
152 * pending from the terminal, so do not reset
153 * ditrm->bracketed_pasting. */
154 set_kbd_interlink_event(&ev
, KBD_CTRL_C
, KBD_MOD_NONE
);
155 itrm_queue_event(ditrm
, (unsigned char *) &ev
, sizeof(ev
));
158 #define write_sequence(fd, seq) \
159 hard_write(fd, seq, sizeof(seq) - 1)
162 #define INIT_TERMINAL_SEQ "\033)0\0337" /**< Special Character and Line Drawing Set, Save Cursor */
163 #define INIT_ALT_SCREEN_SEQ "\033[?47h" /**< Use Alternate Screen Buffer */
164 #define INIT_BRACKETED_PASTE_SEQ "\033[?2004h" /**< Enable XTerm bracketed paste mode */
167 send_init_sequence(int h
, int altscreen
)
169 write_sequence(h
, INIT_TERMINAL_SEQ
);
171 /* If alternate screen is supported switch to it. */
173 write_sequence(h
, INIT_ALT_SCREEN_SEQ
);
176 send_mouse_init_sequence(h
);
178 write_sequence(h
, INIT_BRACKETED_PASTE_SEQ
);
181 #define DONE_CLS_SEQ "\033[2J" /**< Erase in Display, Clear All */
182 #define DONE_TERMINAL_SEQ "\0338\r \b" /**< Restore Cursor (DECRC) + ??? */
183 #define DONE_ALT_SCREEN_SEQ "\033[?47l" /**< Use Normal Screen Buffer */
184 #define DONE_BRACKETED_PASTE_SEQ "\033[?2004l" /**< Disable XTerm bracketed paste mode */
187 send_done_sequence(int h
, int altscreen
)
189 write_sequence(h
, DONE_BRACKETED_PASTE_SEQ
);
190 write_sequence(h
, DONE_CLS_SEQ
);
193 send_mouse_done_sequence(h
);
196 /* Switch from alternate screen. */
198 write_sequence(h
, DONE_ALT_SCREEN_SEQ
);
201 write_sequence(h
, DONE_TERMINAL_SEQ
);
205 #undef write_sequence
208 resize_terminal(void)
210 struct interlink_event ev
;
213 get_terminal_size(ditrm
->out
.std
, &width
, &height
);
214 set_resize_interlink_event(&ev
, width
, height
);
215 itrm_queue_event(ditrm
, (char *) &ev
, sizeof(ev
));
219 get_terminal_name(unsigned char name
[MAX_TERM_LEN
])
221 unsigned char *term
= getenv("TERM");
224 memset(name
, 0, MAX_TERM_LEN
);
228 for (i
= 0; term
[i
] != 0 && i
< MAX_TERM_LEN
- 1; i
++)
229 name
[i
] = isident(term
[i
]) ? term
[i
] : '-';
234 setraw(struct itrm
*itrm
, int save_orig
)
239 memset(&t
, 0, sizeof(t
));
240 if (tcgetattr(itrm
->in
.ctl
, &t
)) return -1;
242 if (save_orig
) copy_struct(&itrm
->t
, &t
);
244 #ifdef _POSIX_VDISABLE
245 vdisable
= _POSIX_VDISABLE
;
246 #elif defined(HAVE_FPATHCONF)
247 vdisable
= fpathconf(itrm
->in
.ctl
, _PC_VDISABLE
);
251 /* Is VERASE defined on Windows? */
252 if (vdisable
!= -1 && t
.c_cc
[VERASE
] == vdisable
)
255 itrm
->verase
= (unsigned char) t
.c_cc
[VERASE
];
260 elinks_cfmakeraw(&t
);
266 if (tcsetattr(itrm
->in
.ctl
, TCSANOW
, &t
)) return -1;
271 /** Construct the struct itrm of this process, make ::ditrm point to it,
272 * set up select() handlers, and send the initial interlink packet.
274 * The first five parameters are file descriptors that this function
275 * saves in submembers of struct itrm, and for which this function may
276 * set select() handlers. Please see the definitions of struct
277 * itrm_in and struct itrm_out for further explanations.
279 * @param std_in itrm_in.std: read tty device (or pipe)
280 * @param std_out itrm_out.std: write tty device (or pipe)
281 * @param sock_in itrm_in.sock
282 * - If master: == @a std_out (masterhood flag)
283 * - If slave: read socket from master
284 * @param sock_out itrm_out.sock
285 * - If master: write pipe to same process
286 * - If slave: write socket to master
287 * @param ctl_in itrm_in.ctl: control tty device
289 * The remaining three parameters control the initial interlink packet.
291 * @param init_string A string to be passed to the master process. Need
292 * not be null-terminated. If @a remote == 0, this is
293 * a URI. Otherwise, this is a remote command.
294 * @param init_len The length of init_string, in bytes.
295 * @param remote = 0 if asking the master to start a new session
296 * and display it via this process. Otherwise,
297 * enum ::remote_session_flags. */
299 handle_trm(int std_in
, int std_out
, int sock_in
, int sock_out
, int ctl_in
,
300 void *init_string
, int init_len
, int remote
)
303 struct terminal_info info
;
304 struct interlink_event_size
*size
= &info
.event
.info
.size
;
307 memset(&info
, 0, sizeof(info
));
309 get_terminal_size(ctl_in
, &size
->width
, &size
->height
);
310 info
.event
.ev
= EVENT_INIT
;
311 info
.system_env
= get_system_env();
312 info
.length
= init_len
;
315 info
.session_info
= remote
;
316 info
.magic
= INTERLINK_REMOTE_MAGIC
;
318 info
.session_info
= get_cmd_opt_int("base-session");
319 info
.magic
= INTERLINK_NORMAL_MAGIC
;
322 itrm
= mem_calloc(1, sizeof(*itrm
));
325 itrm
->in
.queue
.data
= mem_calloc(1, ITRM_IN_QUEUE_SIZE
);
326 if (!itrm
->in
.queue
.data
) {
332 itrm
->in
.std
= std_in
;
333 itrm
->out
.std
= std_out
;
334 itrm
->in
.sock
= sock_in
;
335 itrm
->out
.sock
= sock_out
;
336 itrm
->in
.ctl
= ctl_in
;
337 itrm
->timer
= TIMER_ID_UNDEF
;
338 itrm
->remote
= !!remote
;
340 /* If the master does not tell which charset it's using in
341 * this terminal, assume it's some ISO 8859. Because that's
342 * what older versions of ELinks did. */
343 itrm
->title_codepage
= get_cp_index("ISO-8859-1");
345 /* FIXME: Combination altscreen + xwin does not work as it should,
346 * mouse clicks are reportedly partially ignored. */
347 if (info
.system_env
& (ENV_SCREEN
| ENV_XWIN
))
351 if (ctl_in
>= 0) setraw(itrm
, 1);
352 send_init_sequence(std_out
, itrm
->altscreen
);
353 handle_terminal_resize(ctl_in
, resize_terminal
);
357 handle_itrm_stdin(itrm
);
359 /* elinks -remote may not have a valid stdin if not run from a tty (bug 938) */
360 if (std_in
>= 0) handle_itrm_stdin(itrm
);
363 if (sock_in
!= std_out
)
364 set_handlers(sock_in
, (select_handler_T
) in_sock
,
365 NULL
, (select_handler_T
) free_itrm
, itrm
);
367 get_terminal_name(info
.name
);
371 memcpy(info
.cwd
, ts
, int_min(strlen(ts
), MAX_CWD_LEN
));
375 itrm_queue_event(itrm
, (char *) &info
, TERMINAL_INFO_SIZE
);
376 itrm_queue_event(itrm
, (char *) init_string
, init_len
);
380 /** A select_handler_T read_func and error_func for the pipe (long) @a h.
381 * This is called when the subprocess started on the terminal of this
382 * ELinks process exits. ELinks then resumes using the terminal. */
384 unblock_itrm_x(void *h
)
390 /* Note: External editor support depends on this resize event. */
398 if (!ditrm
) return -1;
400 if (ditrm
->in
.ctl
>= 0 && setraw(ditrm
, 0)) return -1;
402 send_init_sequence(ditrm
->out
.std
, ditrm
->altscreen
);
404 handle_itrm_stdin(ditrm
);
405 resume_mouse(ditrm
->mouse_h
);
407 handle_terminal_resize(ditrm
->in
.ctl
, resize_terminal
);
421 kill_timer(&ditrm
->timer
);
422 ditrm
->in
.queue
.len
= 0;
423 unhandle_terminal_resize(ditrm
->in
.ctl
);
424 send_done_sequence(ditrm
->out
.std
, ditrm
->altscreen
);
425 tcsetattr(ditrm
->in
.ctl
, TCSANOW
, &ditrm
->t
);
426 unhandle_itrm_stdin(ditrm
);
427 suspend_mouse(ditrm
->mouse_h
);
432 free_itrm(struct itrm
*itrm
)
437 if (itrm
->orig_title
&& *itrm
->orig_title
) {
438 set_window_title(itrm
->orig_title
, itrm
->title_codepage
);
440 } else if (itrm
->touched_title
) {
441 /* Set the window title to the value of $TERM if X11
442 * wasn't compiled in. Should hopefully make at least
443 * half the users happy. (debian bug #312955) */
444 unsigned char title
[MAX_TERM_LEN
];
446 get_terminal_name(title
);
448 set_window_title(title
,
449 get_cp_index("US-ASCII"));
453 unhandle_terminal_resize(itrm
->in
.ctl
);
457 send_done_sequence(itrm
->out
.std
, itrm
->altscreen
);
458 tcsetattr(itrm
->in
.ctl
, TCSANOW
, &itrm
->t
);
461 mem_free_set(&itrm
->orig_title
, NULL
);
463 /* elinks -remote may not have a valid stdin if not run from a tty (bug 938) */
464 if (!itrm
->remote
|| itrm
->in
.std
>= 0) clear_handlers(itrm
->in
.std
);
465 clear_handlers(itrm
->in
.sock
);
466 clear_handlers(itrm
->out
.std
);
467 clear_handlers(itrm
->out
.sock
);
469 kill_timer(&itrm
->timer
);
471 if (itrm
== ditrm
) ditrm
= NULL
;
472 mem_free_if(itrm
->out
.queue
.data
);
473 mem_free_if(itrm
->in
.queue
.data
);
477 /** Resize terminal to dimensions specified by @a text string.
478 * @a text should look like "width,height,old-width,old-height"
479 * where width and height are integers. */
481 resize_terminal_from_str(unsigned char *text
)
483 enum { NEW_WIDTH
= 0, NEW_HEIGHT
, OLD_WIDTH
, OLD_HEIGHT
, NUMBERS
} i
;
484 int numbers
[NUMBERS
];
486 assert(text
&& *text
);
487 if_assert_failed
return;
489 for (i
= 0; i
< NUMBERS
; i
++) {
490 unsigned char *p
= strchr(text
, ',');
495 } else if (i
< OLD_HEIGHT
) {
499 numbers
[i
] = atoi(text
);
504 resize_window(numbers
[NEW_WIDTH
], numbers
[NEW_HEIGHT
],
505 numbers
[OLD_WIDTH
], numbers
[OLD_HEIGHT
]);
510 dispatch_special(unsigned char *text
)
518 if (!ditrm
->orig_title
)
519 ditrm
->orig_title
= get_window_title();
520 ditrm
->touched_title
= 1;
522 /* TODO: Is it really possible to get here with
523 * ditrm == NULL, and which charset would then
524 * be most appropriate? */
525 set_window_title(text
+ 1,
526 ditrm
? ditrm
->title_codepage
527 : get_cp_index("US-ASCII"));
530 if (ditrm
&& ditrm
->remote
)
533 resize_terminal_from_str(text
+ 1);
535 case TERM_FN_TITLE_CODEPAGE
:
537 int cp
= get_cp_index(text
+ 1);
539 /* If the master sends the name of an
540 * unrecognized charset, assume only
541 * that it's ASCII compatible. */
543 cp
= get_cp_index("US-ASCII");
544 ditrm
->title_codepage
= cp
;
551 safe_hard_write(int fd
, unsigned char *buf
, int len
)
553 if (is_blocked()) return;
556 hard_write(fd
, buf
, len
);
560 /** A select_handler_T read_func for itrm_in.sock. A slave process
561 * calls this when the master sends it data to be displayed. The
562 * master process never calls this. */
564 in_sock(struct itrm
*itrm
)
567 struct string
delete;
569 int fg
; /* enum term_exec */
570 ssize_t bytes_read
, i
, p
;
571 unsigned char buf
[ITRM_OUT_QUEUE_SIZE
];
573 bytes_read
= safe_read(itrm
->in
.sock
, buf
, ITRM_OUT_QUEUE_SIZE
);
574 if (bytes_read
<= 0) goto free_and_return
;
577 for (i
= 0; i
< bytes_read
; i
++)
581 safe_hard_write(itrm
->out
.std
, buf
, bytes_read
);
585 if (i
) safe_hard_write(itrm
->out
.std
, buf
, i
);
588 assert(ITRM_OUT_QUEUE_SIZE
- i
> 0);
589 memmove(buf
, buf
+ i
, ITRM_OUT_QUEUE_SIZE
- i
);
596 if (p < bytes_read) \
598 else if ((hard_read(itrm->in.sock, &cc, 1)) <= 0) \
599 goto free_and_return; \
605 if (!init_string(&path
)) goto free_and_return
;
610 add_char_to_string(&path
, ch
);
613 if (!init_string(&delete)) {
615 goto free_and_return
;
621 add_char_to_string(&delete, ch
);
627 dispatch_special(delete.source
);
631 unsigned char *param
;
632 int path_len
, del_len
, param_len
;
634 /* TODO: Should this be changed to allow TERM_EXEC_NEWWIN
635 * in a blocked terminal? There is similar code in
636 * exec_on_terminal(). --KON, 2007 */
637 if (is_blocked() && fg
!= TERM_EXEC_BG
) {
638 if (*delete.source
) unlink(delete.source
);
642 path_len
= path
.length
;
643 del_len
= delete.length
;
644 param_len
= path_len
+ del_len
+ 3;
646 param
= mem_alloc(param_len
);
647 if (!param
) goto nasty_thing
;
650 memcpy(param
+ 1, path
.source
, path_len
+ 1);
651 memcpy(param
+ 1 + path_len
+ 1, delete.source
, del_len
+ 1);
653 if (fg
== TERM_EXEC_FG
) block_itrm();
655 blockh
= start_thread((void (*)(void *, int)) exec_thread
,
660 if (fg
== TERM_EXEC_FG
)
666 if (fg
== TERM_EXEC_FG
) {
667 set_handlers(blockh
, (select_handler_T
) unblock_itrm_x
,
668 NULL
, (select_handler_T
) unblock_itrm_x
,
669 (void *) (long) blockh
);
672 set_handlers(blockh
, close_handle
, NULL
, close_handle
,
673 (void *) (long) blockh
);
679 done_string(&delete);
680 assert(ITRM_OUT_QUEUE_SIZE
- p
> 0);
681 memmove(buf
, buf
+ p
, ITRM_OUT_QUEUE_SIZE
- p
);
691 /** Parse an ECMA-48 control sequence that was received from a
692 * terminal. Extract the Final Byte (if there are no Intermediate
693 * Bytes) and the value of the first parameter (if it is an integer).
695 * This function assumes the control sequence begins with a CSI -
696 * CONTROL SEQUENCE INTRODUCER encoded as ESC [. (ECMA-48 also allows
697 * 0x9B as a single-byte CSI, but we don't support that here.)
700 * - -1 if the control sequence is not yet complete; the caller sets a timer.
701 * - 0 if the control sequence does not comply with ECMA-48.
702 * - The length of the control sequence otherwise. */
704 get_esc_code(unsigned char *str
, int len
, unsigned char *final_byte
,
705 int *first_param_value
)
707 const int parameter_pos
= 2;
708 int intermediate_pos
;
713 *first_param_value
= 0;
715 /* Parameter Bytes */
717 while (pos
< len
&& str
[pos
] >= 0x30 && str
[pos
] <= 0x3F)
720 /* Intermediate Bytes */
721 intermediate_pos
= pos
;
722 while (pos
< len
&& str
[pos
] >= 0x20 && str
[pos
] <= 0x2F)
729 if (!(str
[pos
] >= 0x40 && str
[pos
] <= 0x7E))
732 /* The control sequence seems OK. If the first Parameter
733 * Byte indicates that the parameter string is formatted
734 * as specified in clause 5.4.2 of ECMA-48, and the first
735 * parameter is an integer, then compute its value.
736 * (We need not check @len here because the loop cannot get
737 * past the Final Byte.) */
738 for (pos
= parameter_pos
; str
[pos
] >= 0x30 && str
[pos
] <= 0x39; ++pos
)
739 *first_param_value
= *first_param_value
* 10 + str
[pos
] - 0x30;
740 /* If the first parameter contains an embedded separator, then
741 * the value is not an integer, so discard what we computed. */
742 if (str
[pos
] == 0x3A)
743 *first_param_value
= 0;
745 /* The meaning of the Final Byte depends on the Intermediate
746 * Bytes. Because we don't currently need to recognize any
747 * control sequences that use Intermediate Bytes, we just
748 * discard the Final Byte if there are any Intermediate
750 if (intermediate_pos
== final_pos
)
751 *final_byte
= str
[final_pos
];
753 return final_pos
+ 1;
756 /* Define it to dump queue content in a readable form,
757 * it may help to determine terminal sequences, and see what goes on. --Zas */
758 /* #define DEBUG_ITRM_QUEUE */
760 #ifdef DEBUG_ITRM_QUEUE
762 #include <ctype.h> /* isprint() isspace() */
765 /** Decode a control sequence that begins with CSI (CONTROL SEQUENCE
766 * INTRODUCER) encoded as ESC [, and set @a *ev accordingly.
767 * (ECMA-48 also allows 0x9B as a single-byte CSI, but we don't
768 * support that here.)
771 * - -1 if the control sequence is not yet complete; the caller sets a timer.
772 * - 0 if the control sequence should be parsed by some other function.
773 * - The length of the control sequence otherwise.
774 * Returning >0 does not imply this function has altered @a *ev. */
776 decode_terminal_escape_sequence(struct itrm
*itrm
, struct interlink_event
*ev
)
778 struct term_event_keyboard kbd
= { KBD_UNDEF
, KBD_MOD_NONE
};
783 if (itrm
->in
.queue
.len
< 3) return -1;
785 if (itrm
->in
.queue
.data
[2] == '[') {
786 /* The terminfo entry for linux has "kf1=\E[[A", etc.
787 * These are not control sequences compliant with
788 * clause 5.4 of ECMA-48. (According to ECMA-48,
789 * "\E[[" is SRS - START REVERSED STRING.) */
790 if (itrm
->in
.queue
.len
>= 4
791 && itrm
->in
.queue
.data
[3] >= 'A'
792 && itrm
->in
.queue
.data
[3] <= 'L') {
793 kbd
.key
= number_to_kbd_fkey(itrm
->in
.queue
.data
[3] - 'A' + 1);
794 set_kbd_interlink_event(ev
, kbd
.key
, kbd
.modifier
);
801 el
= get_esc_code(itrm
->in
.queue
.data
, itrm
->in
.queue
.len
, &c
, &v
);
803 /* If the control sequence is incomplete but itrm->in.queue
804 * is already full, then we must not wait for more input:
805 * kbd_timeout might call in_kbd and thus process_input
806 * and come right back here. Better just reject the whole
807 * thing and let the initial CSI be handled as Alt-[. */
808 if (itrm
->in
.queue
.len
== ITRM_IN_QUEUE_SIZE
)
813 #ifdef DEBUG_ITRM_QUEUE
814 fprintf(stderr
, "esc code: %c v=%d c=%c el=%d\n", itrm
->in
.queue
.data
[1], v
, c
, el
);
818 /* The following information should be listed for each escape
819 * sequence recognized here:
821 * 1. Which control function ECMA-48 assigns to the sequence.
822 * Put parentheses around this if the control function
823 * seems unrelated to how ELinks actually treats the
824 * sequence. Write "private" if it is a control sequence
825 * reserved for private or experimental use in ECMA-48.
826 * (Those have a Final Byte in the range 0x70 to 0x7F,
827 * optionally preceded by a single Intermediate Byte 0x20.)
829 * 2. The capname used by Terminfo, if any. These should help
830 * when ELinks is eventually changed to read escape
831 * sequences from Terminfo (bug 96).
833 * 3. The $TERM identifier of some terminal that generates
834 * this escape sequence with the meaning expected by
835 * ELinks. Escape sequences with no known terminal may end
836 * up being removed from ELinks when bug 96 is fixed.
837 */ /* ECMA-48 Terminfo $TERM */
838 switch (c
) { /* ------- -------- ----- */
839 case 'A': kbd
.key
= KBD_UP
; break; /* CUU kcuu1 vt200 */
840 case 'B': kbd
.key
= KBD_DOWN
; break; /* CUD kcud1 vt200 */
841 case 'C': kbd
.key
= KBD_RIGHT
; break; /* CUF kcuf1 vt200 */
842 case 'D': kbd
.key
= KBD_LEFT
; break; /* CUB kcub1 vt200 */
843 case 'F': /* (CPL) kend cons25 */
844 case 'e': kbd
.key
= KBD_END
; break; /* (VPR) kend */
845 case 'H': kbd
.key
= KBD_HOME
; break; /* CUP khome cons25 */
846 case 'I': kbd
.key
= KBD_PAGE_UP
; break; /* (CHT) kpp cons25 */
847 case 'G': kbd
.key
= KBD_PAGE_DOWN
; break; /* (CHA) knp cons25 */
848 case 'L': kbd
.key
= KBD_INS
; break; /* (IL) kich1 cons25 */
849 /* Free BSD (TERM=cons25 etc.) */
850 /* case 'M': kbd.key = KBD_F1; break;*/ /* (DL) kf1 cons25 */
851 case 'N': kbd
.key
= KBD_F2
; break; /* (EF) kf2 cons25 */
852 case 'O': kbd
.key
= KBD_F3
; break; /* (EA) kf3 cons25 */
853 case 'P': kbd
.key
= KBD_F4
; break; /* (DCH) kf4 cons25 */
854 case 'Q': kbd
.key
= KBD_F5
; break; /* (SEE) kf5 cons25 */
855 /* case 'R': kbd.key = KBD_F6; break;*/ /* (CPR) kf6 cons25 */
856 case 'S': kbd
.key
= KBD_F7
; break; /* (SU) kf7 cons25 */
857 case 'T': kbd
.key
= KBD_F8
; break; /* (SD) kf8 cons25 */
858 case 'U': kbd
.key
= KBD_F9
; break; /* (NP) kf9 cons25 */
859 case 'V': kbd
.key
= KBD_F10
; break; /* (PP) kf10 cons25 */
860 case 'W': kbd
.key
= KBD_F11
; break; /* (CTC) kf11 cons25 */
861 case 'X': kbd
.key
= KBD_F12
; break; /* (ECH) kf12 cons25 */
863 case 'Z': /* CBT kcbt cons25 */
864 kbd
.key
= KBD_TAB
; kbd
.modifier
= KBD_MOD_SHIFT
; break;
866 case 'z': switch (v
) { /* private */
867 case 247: kbd
.key
= KBD_INS
; break; /* kich1 */
868 case 214: kbd
.key
= KBD_HOME
; break; /* khome sun */
869 case 220: kbd
.key
= KBD_END
; break; /* kend sun */
870 case 216: kbd
.key
= KBD_PAGE_UP
; break; /* kpp sun */
871 case 222: kbd
.key
= KBD_PAGE_DOWN
; break; /* knp sun */
872 case 249: kbd
.key
= KBD_DEL
; break; /* kdch1 */
875 case '~': switch (v
) { /* private */
876 case 1: kbd
.key
= KBD_HOME
; break; /* khome linux */
877 case 2: kbd
.key
= KBD_INS
; break; /* kich1 linux */
878 case 3: kbd
.key
= KBD_DEL
; break; /* kdch1 linux */
879 case 4: kbd
.key
= KBD_END
; break; /* kend linux */
880 case 5: kbd
.key
= KBD_PAGE_UP
; break; /* kpp linux */
881 case 6: kbd
.key
= KBD_PAGE_DOWN
; break; /* knp linux */
882 case 7: kbd
.key
= KBD_HOME
; break; /* khome rxvt */
883 case 8: kbd
.key
= KBD_END
; break; /* kend rxvt */
885 case 11: kbd
.key
= KBD_F1
; break; /* kf1 rxvt */
886 case 12: kbd
.key
= KBD_F2
; break; /* kf2 rxvt */
887 case 13: kbd
.key
= KBD_F3
; break; /* kf3 rxvt */
888 case 14: kbd
.key
= KBD_F4
; break; /* kf4 rxvt */
889 case 15: kbd
.key
= KBD_F5
; break; /* kf5 rxvt */
891 case 17: kbd
.key
= KBD_F6
; break; /* kf6 vt200 */
892 case 18: kbd
.key
= KBD_F7
; break; /* kf7 vt200 */
893 case 19: kbd
.key
= KBD_F8
; break; /* kf8 vt200 */
894 case 20: kbd
.key
= KBD_F9
; break; /* kf9 vt200 */
895 case 21: kbd
.key
= KBD_F10
; break; /* kf10 vt200 */
897 case 23: kbd
.key
= KBD_F11
; break; /* kf11 vt200 */
898 case 24: kbd
.key
= KBD_F12
; break; /* kf12 vt200 */
900 /* Give preference to F11 and F12 over shifted F1 and F2. */
902 case 23: kbd.key = KBD_F1; kbd.modifier = KBD_MOD_SHIFT; break;
903 case 24: kbd.key = KBD_F2; kbd.modifier = KBD_MOD_SHIFT; break;
906 case 25: kbd
.key
= KBD_F3
; kbd
.modifier
= KBD_MOD_SHIFT
; break;
907 case 26: kbd
.key
= KBD_F4
; kbd
.modifier
= KBD_MOD_SHIFT
; break;
909 case 28: kbd
.key
= KBD_F5
; kbd
.modifier
= KBD_MOD_SHIFT
; break;
910 case 29: kbd
.key
= KBD_F6
; kbd
.modifier
= KBD_MOD_SHIFT
; break;
912 case 31: kbd
.key
= KBD_F7
; kbd
.modifier
= KBD_MOD_SHIFT
; break;
913 case 32: kbd
.key
= KBD_F8
; kbd
.modifier
= KBD_MOD_SHIFT
; break;
914 case 33: kbd
.key
= KBD_F9
; kbd
.modifier
= KBD_MOD_SHIFT
; break;
915 case 34: kbd
.key
= KBD_F10
; kbd
.modifier
= KBD_MOD_SHIFT
; break;
917 case 200: itrm
->bracketed_pasting
= 1; break; /* xterm */
918 case 201: itrm
->bracketed_pasting
= 0; break; /* xterm */
922 case 'R': resize_terminal(); break; /* CPR u6 */
923 case 'M': /* (DL) kmous xterm */
925 el
= decode_terminal_mouse_escape_sequence(itrm
, ev
, el
, v
);
926 #endif /* CONFIG_MOUSE */
930 /* KBD_UNDEF here means it was unrecognized or a mouse event. */
931 if (kbd
.key
!= KBD_UNDEF
)
932 set_kbd_interlink_event(ev
, kbd
.key
, kbd
.modifier
);
937 /** Decode an escape sequence that begins with SS3 (SINGLE SHIFT 3).
938 * These are used for application cursor keys and the application keypad.
940 * - -1 if the escape sequence is not yet complete; the caller sets a timer.
941 * - 0 if the escape sequence should be parsed by some other function.
942 * - The length of the escape sequence otherwise.
943 * Returning >0 does not imply this function has altered @a *ev. */
945 decode_terminal_application_key(struct itrm
*itrm
, struct interlink_event
*ev
)
948 struct interlink_event_keyboard kbd
= { KBD_UNDEF
, KBD_MOD_NONE
};
950 assert(itrm
->in
.queue
.len
>= 2);
951 assert(itrm
->in
.queue
.data
[0] == ASCII_ESC
);
952 assert(itrm
->in
.queue
.data
[1] == 0x4F); /* == 'O', incidentally */
953 if_assert_failed
return 0;
955 if (itrm
->in
.queue
.len
< 3) return -1;
956 /* According to ECMA-35 section 8.4, a single (possibly multibyte)
957 * character follows the SS3. We now assume the code identifies
958 * GL as the single-shift area and the designated set has 94
960 c
= itrm
->in
.queue
.data
[2];
961 if (c
< 0x21 || c
> 0x7E) return 0;
963 switch (c
) { /* Terminfo $TERM */
964 case ' ': kbd
.key
= ' '; break; /* xterm */
965 case 'A': kbd
.key
= KBD_UP
; break; /* kcuu1 vt100 */
966 case 'B': kbd
.key
= KBD_DOWN
; break; /* kcud1 vt100 */
967 case 'C': kbd
.key
= KBD_RIGHT
; break; /* kcuf1 vt100 */
968 case 'D': kbd
.key
= KBD_LEFT
; break; /* kcub1 vt100 */
969 case 'F': kbd
.key
= KBD_END
; break; /* kend xterm */
970 case 'H': kbd
.key
= KBD_HOME
; break; /* khome xterm */
971 case 'I': kbd
.key
= KBD_TAB
; break; /* xterm */
972 case 'M': kbd
.key
= KBD_ENTER
; break; /* kent vt100 */
973 /* FIXME: xterm generates ESC O 2 P for Shift-PF1 */
974 case 'P': kbd
.key
= KBD_F1
; break; /* kf1 vt100 */
975 case 'Q': kbd
.key
= KBD_F2
; break; /* kf2 vt100 */
976 case 'R': kbd
.key
= KBD_F3
; break; /* kf3 vt100 */
977 case 'S': kbd
.key
= KBD_F4
; break; /* kf4 vt100 */
978 case 'X': kbd
.key
= '='; break; /* xterm */
980 case 'j': case 'k': case 'l': case 'm': /* *+,- xterm */
981 case 'n': case 'o': case 'p': case 'q': /* ./01 xterm */
982 case 'r': case 's': case 't': case 'u': /* 2345 xterm */
983 case 'v': case 'w': case 'x': case 'y': /* 6789 xterm */
984 kbd
.key
= c
- 'p' + '0'; break;
986 if (kbd
.key
!= KBD_UNDEF
)
987 copy_struct(&ev
->info
.keyboard
, &kbd
);
989 return 3; /* even if we didn't recognize it */
993 /** Initialize @a *ev to match the byte @a key received from the terminal.
994 * @a key must not be a value from enum term_event_special_key. */
996 set_kbd_event(const struct itrm
*itrm
, struct interlink_event
*ev
,
997 int key
, term_event_modifier_T modifier
)
999 if (key
== itrm
->verase
)
1005 case ASCII_DEL
: /* often overridden by itrm->verase above */
1017 case ASCII_BS
: /* often overridden by itrm->verase above */
1021 modifier
|= KBD_MOD_CTRL
;
1025 set_kbd_interlink_event(ev
, key
, modifier
);
1028 /** Timer callback for itrm.timer. As explained in install_timer(),
1029 * this function must erase the expired timer ID from all variables. */
1031 kbd_timeout(struct itrm
*itrm
)
1033 struct interlink_event ev
;
1036 itrm
->timer
= TIMER_ID_UNDEF
;
1037 /* The expired timer ID has now been erased. */
1039 assertm(itrm
->in
.queue
.len
, "timeout on empty queue");
1040 assert(!itrm
->blocked
); /* block_itrm should have killed itrm->timer */
1041 if_assert_failed
return;
1043 if (can_read(itrm
->in
.std
)) {
1048 if (itrm
->in
.queue
.len
>= 2 && itrm
->in
.queue
.data
[0] == ASCII_ESC
) {
1049 /* This is used for ESC [ and ESC O. */
1050 set_kbd_event(itrm
, &ev
, itrm
->in
.queue
.data
[1], KBD_MOD_ALT
);
1053 set_kbd_event(itrm
, &ev
, itrm
->in
.queue
.data
[0], KBD_MOD_NONE
);
1056 itrm
->bracketed_pasting
= 0;
1057 itrm_queue_event(itrm
, (char *) &ev
, sizeof(ev
));
1059 itrm
->in
.queue
.len
-= el
;
1060 if (itrm
->in
.queue
.len
)
1061 memmove(itrm
->in
.queue
.data
, itrm
->in
.queue
.data
+ el
, itrm
->in
.queue
.len
);
1063 while (process_queue(itrm
));
1066 /** Parse one event from itrm_in.queue and append to itrm_out.queue.
1067 * @pre On entry, @a *itrm must not be blocked.
1068 * @returns the number of bytes removed from itrm->in.queue; at least 0.
1069 * @post If this function leaves the queue not full, it also reenables
1070 * reading from itrm->in.std. (Because it does not add to the queue,
1071 * it never need disable reading.) */
1073 process_queue(struct itrm
*itrm
)
1075 struct interlink_event ev
;
1078 if (!itrm
->in
.queue
.len
) goto return_without_event
;
1079 assert(!itrm
->blocked
);
1080 if_assert_failed
return 0; /* unlike goto, don't enable reading */
1082 set_kbd_interlink_event(&ev
, KBD_UNDEF
, KBD_MOD_NONE
);
1084 #ifdef DEBUG_ITRM_QUEUE
1088 /* Dump current queue in a readable form to stderr. */
1089 for (i
= 0; i
< itrm
->in
.queue
.len
; i
++)
1090 if (itrm
->in
.queue
.data
[i
] == ASCII_ESC
)
1091 fprintf(stderr
, "ESC ");
1092 else if (isprint(itrm
->in
.queue
.data
[i
]) && !isspace(itrm
->in
.queue
.data
[i
]))
1093 fprintf(stderr
, "%c ", itrm
->in
.queue
.data
[i
]);
1095 fprintf(stderr
, "0x%02x ", itrm
->in
.queue
.data
[i
]);
1097 fprintf(stderr
, "\n");
1100 #endif /* DEBUG_ITRM_QUEUE */
1102 /* el == -1 means itrm->in.queue appears to be the beginning of an
1103 * escape sequence but it is not yet complete. Set a timer;
1104 * if it times out, then assume it wasn't an escape sequence
1106 * el == 0 means this function has not yet figured out what the data
1107 * in itrm->in.queue is, but some possibilities remain.
1108 * One of them will be chosen before returning.
1109 * el > 0 means some bytes were successfully parsed from the beginning
1110 * of itrm->in.queue and should now be removed from there.
1111 * However, this does not always imply an event will be queued.
1114 /* ELinks should also recognize U+009B CONTROL SEQUENCE INTRODUCER
1115 * as meaning the same as ESC 0x5B, and U+008F SINGLE SHIFT THREE as
1116 * meaning the same as ESC 0x4F, but those cannot yet be implemented
1117 * because of bug 777: the UTF-8 decoder is run too late. */
1118 if (itrm
->in
.queue
.data
[0] == ASCII_ESC
) {
1119 if (itrm
->in
.queue
.len
< 2) {
1121 } else if (itrm
->in
.queue
.data
[1] == 0x5B /* CSI */) {
1122 el
= decode_terminal_escape_sequence(itrm
, &ev
);
1123 } else if (itrm
->in
.queue
.data
[1] == 0x4F /* SS3 */) {
1124 el
= decode_terminal_application_key(itrm
, &ev
);
1125 } else if (itrm
->in
.queue
.data
[1] == ASCII_ESC
) {
1126 /* ESC ESC can be either Alt-Esc or the
1127 * beginning of e.g. ESC ESC 0x5B 0x41,
1128 * which we should parse as Esc Up. */
1129 if (itrm
->in
.queue
.len
< 3) {
1130 /* Need more data to figure it out. */
1132 } else if (itrm
->in
.queue
.data
[2] == 0x5B
1133 || itrm
->in
.queue
.data
[2] == 0x4F) {
1134 /* The first ESC appears to be followed
1135 * by an escape sequence. Treat it as
1136 * a standalone Esc. */
1138 set_kbd_event(itrm
, &ev
,
1139 itrm
->in
.queue
.data
[0],
1142 /* The second ESC of ESC ESC is not the
1143 * beginning of any known escape sequence.
1144 * This must be Alt-Esc, then. */
1146 set_kbd_event(itrm
, &ev
,
1147 itrm
->in
.queue
.data
[1],
1151 if (el
== 0) { /* Begins with ESC, but none of the above */
1153 set_kbd_event(itrm
, &ev
, itrm
->in
.queue
.data
[1],
1157 } else if (itrm
->in
.queue
.data
[0] == 0) {
1158 static const struct term_event_keyboard os2xtd
[256] = {
1159 #include "terminal/key.inc"
1162 if (itrm
->in
.queue
.len
< 2)
1166 set_kbd_interlink_event(&ev
,
1167 os2xtd
[itrm
->in
.queue
.data
[1]].key
,
1168 os2xtd
[itrm
->in
.queue
.data
[1]].modifier
);
1174 set_kbd_event(itrm
, &ev
, itrm
->in
.queue
.data
[0],
1175 itrm
->bracketed_pasting
? KBD_MOD_PASTE
: KBD_MOD_NONE
);
1178 /* The call to decode_terminal_escape_sequence() might have changed the
1179 * keyboard event to a mouse event. */
1180 if (ev
.ev
== EVENT_MOUSE
|| ev
.info
.keyboard
.key
!= KBD_UNDEF
) {
1181 itrm_queue_event(itrm
, (char *) &ev
, sizeof(ev
));
1182 itrm
->bracketed_pasting
=
1184 && (ev
.info
.keyboard
.modifier
& KBD_MOD_PASTE
));
1187 return_without_event
:
1189 install_timer(&itrm
->timer
, ESC_TIMEOUT
, (void (*)(void *)) kbd_timeout
,
1193 assertm(itrm
->in
.queue
.len
>= el
, "event queue underflow");
1194 if_assert_failed
{ itrm
->in
.queue
.len
= el
; }
1196 itrm
->in
.queue
.len
-= el
;
1197 if (itrm
->in
.queue
.len
)
1198 memmove(itrm
->in
.queue
.data
, itrm
->in
.queue
.data
+ el
, itrm
->in
.queue
.len
);
1200 if (itrm
->in
.queue
.len
< ITRM_IN_QUEUE_SIZE
)
1201 handle_itrm_stdin(itrm
);
1208 /** A select_handler_T read_func for itrm_in.std. This is called when
1209 * characters typed by the user arrive from the terminal. */
1211 in_kbd(struct itrm
*itrm
)
1215 if (!can_read(itrm
->in
.std
)) return;
1217 kill_timer(&itrm
->timer
);
1219 if (itrm
->in
.queue
.len
>= ITRM_IN_QUEUE_SIZE
) {
1220 unhandle_itrm_stdin(itrm
);
1221 while (process_queue(itrm
));
1225 r
= safe_read(itrm
->in
.std
, itrm
->in
.queue
.data
+ itrm
->in
.queue
.len
,
1226 ITRM_IN_QUEUE_SIZE
- itrm
->in
.queue
.len
);
1232 itrm
->in
.queue
.len
+= r
;
1233 if (itrm
->in
.queue
.len
> ITRM_IN_QUEUE_SIZE
) {
1234 ERROR(gettext("Too many bytes read from the itrm!"));
1235 itrm
->in
.queue
.len
= ITRM_IN_QUEUE_SIZE
;
1238 while (process_queue(itrm
));
1241 /** Enable reading from itrm_in.std. ELinks will read any available
1242 * bytes from the tty into itrm->in.queue and then parse them.
1243 * Reading should be enabled whenever itrm->in.queue is not full and
1244 * itrm->blocked is 0. */
1246 handle_itrm_stdin(struct itrm
*itrm
)
1248 assert(itrm
->in
.std
>= 0);
1249 if_assert_failed
return;
1251 set_handlers(itrm
->in
.std
, (select_handler_T
) in_kbd
, NULL
,
1252 (select_handler_T
) free_itrm
, itrm
);
1255 /** Disable reading from itrm_in.std. Reading should be disabled
1256 * whenever itrm->in.queue is full (there is no room for the data)
1257 * or itrm->blocked is 1 (other processes may read the data). */
1259 unhandle_itrm_stdin(struct itrm
*itrm
)
1261 assert(itrm
->in
.std
>= 0);
1262 if_assert_failed
return;
1264 set_handlers(itrm
->in
.std
, (select_handler_T
) NULL
, NULL
,
1265 (select_handler_T
) free_itrm
, itrm
);