diff options
author | Nicholas Marriott <nicm@cvs.openbsd.org> | 2010-03-22 19:02:55 +0000 |
---|---|---|
committer | Nicholas Marriott <nicm@cvs.openbsd.org> | 2010-03-22 19:02:55 +0000 |
commit | 939cfceb976ee9c613eb5e06a59fc85037bc2b93 (patch) | |
tree | 3a82c44a05c344b52b3d02be578c82fa6457d5ba | |
parent | bdd42d96cd5381a69bda562d85bf5e2f7e061ce6 (diff) |
New input parser based on http://vt100.net/emu/dec_ansi_parser.
-rw-r--r-- | usr.bin/tmux/input.c | 2283 | ||||
-rw-r--r-- | usr.bin/tmux/tmux.h | 53 | ||||
-rw-r--r-- | usr.bin/tmux/window.c | 77 |
3 files changed, 1238 insertions, 1175 deletions
diff --git a/usr.bin/tmux/input.c b/usr.bin/tmux/input.c index 70080438b58..6d83af2382b 100644 --- a/usr.bin/tmux/input.c +++ b/usr.bin/tmux/input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: input.c,v 1.27 2010/02/08 00:14:38 nicm Exp $ */ +/* $OpenBSD: input.c,v 1.28 2010/03/22 19:02:54 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> @@ -18,1395 +18,1259 @@ #include <sys/types.h> -#include <stdint.h> #include <stdlib.h> #include <string.h> #include "tmux.h" -#define INPUT_C0CONTROL(ch) (ch <= 0x1f) -#define INPUT_INTERMEDIATE(ch) (ch == 0xa0 || (ch >= 0x20 && ch <= 0x2f)) -#define INPUT_PARAMETER(ch) (ch >= 0x30 && ch <= 0x3f) -#define INPUT_UPPERCASE(ch) (ch >= 0x40 && ch <= 0x5f) -#define INPUT_LOWERCASE(ch) (ch >= 0x60 && ch <= 0x7e) -#define INPUT_DELETE(ch) (ch == 0x7f) -#define INPUT_C1CONTROL(ch) (ch >= 0x80 && ch <= 0x9f) -#define INPUT_G1DISPLAYABLE(ch) (ch >= 0xa1 && ch <= 0xfe) -#define INPUT_SPECIAL(ch) (ch == 0xff) - -int input_get_argument(struct input_ctx *, u_int, uint16_t *, uint16_t); -void input_new_argument(struct input_ctx *); -int input_add_argument(struct input_ctx *, u_char); - -void input_start_string(struct input_ctx *, int); -void input_abort_string(struct input_ctx *); -int input_add_string(struct input_ctx *, u_char); -char *input_get_string(struct input_ctx *); - -void input_state(struct input_ctx *, void *); - -void input_state_first(u_char, struct input_ctx *); -void input_state_escape(u_char, struct input_ctx *); -void input_state_intermediate(u_char, struct input_ctx *); -void input_state_sequence_first(u_char, struct input_ctx *); -void input_state_sequence_next(u_char, struct input_ctx *); -void input_state_sequence_intermediate(u_char, struct input_ctx *); -void input_state_string_next(u_char, struct input_ctx *); -void input_state_string_escape(u_char, struct input_ctx *); -void input_state_utf8(u_char, struct input_ctx *); - -void input_handle_character(u_char, struct input_ctx *); -void input_handle_c0_control(u_char, struct input_ctx *); -void input_handle_c1_control(u_char, struct input_ctx *); -void input_handle_private_two(u_char, struct input_ctx *); -void input_handle_standard_two(u_char, struct input_ctx *); -void input_handle_sequence(u_char, struct input_ctx *); - -void input_handle_sequence_cuu(struct input_ctx *); -void input_handle_sequence_cud(struct input_ctx *); -void input_handle_sequence_cuf(struct input_ctx *); -void input_handle_sequence_cub(struct input_ctx *); -void input_handle_sequence_dch(struct input_ctx *); -void input_handle_sequence_cbt(struct input_ctx *); -void input_handle_sequence_da(struct input_ctx *); -void input_handle_sequence_dl(struct input_ctx *); -void input_handle_sequence_ich(struct input_ctx *); -void input_handle_sequence_il(struct input_ctx *); -void input_handle_sequence_vpa(struct input_ctx *); -void input_handle_sequence_hpa(struct input_ctx *); -void input_handle_sequence_cup(struct input_ctx *); -void input_handle_sequence_cup(struct input_ctx *); -void input_handle_sequence_tbc(struct input_ctx *); -void input_handle_sequence_ed(struct input_ctx *); -void input_handle_sequence_el(struct input_ctx *); -void input_handle_sequence_sm(struct input_ctx *); -void input_handle_sequence_rm(struct input_ctx *); -void input_handle_sequence_decstbm(struct input_ctx *); -void input_handle_sequence_sgr(struct input_ctx *); -void input_handle_sequence_dsr(struct input_ctx *); - -int input_sequence_cmp(const void *, const void *); - -struct input_sequence_entry { - u_char ch; - void (*fn)(struct input_ctx *); -}; -const struct input_sequence_entry input_sequence_table[] = { - { '@', input_handle_sequence_ich }, - { 'A', input_handle_sequence_cuu }, - { 'B', input_handle_sequence_cud }, - { 'C', input_handle_sequence_cuf }, - { 'D', input_handle_sequence_cub }, - { 'G', input_handle_sequence_hpa }, - { 'H', input_handle_sequence_cup }, - { 'J', input_handle_sequence_ed }, - { 'K', input_handle_sequence_el }, - { 'L', input_handle_sequence_il }, - { 'M', input_handle_sequence_dl }, - { 'P', input_handle_sequence_dch }, - { 'Z', input_handle_sequence_cbt }, - { 'c', input_handle_sequence_da }, - { 'd', input_handle_sequence_vpa }, - { 'f', input_handle_sequence_cup }, - { 'g', input_handle_sequence_tbc }, - { 'h', input_handle_sequence_sm }, - { 'l', input_handle_sequence_rm }, - { 'm', input_handle_sequence_sgr }, - { 'n', input_handle_sequence_dsr }, - { 'r', input_handle_sequence_decstbm }, +/* + * Based on the description by Paul Williams at: + * + * http://vt100.net/emu/dec_ansi_parser + * + * With the following changes: + * + * - 7-bit only. + * + * - Support for UTF-8. + * + * - OSC (but not APC) may be terminated by \007 as well as ST. + * + * - A state for APC similar to OSC. Some terminals appear to use this to set + * the title. + * + * - A state for the screen \033k...\033\\ sequence to rename a window. This is + * pretty stupid but not supporting it is more trouble than it is worth. + */ + +/* Helper functions. */ +int input_split(struct input_ctx *); +int input_get(struct input_ctx *, u_int, int, int); +void input_reply(struct input_ctx *, const char *, ...); + +/* Transition entry/exit handlers. */ +void input_clear(struct input_ctx *); +void input_enter_dcs(struct input_ctx *); +void input_exit_dcs(struct input_ctx *); +void input_enter_osc(struct input_ctx *); +void input_exit_osc(struct input_ctx *); +void input_enter_apc(struct input_ctx *); +void input_exit_apc(struct input_ctx *); +void input_enter_rename(struct input_ctx *); +void input_exit_rename(struct input_ctx *); + +/* Input state handlers. */ +int input_print(struct input_ctx *); +int input_intermediate(struct input_ctx *); +int input_parameter(struct input_ctx *); +int input_input(struct input_ctx *); +int input_c0_dispatch(struct input_ctx *); +int input_esc_dispatch(struct input_ctx *); +int input_csi_dispatch(struct input_ctx *); +void input_csi_dispatch_sgr(struct input_ctx *); +int input_utf8_open(struct input_ctx *); +int input_utf8_add(struct input_ctx *); +int input_utf8_close(struct input_ctx *); + +/* Command table comparison function. */ +int input_table_compare(const void *, const void *); + +/* Command table entry. */ +struct input_table_entry { + int ch; + const char *interm; + int type; }; -int -input_sequence_cmp(const void *a, const void *b) -{ - int ai = ((const struct input_sequence_entry *) a)->ch; - int bi = ((const struct input_sequence_entry *) b)->ch; +/* Escape commands. */ +enum input_esc_type { + INPUT_ESC_DECALN, + INPUT_ESC_DECKPAM, + INPUT_ESC_DECKPNM, + INPUT_ESC_DECRC, + INPUT_ESC_DECSC, + INPUT_ESC_HTS, + INPUT_ESC_IND, + INPUT_ESC_NEL, + INPUT_ESC_RI, + INPUT_ESC_RIS, + INPUT_ESC_SCSOFF_G0, + INPUT_ESC_SCSON_G0, +}; - return (ai - bi); -} +/* Escape command table. */ +const struct input_table_entry input_esc_table[] = { + { '0', "(", INPUT_ESC_SCSOFF_G0 }, + { '7', "", INPUT_ESC_DECSC }, + { '8', "", INPUT_ESC_DECRC }, + { '8', "#", INPUT_ESC_DECALN }, + { '=', "", INPUT_ESC_DECKPAM }, + { '>', "", INPUT_ESC_DECKPNM }, + { 'B', "(", INPUT_ESC_SCSON_G0 }, + { 'D', "", INPUT_ESC_IND }, + { 'E', "", INPUT_ESC_NEL }, + { 'H', "", INPUT_ESC_HTS }, + { 'M', "", INPUT_ESC_RI }, + { 'c', "", INPUT_ESC_RIS }, +}; -void -input_new_argument(struct input_ctx *ictx) -{ - struct input_arg *arg; +/* Control (CSI) commands. */ +enum input_csi_type { + INPUT_CSI_CBT, + INPUT_CSI_CUB, + INPUT_CSI_CUD, + INPUT_CSI_CUF, + INPUT_CSI_CUP, + INPUT_CSI_CUU, + INPUT_CSI_DA, + INPUT_CSI_DCH, + INPUT_CSI_DECSTBM, + INPUT_CSI_DL, + INPUT_CSI_DSR, + INPUT_CSI_ED, + INPUT_CSI_EL, + INPUT_CSI_HPA, + INPUT_CSI_ICH, + INPUT_CSI_IL, + INPUT_CSI_RM, + INPUT_CSI_RM_PRIVATE, + INPUT_CSI_SGR, + INPUT_CSI_SM, + INPUT_CSI_SM_PRIVATE, + INPUT_CSI_TBC, + INPUT_CSI_VPA, +}; - ARRAY_EXPAND(&ictx->args, 1); +/* Control (CSI) command table. */ +const struct input_table_entry input_csi_table[] = { + { '@', "", INPUT_CSI_ICH }, + { 'A', "", INPUT_CSI_CUU }, + { 'B', "", INPUT_CSI_CUD }, + { 'C', "", INPUT_CSI_CUF }, + { 'D', "", INPUT_CSI_CUB }, + { 'G', "", INPUT_CSI_HPA }, + { 'H', "", INPUT_CSI_CUP }, + { 'J', "", INPUT_CSI_ED }, + { 'K', "", INPUT_CSI_EL }, + { 'L', "", INPUT_CSI_IL }, + { 'M', "", INPUT_CSI_DL }, + { 'P', "", INPUT_CSI_DCH }, + { 'Z', "", INPUT_CSI_CBT }, + { 'c', "", INPUT_CSI_DA }, + { 'd', "", INPUT_CSI_VPA }, + { 'f', "", INPUT_CSI_CUP }, + { 'g', "", INPUT_CSI_TBC }, + { 'h', "", INPUT_CSI_SM }, + { 'h', "?", INPUT_CSI_SM_PRIVATE }, + { 'l', "", INPUT_CSI_RM }, + { 'l', "?", INPUT_CSI_RM_PRIVATE }, + { 'm', "", INPUT_CSI_SGR }, + { 'n', "", INPUT_CSI_DSR }, + { 'r', "", INPUT_CSI_DECSTBM }, +}; - arg = &ARRAY_LAST(&ictx->args); - arg->used = 0; -} +/* Input transition. */ +struct input_transition { + int first; + int last; -int -input_add_argument(struct input_ctx *ictx, u_char ch) -{ - struct input_arg *arg; - - if (ARRAY_LENGTH(&ictx->args) == 0) - return (0); + int (*handler)(struct input_ctx *); + const struct input_state *state; +}; - arg = &ARRAY_LAST(&ictx->args); - if (arg->used > (sizeof arg->data) - 1) - return (-1); - arg->data[arg->used++] = ch; +/* Input state. */ +struct input_state { + const char *name; + void (*enter)(struct input_ctx *); + void (*exit)(struct input_ctx *); + const struct input_transition *transitions; +}; - return (0); -} +/* State transitions available from all states. */ +#define INPUT_STATE_ANYWHERE \ + { 0x18, 0x18, input_c0_dispatch, &input_state_ground }, \ + { 0x1a, 0x1a, input_c0_dispatch, &input_state_ground }, \ + { 0x1b, 0x1b, NULL, &input_state_esc_enter } + +/* Forward declarations of state tables. */ +const struct input_transition input_state_ground_table[]; +const struct input_transition input_state_esc_enter_table[]; +const struct input_transition input_state_esc_intermediate_table[]; +const struct input_transition input_state_csi_enter_table[]; +const struct input_transition input_state_csi_parameter_table[]; +const struct input_transition input_state_csi_intermediate_table[]; +const struct input_transition input_state_csi_ignore_table[]; +const struct input_transition input_state_dcs_enter_table[]; +const struct input_transition input_state_dcs_parameter_table[]; +const struct input_transition input_state_dcs_intermediate_table[]; +const struct input_transition input_state_dcs_handler_table[]; +const struct input_transition input_state_dcs_ignore_table[]; +const struct input_transition input_state_osc_string_table[]; +const struct input_transition input_state_apc_string_table[]; +const struct input_transition input_state_rename_string_table[]; +const struct input_transition input_state_consume_st_table[]; +const struct input_transition input_state_utf8_three_table[]; +const struct input_transition input_state_utf8_two_table[]; +const struct input_transition input_state_utf8_one_table[]; + +/* ground state definition. */ +const struct input_state input_state_ground = { + "ground", + NULL, NULL, + input_state_ground_table +}; -int -input_get_argument(struct input_ctx *ictx, u_int i, uint16_t *n, uint16_t d) -{ - struct input_arg *arg; - const char *errstr; +/* esc_enter state definition. */ +const struct input_state input_state_esc_enter = { + "esc_enter", + input_clear, NULL, + input_state_esc_enter_table +}; - *n = d; - if (i >= ARRAY_LENGTH(&ictx->args)) - return (0); +/* esc_intermediate state definition. */ +const struct input_state input_state_esc_intermediate = { + "esc_intermediate", + NULL, NULL, + input_state_esc_intermediate_table +}; - arg = &ARRAY_ITEM(&ictx->args, i); - if (*arg->data == '\0') - return (0); +/* csi_enter state definition. */ +const struct input_state input_state_csi_enter = { + "csi_enter", + input_clear, NULL, + input_state_csi_enter_table +}; - *n = strtonum(arg->data, 0, UINT16_MAX, &errstr); - if (errstr != NULL) - return (-1); - return (0); -} +/* csi_parameter state definition. */ +const struct input_state input_state_csi_parameter = { + "csi_parameter", + NULL, NULL, + input_state_csi_parameter_table +}; -void -input_start_string(struct input_ctx *ictx, int type) -{ - ictx->string_type = type; - ictx->string_len = 0; -} +/* csi_intermediate state definition. */ +const struct input_state input_state_csi_intermediate = { + "csi_intermediate", + NULL, NULL, + input_state_csi_intermediate_table +}; -void -input_abort_string(struct input_ctx *ictx) -{ - if (ictx->string_buf != NULL) - xfree(ictx->string_buf); - ictx->string_buf = NULL; -} +/* csi_ignore state definition. */ +const struct input_state input_state_csi_ignore = { + "csi_ignore", + NULL, NULL, + input_state_csi_ignore_table +}; -int -input_add_string(struct input_ctx *ictx, u_char ch) -{ - ictx->string_buf = xrealloc(ictx->string_buf, 1, ictx->string_len + 1); - ictx->string_buf[ictx->string_len++] = ch; +/* dcs_enter state definition. */ +const struct input_state input_state_dcs_enter = { + "dcs_enter", + input_clear, NULL, + input_state_dcs_enter_table +}; - if (ictx->string_len >= MAXSTRINGLEN) { - input_abort_string(ictx); - return (1); - } +/* dcs_parameter state definition. */ +const struct input_state input_state_dcs_parameter = { + "dcs_parameter", + NULL, NULL, + input_state_dcs_parameter_table +}; - return (0); -} +/* dcs_intermediate state definition. */ +const struct input_state input_state_dcs_intermediate = { + "dcs_intermediate", + NULL, NULL, + input_state_dcs_intermediate_table +}; -char * -input_get_string(struct input_ctx *ictx) -{ - char *s; +/* dcs_handler state definition. */ +const struct input_state input_state_dcs_handler = { + "dcs_handler", + input_enter_dcs, input_exit_dcs, + input_state_dcs_handler_table +}; - if (ictx->string_buf == NULL || input_add_string(ictx, '\0') != 0) - return (xstrdup("")); +/* dcs_ignore state definition. */ +const struct input_state input_state_dcs_ignore = { + "dcs_ignore", + NULL, NULL, + input_state_dcs_ignore_table +}; - s = ictx->string_buf; - ictx->string_buf = NULL; - return (s); -} +/* osc_string state definition. */ +const struct input_state input_state_osc_string = { + "osc_string", + input_enter_osc, input_exit_osc, + input_state_osc_string_table +}; -void -input_state(struct input_ctx *ictx, void *state) -{ - ictx->state = state; -} +/* apc_string state definition. */ +const struct input_state input_state_apc_string = { + "apc_string", + input_enter_apc, input_exit_apc, + input_state_apc_string_table +}; -void -input_init(struct window_pane *wp) -{ - struct input_ctx *ictx = &wp->ictx; +/* rename_string state definition. */ +const struct input_state input_state_rename_string = { + "rename_string", + input_enter_rename, input_exit_rename, + input_state_rename_string_table +}; - ARRAY_INIT(&ictx->args); +/* consume_st state definition. */ +const struct input_state input_state_consume_st = { + "consume_st", + NULL, NULL, + input_state_consume_st_table +}; - ictx->string_len = 0; - ictx->string_buf = NULL; +/* utf8_three state definition. */ +const struct input_state input_state_utf8_three = { + "utf8_three", + NULL, NULL, + input_state_utf8_three_table +}; - memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell); +/* utf8_two state definition. */ +const struct input_state input_state_utf8_two = { + "utf8_two", + NULL, NULL, + input_state_utf8_two_table +}; - memcpy(&ictx->saved_cell, &grid_default_cell, sizeof ictx->saved_cell); - ictx->saved_cx = 0; - ictx->saved_cy = 0; +/* utf8_one state definition. */ +const struct input_state input_state_utf8_one = { + "utf8_one", + NULL, NULL, + input_state_utf8_one_table +}; - input_state(ictx, input_state_first); +/* ground state table. */ +const struct input_transition input_state_ground_table[] = { + INPUT_STATE_ANYWHERE, + + { 0x00, 0x17, input_c0_dispatch, NULL }, + { 0x19, 0x19, input_c0_dispatch, NULL }, + { 0x1c, 0x1f, input_c0_dispatch, NULL }, + { 0x20, 0x7e, input_print, NULL }, + { 0x7f, 0x7f, NULL, NULL }, + { 0x80, 0xc1, input_print, NULL }, + { 0xc2, 0xdf, input_utf8_open, &input_state_utf8_one }, + { 0xe0, 0xef, input_utf8_open, &input_state_utf8_two }, + { 0xf0, 0xf4, input_utf8_open, &input_state_utf8_three }, + { 0xf5, 0xff, input_print, NULL }, + + { -1, -1, NULL, NULL } +}; - ictx->was = 0; -} +/* esc_enter state table. */ +const struct input_transition input_state_esc_enter_table[] = { + INPUT_STATE_ANYWHERE, + + { 0x00, 0x17, input_c0_dispatch, NULL }, + { 0x19, 0x19, input_c0_dispatch, NULL }, + { 0x1c, 0x1f, input_c0_dispatch, NULL }, + { 0x20, 0x2f, input_intermediate, &input_state_esc_intermediate }, + { 0x30, 0x4f, input_esc_dispatch, &input_state_ground }, + { 0x50, 0x50, NULL, &input_state_dcs_enter }, + { 0x51, 0x57, input_esc_dispatch, &input_state_ground }, + { 0x58, 0x58, NULL, &input_state_consume_st }, + { 0x59, 0x59, input_esc_dispatch, &input_state_ground }, + { 0x5a, 0x5a, input_esc_dispatch, &input_state_ground }, + { 0x5b, 0x5b, NULL, &input_state_csi_enter }, + { 0x5c, 0x5c, input_esc_dispatch, &input_state_ground }, + { 0x5d, 0x5d, NULL, &input_state_osc_string }, + { 0x5e, 0x5e, NULL, &input_state_consume_st }, + { 0x5f, 0x5f, NULL, &input_state_apc_string }, + { 0x60, 0x6a, input_esc_dispatch, &input_state_ground }, + { 0x6b, 0x6b, NULL, &input_state_rename_string }, + { 0x6c, 0x7c, input_esc_dispatch, &input_state_ground }, + { 0x7f, 0xff, NULL, NULL }, + + { -1, -1, NULL, NULL } +}; -void -input_free(struct window_pane *wp) -{ - if (wp->ictx.string_buf != NULL) - xfree(wp->ictx.string_buf); +/* esc_interm state table. */ +const struct input_transition input_state_esc_intermediate_table[] = { + INPUT_STATE_ANYWHERE, - ARRAY_FREE(&wp->ictx.args); -} + { 0x00, 0x17, input_c0_dispatch, NULL }, + { 0x19, 0x19, input_c0_dispatch, NULL }, + { 0x1c, 0x1f, input_c0_dispatch, NULL }, + { 0x20, 0x2f, input_intermediate, NULL }, + { 0x30, 0x7e, input_esc_dispatch, &input_state_ground }, + { 0x7f, 0xff, NULL, NULL }, -void -input_parse(struct window_pane *wp) -{ - struct input_ctx *ictx = &wp->ictx; - u_char ch; + { -1, -1, NULL, NULL } +}; - if (EVBUFFER_LENGTH(wp->event->input) == ictx->was) - return; - wp->window->flags |= WINDOW_ACTIVITY; +/* csi_enter state table. */ +const struct input_transition input_state_csi_enter_table[] = { + INPUT_STATE_ANYWHERE, + + { 0x00, 0x17, input_c0_dispatch, NULL }, + { 0x19, 0x19, input_c0_dispatch, NULL }, + { 0x1c, 0x1f, input_c0_dispatch, NULL }, + { 0x20, 0x2f, input_intermediate, &input_state_csi_intermediate }, + { 0x30, 0x39, input_parameter, &input_state_csi_parameter }, + { 0x3a, 0x3a, NULL, &input_state_csi_ignore }, + { 0x3b, 0x3b, input_parameter, &input_state_csi_parameter }, + { 0x3c, 0x3f, input_intermediate, &input_state_csi_parameter }, + { 0x40, 0x7e, input_csi_dispatch, &input_state_ground }, + { 0x7f, 0xff, NULL, NULL }, + + { -1, -1, NULL, NULL } +}; - ictx->buf = EVBUFFER_DATA(wp->event->input); - ictx->len = EVBUFFER_LENGTH(wp->event->input); - ictx->off = 0; +/* csi_parameter state table. */ +const struct input_transition input_state_csi_parameter_table[] = { + INPUT_STATE_ANYWHERE, + + { 0x00, 0x17, input_c0_dispatch, NULL }, + { 0x19, 0x19, input_c0_dispatch, NULL }, + { 0x1c, 0x1f, input_c0_dispatch, NULL }, + { 0x20, 0x2f, input_intermediate, &input_state_csi_intermediate }, + { 0x30, 0x39, input_parameter, NULL }, + { 0x3a, 0x3a, NULL, &input_state_csi_ignore }, + { 0x3b, 0x3b, input_parameter, NULL }, + { 0x3c, 0x3f, NULL, &input_state_csi_ignore }, + { 0x40, 0x7e, input_csi_dispatch, &input_state_ground }, + { 0x7f, 0xff, NULL, NULL }, + + { -1, -1, NULL, NULL } +}; - ictx->wp = wp; +/* csi_intermediate state table. */ +const struct input_transition input_state_csi_intermediate_table[] = { + INPUT_STATE_ANYWHERE, - /* If there is a mode set, don't want to update the screen. */ - if (wp->mode == NULL) - screen_write_start(&ictx->ctx, wp, &wp->base); - else - screen_write_start(&ictx->ctx, NULL, &wp->base); + { 0x00, 0x17, input_c0_dispatch, NULL }, + { 0x19, 0x19, input_c0_dispatch, NULL }, + { 0x1c, 0x1f, input_c0_dispatch, NULL }, + { 0x20, 0x2f, input_intermediate, NULL }, + { 0x30, 0x3f, NULL, &input_state_csi_ignore }, + { 0x40, 0x7e, input_csi_dispatch, &input_state_ground }, + { 0x7f, 0xff, NULL, NULL }, - while (ictx->off < ictx->len) { - ch = ictx->buf[ictx->off++]; - ictx->state(ch, ictx); - } + { -1, -1, NULL, NULL } +}; - screen_write_stop(&ictx->ctx); +/* csi_ignore state table. */ +const struct input_transition input_state_csi_ignore_table[] = { + INPUT_STATE_ANYWHERE, - evbuffer_drain(wp->event->input, ictx->len); - ictx->was = EVBUFFER_LENGTH(wp->event->input); -} + { 0x00, 0x17, input_c0_dispatch, NULL }, + { 0x19, 0x19, input_c0_dispatch, NULL }, + { 0x1c, 0x1f, input_c0_dispatch, NULL }, + { 0x20, 0x3f, NULL, NULL }, + { 0x40, 0x7e, NULL, &input_state_ground }, + { 0x7f, 0xff, NULL, NULL }, -void -input_state_first(u_char ch, struct input_ctx *ictx) -{ - ictx->intermediate = '\0'; + { -1, -1, NULL, NULL } +}; - if (INPUT_C0CONTROL(ch)) { - if (ch == 0x1b) - input_state(ictx, input_state_escape); - else - input_handle_c0_control(ch, ictx); - return; - } +/* dcs_enter state table. */ +const struct input_transition input_state_dcs_enter_table[] = { + INPUT_STATE_ANYWHERE, + + { 0x00, 0x17, NULL, NULL }, + { 0x19, 0x19, NULL, NULL }, + { 0x1c, 0x1f, NULL, NULL }, + { 0x20, 0x2f, input_intermediate, &input_state_dcs_intermediate }, + { 0x30, 0x39, input_parameter, &input_state_dcs_parameter }, + { 0x3a, 0x3a, NULL, &input_state_dcs_ignore }, + { 0x3b, 0x3b, input_parameter, &input_state_dcs_parameter }, + { 0x3c, 0x3f, input_intermediate, &input_state_dcs_parameter }, + { 0x40, 0x7e, NULL, &input_state_dcs_handler }, + { 0x7f, 0xff, NULL, NULL }, + + { -1, -1, NULL, NULL } +}; -#if 0 - if (INPUT_C1CONTROL(ch)) { - ch -= 0x40; - if (ch == '[') - input_state(ictx, input_state_sequence_first); - else if (ch == ']') { - input_start_string(ictx, STRING_SYSTEM); - input_state(ictx, input_state_string_next); - } else if (ch == '_') { - input_start_string(ictx, STRING_APPLICATION); - input_state(ictx, input_state_string_next); - } else - input_handle_c1_control(ch, ictx); - return; - } -#endif +/* dcs_parameter state table. */ +const struct input_transition input_state_dcs_parameter_table[] = { + INPUT_STATE_ANYWHERE, + + { 0x00, 0x17, NULL, NULL }, + { 0x19, 0x19, NULL, NULL }, + { 0x1c, 0x1f, NULL, NULL }, + { 0x20, 0x2f, input_intermediate, &input_state_dcs_intermediate }, + { 0x30, 0x39, input_parameter, NULL }, + { 0x3a, 0x3a, NULL, &input_state_dcs_ignore }, + { 0x3b, 0x3b, input_parameter, NULL }, + { 0x3c, 0x3f, NULL, &input_state_dcs_ignore }, + { 0x40, 0x7e, NULL, &input_state_dcs_handler }, + { 0x7f, 0xff, NULL, NULL }, + + { -1, -1, NULL, NULL } +}; - if (INPUT_DELETE(ch)) - return; +/* dcs_interm state table. */ +const struct input_transition input_state_dcs_intermediate_table[] = { + INPUT_STATE_ANYWHERE, - input_handle_character(ch, ictx); -} + { 0x00, 0x17, NULL, NULL }, + { 0x19, 0x19, NULL, NULL }, + { 0x1c, 0x1f, NULL, NULL }, + { 0x20, 0x2f, input_intermediate, NULL }, + { 0x30, 0x3f, NULL, &input_state_dcs_ignore }, + { 0x40, 0x7e, NULL, &input_state_dcs_handler }, + { 0x7f, 0xff, NULL, NULL }, -void -input_state_escape(u_char ch, struct input_ctx *ictx) -{ - /* Treat C1 control and G1 displayable as 7-bit equivalent. */ - if (INPUT_C1CONTROL(ch) || INPUT_G1DISPLAYABLE(ch)) - ch &= 0x7f; + { -1, -1, NULL, NULL } +}; - if (INPUT_C0CONTROL(ch)) { - input_handle_c0_control(ch, ictx); - return; - } +/* dcs_handler state table. */ +const struct input_transition input_state_dcs_handler_table[] = { + INPUT_STATE_ANYWHERE, - if (INPUT_INTERMEDIATE(ch)) { - log_debug2(":: in1 %zu: %hhu (%c)", ictx->off, ch, ch); - ictx->intermediate = ch; - input_state(ictx, input_state_intermediate); - return; - } + { 0x00, 0x17, NULL, NULL }, + { 0x19, 0x19, input_input, NULL }, + { 0x1c, 0x1f, input_input, NULL }, + { 0x20, 0x7e, input_input, NULL }, + { 0x7f, 0xff, NULL, NULL }, - if (INPUT_PARAMETER(ch)) { - input_state(ictx, input_state_first); - input_handle_private_two(ch, ictx); - return; - } + { -1, -1, NULL, NULL } +}; - if (INPUT_UPPERCASE(ch)) { - if (ch == '[') - input_state(ictx, input_state_sequence_first); - else if (ch == ']') { - input_start_string(ictx, STRING_SYSTEM); - input_state(ictx, input_state_string_next); - } else if (ch == '_') { - input_start_string(ictx, STRING_APPLICATION); - input_state(ictx, input_state_string_next); - } else { - input_state(ictx, input_state_first); - input_handle_c1_control(ch, ictx); - } - return; - } +/* device_ignore state table. */ +const struct input_transition input_state_dcs_ignore_table[] = { + INPUT_STATE_ANYWHERE, - if (INPUT_LOWERCASE(ch)) { - input_state(ictx, input_state_first); - input_handle_standard_two(ch, ictx); - return; - } + { 0x00, 0x17, NULL, NULL }, + { 0x19, 0x19, NULL, NULL }, + { 0x1c, 0x1f, NULL, NULL }, + { 0x20, 0xff, NULL, NULL }, - input_state(ictx, input_state_first); -} + { -1, -1, NULL, NULL } +}; -void -input_state_intermediate(u_char ch, struct input_ctx *ictx) -{ - if (INPUT_INTERMEDIATE(ch)) { - /* Multiple intermediates currently ignored. */ - log_debug2(":: in2 %zu: %hhu (%c)", ictx->off, ch, ch); - return; - } +/* osc_string state table. */ +const struct input_transition input_state_osc_string_table[] = { + INPUT_STATE_ANYWHERE, - if (INPUT_PARAMETER(ch)) { - input_state(ictx, input_state_first); - input_handle_private_two(ch, ictx); - return; - } + { 0x00, 0x06, NULL, NULL }, + { 0x07, 0x07, NULL, &input_state_ground }, + { 0x08, 0x17, NULL, NULL }, + { 0x19, 0x19, NULL, NULL }, + { 0x1c, 0x1f, NULL, NULL }, + { 0x20, 0xff, input_input, NULL }, - if (INPUT_UPPERCASE(ch) || INPUT_LOWERCASE(ch)) { - input_state(ictx, input_state_first); - input_handle_standard_two(ch, ictx); - return; - } + { -1, -1, NULL, NULL } +}; - input_state(ictx, input_state_first); -} +/* apc_string state table. */ +const struct input_transition input_state_apc_string_table[] = { + INPUT_STATE_ANYWHERE, -void -input_state_sequence_first(u_char ch, struct input_ctx *ictx) -{ - ictx->private = '\0'; - ARRAY_CLEAR(&ictx->args); - - /* Most C0 control are accepted within CSI. */ - if (INPUT_C0CONTROL(ch)) { - if (ch == 0x1b) { /* ESC */ - /* Abort sequence and begin with new. */ - input_state(ictx, input_state_escape); - return; - } else if (ch == 0x18 || ch == 0x1a) { /* CAN and SUB */ - /* Abort sequence. */ - input_state(ictx, input_state_first); - return; - } + { 0x00, 0x17, NULL, NULL }, + { 0x19, 0x19, NULL, NULL }, + { 0x1c, 0x1f, NULL, NULL }, + { 0x20, 0xff, input_input, NULL }, - /* Handle C0 immediately. */ - input_handle_c0_control(ch, ictx); + { -1, -1, NULL, NULL } +}; - /* - * Just come back to this state, in case the next character - * is the start of a private sequence. - */ - return; - } +/* rename_string state table. */ +const struct input_transition input_state_rename_string_table[] = { + INPUT_STATE_ANYWHERE, - input_state(ictx, input_state_sequence_next); + { 0x00, 0x17, NULL, NULL }, + { 0x19, 0x19, NULL, NULL }, + { 0x1c, 0x1f, NULL, NULL }, + { 0x20, 0xff, input_input, NULL }, - /* Private sequence: always the first character. */ - if (ch >= 0x3c && ch <= 0x3f) { - ictx->private = ch; - return; - } + { -1, -1, NULL, NULL } +}; - /* Pass character on directly. */ - input_state_sequence_next(ch, ictx); -} +/* consume_st state table. */ +const struct input_transition input_state_consume_st_table[] = { + INPUT_STATE_ANYWHERE, -void -input_state_sequence_next(u_char ch, struct input_ctx *ictx) -{ - if (INPUT_INTERMEDIATE(ch)) { - if (input_add_argument(ictx, '\0') != 0) - input_state(ictx, input_state_first); - else { - log_debug2(":: si1 %zu: %hhu (%c)", ictx->off, ch, ch); - input_state(ictx, input_state_sequence_intermediate); - } - return; - } + { 0x00, 0x17, NULL, NULL }, + { 0x19, 0x19, NULL, NULL }, + { 0x1c, 0x1f, NULL, NULL }, + { 0x20, 0xff, NULL, NULL }, - if (INPUT_PARAMETER(ch)) { - if (ARRAY_EMPTY(&ictx->args)) - input_new_argument(ictx); - - if (ch == ';') { - if (input_add_argument(ictx, '\0') != 0) - input_state(ictx, input_state_first); - else - input_new_argument(ictx); - } else if (input_add_argument(ictx, ch) != 0) - input_state(ictx, input_state_first); - return; - } + { -1, -1, NULL, NULL } +}; - if (INPUT_UPPERCASE(ch) || INPUT_LOWERCASE(ch)) { - if (input_add_argument(ictx, '\0') != 0) - input_state(ictx, input_state_first); - else { - input_state(ictx, input_state_first); - input_handle_sequence(ch, ictx); - } - return; - } +/* utf8_three state table. */ +const struct input_transition input_state_utf8_three_table[] = { + /* No INPUT_STATE_ANYWHERE */ - /* Most C0 control are accepted within CSI. */ - if (INPUT_C0CONTROL(ch)) { - if (ch == 0x1b) { /* ESC */ - /* Abort sequence and begin with new. */ - input_state(ictx, input_state_escape); - return; - } else if (ch == 0x18 || ch == 0x1a) { /* CAN and SUB */ - /* Abort sequence. */ - input_state(ictx, input_state_first); - return; - } + { 0x00, 0x7f, NULL, &input_state_ground }, + { 0x80, 0xbf, input_utf8_add, &input_state_utf8_two }, + { 0xc0, 0xff, NULL, &input_state_ground }, - /* Handle C0 immediately. */ - input_handle_c0_control(ch, ictx); + { -1, -1, NULL, NULL } +}; - return; - } +/* utf8_two state table. */ +const struct input_transition input_state_utf8_two_table[] = { + /* No INPUT_STATE_ANYWHERE */ - input_state(ictx, input_state_first); -} + { 0x00, 0x7f, NULL, &input_state_ground }, + { 0x80, 0xbf, input_utf8_add, &input_state_utf8_one }, + { 0xc0, 0xff, NULL, &input_state_ground }, -void -input_state_sequence_intermediate(u_char ch, struct input_ctx *ictx) -{ - if (INPUT_INTERMEDIATE(ch)) { - log_debug2(":: si2 %zu: %hhu (%c)", ictx->off, ch, ch); - return; - } - - if (INPUT_UPPERCASE(ch) || INPUT_LOWERCASE(ch)) { - input_state(ictx, input_state_first); - input_handle_sequence(ch, ictx); - return; - } + { -1, -1, NULL, NULL } +}; - input_state(ictx, input_state_first); -} +/* utf8_one state table. */ +const struct input_transition input_state_utf8_one_table[] = { + /* No INPUT_STATE_ANYWHERE */ -void -input_state_string_next(u_char ch, struct input_ctx *ictx) -{ - if (ch == 0x1b) { - input_state(ictx, input_state_string_escape); - return; - } - if (ch == 0x07) { - input_state_string_escape(ch, ictx); - return; - } + { 0x00, 0x7f, NULL, &input_state_ground }, + { 0x80, 0xbf, input_utf8_close, &input_state_ground }, + { 0xc0, 0xff, NULL, &input_state_ground }, - if (ch >= 0x20) { - if (input_add_string(ictx, ch) != 0) - input_state(ictx, input_state_first); - return; - } -} + { -1, -1, NULL, NULL } +}; -void -input_state_string_escape(u_char ch, struct input_ctx *ictx) +/* Input table compare. */ +int +input_table_compare(const void *key, const void *value) { - char *s; - - if (ch == '\007' || ch == '\\') { - input_state(ictx, input_state_first); - switch (ictx->string_type) { - case STRING_SYSTEM: - if (ch != '\007') - return; - s = input_get_string(ictx); - if ((s[0] != '0' && s[0] != '2') || s[1] != ';') { - xfree(s); - return; - } - screen_set_title(ictx->ctx.s, s + 2); - server_status_window(ictx->wp->window); - xfree(s); - break; - case STRING_APPLICATION: - if (ch != '\\') - return; - s = input_get_string(ictx); - screen_set_title(ictx->ctx.s, s); - server_status_window(ictx->wp->window); - xfree(s); - break; - case STRING_NAME: - if (ch != '\\') - return; - xfree(ictx->wp->window->name); - ictx->wp->window->name = input_get_string(ictx); - server_status_window(ictx->wp->window); - break; - } - return; - } + const struct input_ctx *ictx = key; + const struct input_table_entry *entry = value; - input_state(ictx, input_state_string_next); - input_state_string_next(ch, ictx); + if (ictx->ch != entry->ch) + return (ictx->ch - entry->ch); + return (strcmp(ictx->interm_buf, entry->interm)); } +/* Initialise input parser. */ void -input_state_utf8(u_char ch, struct input_ctx *ictx) +input_init(struct window_pane *wp) { - log_debug2("-- utf8 next: %zu: %hhu (%c)", ictx->off, ch, ch); + struct input_ctx *ictx = &wp->ictx; - if (utf8_append(&ictx->utf8data, ch)) - return; /* more to come */ - input_state(ictx, input_state_first); + memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell); - ictx->cell.flags |= GRID_FLAG_UTF8; - screen_write_cell(&ictx->ctx, &ictx->cell, &ictx->utf8data); - ictx->cell.flags &= ~GRID_FLAG_UTF8; -} + memcpy(&ictx->old_cell, &grid_default_cell, sizeof ictx->old_cell); + ictx->old_cx = 0; + ictx->old_cy = 0; -void -input_handle_character(u_char ch, struct input_ctx *ictx) -{ - struct window_pane *wp = ictx->wp; + *ictx->interm_buf = '\0'; + ictx->interm_len = 0; - if (ch > 0x7f && options_get_number(&wp->window->options, "utf8")) { - if (utf8_open(&ictx->utf8data, ch)) { - log_debug2("-- utf8 size %zu: %zu: %hhu (%c)", - ictx->utf8data.size, ictx->off, ch, ch); - input_state(ictx, input_state_utf8); - return; - } - } - log_debug2("-- ch %zu: %hhu (%c)", ictx->off, ch, ch); + *ictx->param_buf = '\0'; + ictx->param_len = 0; - ictx->cell.data = ch; - screen_write_cell(&ictx->ctx, &ictx->cell, NULL); + ictx->state = &input_state_ground; + ictx->flags = 0; } +/* Destroy input parser. */ void -input_handle_c0_control(u_char ch, struct input_ctx *ictx) +input_free(unused struct window_pane *wp) { - struct screen *s = ictx->ctx.s; - - log_debug2("-- c0 %zu: %hhu", ictx->off, ch); - - switch (ch) { - case '\0': /* NUL */ - break; - case '\n': /* LF */ - screen_write_linefeed(&ictx->ctx, 0); - break; - case '\r': /* CR */ - screen_write_carriagereturn(&ictx->ctx); - break; - case '\007': /* BELL */ - ictx->wp->window->flags |= WINDOW_BELL; - break; - case '\010': /* BS */ - screen_write_backspace(&ictx->ctx); - break; - case '\011': /* TAB */ - /* Don't tab beyond the end of the line. */ - if (s->cx >= screen_size_x(s) - 1) - break; - - /* Find the next tab point, or use the last column if none. */ - do { - s->cx++; - if (bit_test(s->tabs, s->cx)) - break; - } while (s->cx < screen_size_x(s) - 1); - break; - case '\013': /* VT */ - screen_write_linefeed(&ictx->ctx, 0); - break; - case '\016': /* SO */ - ictx->cell.attr |= GRID_ATTR_CHARSET; - break; - case '\017': /* SI */ - ictx->cell.attr &= ~GRID_ATTR_CHARSET; - break; - default: - log_debug("unknown c0: %hhu", ch); - break; - } } +/* Parse input. */ void -input_handle_c1_control(u_char ch, struct input_ctx *ictx) +input_parse(struct window_pane *wp) { - struct screen *s = ictx->ctx.s; + struct input_ctx *ictx = &wp->ictx; + const struct input_transition *itr; + struct evbuffer *evb = wp->event->input; + u_char *buf; + size_t len, off; - log_debug2("-- c1 %zu: %hhu (%c)", ictx->off, ch, ch); + if (EVBUFFER_LENGTH(evb) == 0) + return; + wp->window->flags |= WINDOW_ACTIVITY; - switch (ch) { - case 'D': /* IND */ - screen_write_linefeed(&ictx->ctx, 0); - break; - case 'E': /* NEL */ - screen_write_carriagereturn(&ictx->ctx); - screen_write_linefeed(&ictx->ctx, 0); - break; - case 'H': /* HTS */ - if (s->cx < screen_size_x(s)) - bit_set(s->tabs, s->cx); - break; - case 'M': /* RI */ - screen_write_reverseindex(&ictx->ctx); - break; - default: - log_debug("unknown c1: %hhu", ch); - break; - } -} + /* + * Open the screen. Use NULL wp if there is a mode set as don't want to + * update the tty. + */ + if (wp->mode == NULL) + screen_write_start(&ictx->ctx, wp, &wp->base); + else + screen_write_start(&ictx->ctx, NULL, &wp->base); + ictx->wp = wp; -void -input_handle_private_two(u_char ch, struct input_ctx *ictx) -{ - struct screen *s = ictx->ctx.s; + buf = EVBUFFER_DATA(evb); + len = EVBUFFER_LENGTH(evb); + off = 0; - log_debug2( - "-- p2 %zu: %hhu (%c) %hhu", ictx->off, ch, ch, ictx->intermediate); + /* Parse the input. */ + while (off < len) { + ictx->ch = buf[off++]; + log_debug("%s: '%c' %s", __func__, ictx->ch, ictx->state->name); - switch (ch) { - case '0': /* SCS */ - /* - * Not really supported, but fake it up enough for those that - * use it to switch character sets (by redefining G0 to - * graphics set, rather than switching to G1). - */ - switch (ictx->intermediate) { - case '(': /* G0 */ - ictx->cell.attr |= GRID_ATTR_CHARSET; - break; + /* Find the transition. */ + itr = ictx->state->transitions; + while (itr->first != -1 && itr->last != -1) { + if (ictx->ch >= itr->first && ictx->ch <= itr->last) + break; + itr++; } - break; - case '=': /* DECKPAM */ - if (ictx->intermediate != '\0') - break; - screen_write_kkeypadmode(&ictx->ctx, 1); - log_debug("kkeypad on (application mode)"); - break; - case '>': /* DECKPNM */ - if (ictx->intermediate != '\0') - break; - screen_write_kkeypadmode(&ictx->ctx, 0); - log_debug("kkeypad off (number mode)"); - break; - case '7': /* DECSC */ - if (ictx->intermediate != '\0') - break; - memcpy(&ictx->saved_cell, &ictx->cell, sizeof ictx->saved_cell); - ictx->saved_cx = s->cx; - ictx->saved_cy = s->cy; - break; - case '8': - switch (ictx->intermediate) { - case '\0': /* DECRC */ - memcpy( - &ictx->cell, &ictx->saved_cell, sizeof ictx->cell); - screen_write_cursormove( - &ictx->ctx, ictx->saved_cx, ictx->saved_cy); - break; - case '#': /* DECALN */ - screen_write_alignmenttest(&ictx->ctx); - break; + if (itr->first == -1 || itr->last == -1) { + /* No transition? Eh? */ + fatalx("No transition from state!"); } - break; - default: - log_debug("unknown p2: %hhu", ch); - break; - } -} -void -input_handle_standard_two(u_char ch, struct input_ctx *ictx) -{ - log_debug2( - "-- s2 %zu: %hhu (%c) %hhu", ictx->off, ch, ch, ictx->intermediate); - - switch (ch) { - case 'B': /* SCS */ /* - * Not really supported, but fake it up enough for those that - * use it to switch character sets (by redefining G0 to - * graphics set, rather than switching to G1). + * Execute the handler, if any. Don't switch state if it + * returns non-zero. */ - switch (ictx->intermediate) { - case '(': /* G0 */ - ictx->cell.attr &= ~GRID_ATTR_CHARSET; - break; - } - break; - case 'c': /* RIS */ - memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell); - - memcpy(&ictx->saved_cell, &ictx->cell, sizeof ictx->saved_cell); - ictx->saved_cx = 0; - ictx->saved_cy = 0; - - screen_reset_tabs(ictx->ctx.s); - - screen_write_scrollregion( - &ictx->ctx, 0, screen_size_y(ictx->ctx.s) - 1); - - screen_write_insertmode(&ictx->ctx, 0); - screen_write_kcursormode(&ictx->ctx, 0); - screen_write_kkeypadmode(&ictx->ctx, 0); - screen_write_mousemode(&ictx->ctx, 0); + if (itr->handler && itr->handler(ictx) != 0) + continue; - screen_write_clearscreen(&ictx->ctx); - screen_write_cursormove(&ictx->ctx, 0, 0); - break; - case 'k': - input_start_string(ictx, STRING_NAME); - input_state(ictx, input_state_string_next); - break; - default: - log_debug("unknown s2: %hhu", ch); - break; + /* And switch state, if necessary. */ + if (itr->state) { + if (ictx->state->exit != NULL) + ictx->state->exit(ictx); + ictx->state = itr->state; + if (ictx->state->enter != NULL) + ictx->state->enter(ictx); + } } -} -void -input_handle_sequence(u_char ch, struct input_ctx *ictx) -{ - struct input_sequence_entry *entry, find; - struct screen *s = ictx->ctx.s; - u_int i; - struct input_arg *iarg; - - log_debug2("-- sq %zu: %hhu (%c): %u [sx=%u, sy=%u, cx=%u, cy=%u, " - "ru=%u, rl=%u]", ictx->off, ch, ch, ARRAY_LENGTH(&ictx->args), - screen_size_x(s), screen_size_y(s), s->cx, s->cy, s->rupper, - s->rlower); - for (i = 0; i < ARRAY_LENGTH(&ictx->args); i++) { - iarg = &ARRAY_ITEM(&ictx->args, i); - if (*iarg->data != '\0') - log_debug2(" ++ %u: %s", i, iarg->data); - } + /* Close the screen. */ + screen_write_stop(&ictx->ctx); - find.ch = ch; - entry = bsearch(&find, - input_sequence_table, nitems(input_sequence_table), - sizeof input_sequence_table[0], input_sequence_cmp); - if (entry != NULL) - entry->fn(ictx); - else - log_debug("unknown sq: %c (%hhu %hhu)", ch, ch, ictx->private); + evbuffer_drain(evb, len); } -void -input_handle_sequence_cuu(struct input_ctx *ictx) -{ - uint16_t n; - - if (ictx->private != '\0') - return; - - if (ARRAY_LENGTH(&ictx->args) > 1) - return; - if (input_get_argument(ictx, 0, &n, 1) != 0) - return; - if (n == 0) - n = 1; - - screen_write_cursorup(&ictx->ctx, n); -} +/* Split the parameter list (if any). */ +int +input_split(struct input_ctx *ictx) -void -input_handle_sequence_cud(struct input_ctx *ictx) { - uint16_t n; - - if (ictx->private != '\0') - return; - - if (ARRAY_LENGTH(&ictx->args) > 1) - return; - if (input_get_argument(ictx, 0, &n, 1) != 0) - return; - if (n == 0) - n = 1; + const char *errstr; + char *ptr, *out; + int n; - screen_write_cursordown(&ictx->ctx, n); -} - -void -input_handle_sequence_cuf(struct input_ctx *ictx) -{ - uint16_t n; + ictx->param_list_len = 0; + if (ictx->param_len == 0) + return (0); - if (ictx->private != '\0') - return; + ptr = ictx->param_buf; + while ((out = strsep(&ptr, ";")) != NULL) { + if (*out == '\0') + n = -1; + else { + n = strtonum(out, 0, INT_MAX, &errstr); + if (errstr != NULL) + return (-1); + } - if (ARRAY_LENGTH(&ictx->args) > 1) - return; - if (input_get_argument(ictx, 0, &n, 1) != 0) - return; - if (n == 0) - n = 1; + ictx->param_list[ictx->param_list_len++] = n; + if (ictx->param_list_len == nitems(ictx->param_list)) + return (-1); + } - screen_write_cursorright(&ictx->ctx, n); + return (0); } -void -input_handle_sequence_cub(struct input_ctx *ictx) +/* Get an argument or return default value..*/ +int +input_get(struct input_ctx *ictx, u_int validx, int minval, int defval) { - uint16_t n; - - if (ictx->private != '\0') - return; + int retval; - if (ARRAY_LENGTH(&ictx->args) > 1) - return; - if (input_get_argument(ictx, 0, &n, 1) != 0) - return; - if (n == 0) - n = 1; + if (validx >= ictx->param_list_len) + return (defval); - screen_write_cursorleft(&ictx->ctx, n); + retval = ictx->param_list[validx]; + if (retval == -1) + return (defval); + if (retval < minval) + return (minval); + return (retval); } +/* Reply to terminal query. */ void -input_handle_sequence_dch(struct input_ctx *ictx) +input_reply(struct input_ctx *ictx, const char *fmt, ...) { - uint16_t n; + va_list ap; + char *reply; - if (ictx->private != '\0') - return; - - if (ARRAY_LENGTH(&ictx->args) > 1) - return; - if (input_get_argument(ictx, 0, &n, 1) != 0) - return; - if (n == 0) - n = 1; + va_start(ap, fmt); + vasprintf(&reply, fmt, ap); + va_end(ap); - screen_write_deletecharacter(&ictx->ctx, n); + bufferevent_write(ictx->wp->event, reply, strlen(reply)); + xfree(reply); } +/* Clear saved state. */ void -input_handle_sequence_cbt(struct input_ctx *ictx) +input_clear(struct input_ctx *ictx) { - struct screen *s = ictx->ctx.s; - uint16_t n; + *ictx->interm_buf = '\0'; + ictx->interm_len = 0; - if (ictx->private != '\0') - return; + *ictx->param_buf = '\0'; + ictx->param_len = 0; - if (ARRAY_LENGTH(&ictx->args) > 1) - return; - if (input_get_argument(ictx, 0, &n, 1) != 0) - return; - if (n == 0) - n = 1; - - /* Find the previous tab point, n times. */ - while (s->cx > 0 && n-- > 0) { - do - s->cx--; - while (s->cx > 0 && !bit_test(s->tabs, s->cx)); - } + ictx->flags &= ~INPUT_DISCARD; } -void -input_handle_sequence_da(struct input_ctx *ictx) +/* Output this character to the screen. */ +int +input_print(struct input_ctx *ictx) { - struct window_pane *wp = ictx->wp; - uint16_t n; - - if (ictx->private != '\0') - return; - - if (ARRAY_LENGTH(&ictx->args) > 1) - return; - if (input_get_argument(ictx, 0, &n, 0) != 0) - return; - if (n != 0) - return; + ictx->cell.data = ictx->ch; + screen_write_cell(&ictx->ctx, &ictx->cell, NULL); - bufferevent_write(wp->event, "\033[?1;2c", (sizeof "\033[?1;2c") - 1); + return (0); } -void -input_handle_sequence_dl(struct input_ctx *ictx) +/* Collect intermediate string. */ +int +input_intermediate(struct input_ctx *ictx) { - uint16_t n; - - if (ictx->private != '\0') - return; - - if (ARRAY_LENGTH(&ictx->args) > 1) - return; - if (input_get_argument(ictx, 0, &n, 1) != 0) - return; - if (n == 0) - n = 1; + if (ictx->interm_len == (sizeof ictx->interm_buf) - 1) + ictx->flags |= INPUT_DISCARD; + else { + ictx->interm_buf[ictx->interm_len++] = ictx->ch; + ictx->interm_buf[ictx->interm_len] = '\0'; + } - screen_write_deleteline(&ictx->ctx, n); + return (0); } -void -input_handle_sequence_ich(struct input_ctx *ictx) +/* Collect parameter string. */ +int +input_parameter(struct input_ctx *ictx) { - uint16_t n; - - if (ictx->private != '\0') - return; - - if (ARRAY_LENGTH(&ictx->args) > 1) - return; - if (input_get_argument(ictx, 0, &n, 1) != 0) - return; - if (n == 0) - n = 1; + if (ictx->param_len == (sizeof ictx->param_buf) - 1) + ictx->flags |= INPUT_DISCARD; + else { + ictx->param_buf[ictx->param_len++] = ictx->ch; + ictx->param_buf[ictx->param_len] = '\0'; + } - screen_write_insertcharacter(&ictx->ctx, n); + return (0); } -void -input_handle_sequence_il(struct input_ctx *ictx) +/* Collect input string. */ +int +input_input(struct input_ctx *ictx) { - uint16_t n; - - if (ictx->private != '\0') - return; - - if (ARRAY_LENGTH(&ictx->args) > 1) - return; - if (input_get_argument(ictx, 0, &n, 1) != 0) - return; - if (n == 0) - n = 1; + if (ictx->input_len == (sizeof ictx->input_buf) - 1) + ictx->flags |= INPUT_DISCARD; + else { + ictx->input_buf[ictx->input_len++] = ictx->ch; + ictx->input_buf[ictx->input_len] = '\0'; + } - screen_write_insertline(&ictx->ctx, n); + return (0); } -void -input_handle_sequence_vpa(struct input_ctx *ictx) +/* Execute C0 control sequence. */ +int +input_c0_dispatch(struct input_ctx *ictx) { - struct screen *s = ictx->ctx.s; - uint16_t n; - - if (ictx->private != '\0') - return; - - if (ARRAY_LENGTH(&ictx->args) > 1) - return; - if (input_get_argument(ictx, 0, &n, 1) != 0) - return; - if (n == 0) - n = 1; - - screen_write_cursormove(&ictx->ctx, s->cx, n - 1); -} + struct screen_write_ctx *sctx = &ictx->ctx; + struct window_pane *wp = ictx->wp; + struct screen *s = sctx->s; -void -input_handle_sequence_hpa(struct input_ctx *ictx) -{ - struct screen *s = ictx->ctx.s; - uint16_t n; + log_debug("%s: '%c", __func__, ictx->ch); - if (ictx->private != '\0') - return; + switch (ictx->ch) { + case '\000': /* NUL */ + break; + case '\007': /* BEL */ + wp->window->flags |= WINDOW_BELL; + break; + case '\010': /* BS */ + screen_write_backspace(sctx); + break; + case '\011': /* HT */ + /* Don't tab beyond the end of the line. */ + if (s->cx >= screen_size_x(s) - 1) + break; - if (ARRAY_LENGTH(&ictx->args) > 1) - return; - if (input_get_argument(ictx, 0, &n, 1) != 0) - return; - if (n == 0) - n = 1; + /* Find the next tab point, or use the last column if none. */ + do { + s->cx++; + if (bit_test(s->tabs, s->cx)) + break; + } while (s->cx < screen_size_x(s) - 1); + break; + case '\012': /* LF */ + case '\013': /* VT */ + case '\014': /* FF */ + screen_write_linefeed(sctx, 0); + break; + case '\015': /* CR */ + screen_write_carriagereturn(sctx); + break; + case '\016': /* SO */ + ictx->cell.attr |= GRID_ATTR_CHARSET; + break; + case '\017': /* SI */ + ictx->cell.attr &= ~GRID_ATTR_CHARSET; + break; + default: + log_debug("%s: unknown '%c'", __func__, ictx->ch); + break; + } - screen_write_cursormove(&ictx->ctx, n - 1, s->cy); + return (0); } -void -input_handle_sequence_cup(struct input_ctx *ictx) +/* Execute escape sequence. */ +int +input_esc_dispatch(struct input_ctx *ictx) { - uint16_t n, m; + struct screen_write_ctx *sctx = &ictx->ctx; + struct screen *s = sctx->s; + struct input_table_entry *entry; - if (ictx->private != '\0') - return; + if (ictx->flags & INPUT_DISCARD) + return (0); + log_debug("%s: '%c', %s", __func__, ictx->ch, ictx->interm_buf); - if (ARRAY_LENGTH(&ictx->args) > 2) - return; - if (input_get_argument(ictx, 0, &n, 1) != 0) - return; - if (input_get_argument(ictx, 1, &m, 1) != 0) - return; - if (n == 0) - n = 1; - if (m == 0) - m = 1; + entry = bsearch(ictx, input_esc_table, nitems(input_esc_table), + sizeof input_esc_table[0], input_table_compare); + if (entry == NULL) { + log_debug("%s: unknown '%c'", __func__, ictx->ch); + return (0); + } - screen_write_cursormove(&ictx->ctx, m - 1, n - 1); -} + switch (entry->type) { + case INPUT_ESC_RIS: + memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell); + memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); + ictx->old_cx = 0; + ictx->old_cy = 0; -void -input_handle_sequence_tbc(struct input_ctx *ictx) -{ - struct screen *s = ictx->ctx.s; - uint16_t n; + screen_reset_tabs(sctx->s); - if (ictx->private != '\0') - return; + screen_write_scrollregion(sctx, 0, screen_size_y(sctx->s) - 1); - if (ARRAY_LENGTH(&ictx->args) > 1) - return; - if (input_get_argument(ictx, 0, &n, 1) != 0) - return; + screen_write_insertmode(sctx, 0); + screen_write_kcursormode(sctx, 0); + screen_write_kkeypadmode(sctx, 0); + screen_write_mousemode(sctx, 0); - switch (n) { - case 0: + screen_write_clearscreen(sctx); + screen_write_cursormove(sctx, 0, 0); + break; + case INPUT_ESC_IND: + screen_write_linefeed(sctx, 0); + break; + case INPUT_ESC_NEL: + screen_write_carriagereturn(sctx); + screen_write_linefeed(sctx, 0); + break; + case INPUT_ESC_HTS: if (s->cx < screen_size_x(s)) - bit_clear(s->tabs, s->cx); + bit_set(s->tabs, s->cx); break; - case 3: - bit_nclear(s->tabs, 0, screen_size_x(s) - 1); + case INPUT_ESC_RI: + screen_write_reverseindex(sctx); break; - } -} - -void -input_handle_sequence_ed(struct input_ctx *ictx) -{ - uint16_t n; - - if (ictx->private != '\0') - return; - - if (ARRAY_LENGTH(&ictx->args) > 1) - return; - if (input_get_argument(ictx, 0, &n, 0) != 0) - return; - if (n > 2) - return; - - switch (n) { - case 0: - screen_write_clearendofscreen(&ictx->ctx); + case INPUT_ESC_DECKPAM: + screen_write_kkeypadmode(sctx, 1); + break; + case INPUT_ESC_DECKPNM: + screen_write_kkeypadmode(sctx, 0); break; - case 1: - screen_write_clearstartofscreen(&ictx->ctx); + case INPUT_ESC_DECSC: + memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); + ictx->old_cx = s->cx; + ictx->old_cy = s->cy; break; - case 2: - screen_write_clearscreen(&ictx->ctx); + case INPUT_ESC_DECRC: + memcpy(&ictx->cell, &ictx->old_cell, sizeof ictx->cell); + screen_write_cursormove(sctx, ictx->old_cx, ictx->old_cy); + break; + case INPUT_ESC_DECALN: + screen_write_alignmenttest(sctx); + break; + case INPUT_ESC_SCSON_G0: + /* + * Not really supported, but fake it up enough for those that + * use it to switch character sets (by redefining G0 to + * graphics set, rather than switching to G1). + */ + ictx->cell.attr &= ~GRID_ATTR_CHARSET; + break; + case INPUT_ESC_SCSOFF_G0: + ictx->cell.attr |= GRID_ATTR_CHARSET; break; } + + return (0); } -void -input_handle_sequence_el(struct input_ctx *ictx) +/* Execute control sequence. */ +int +input_csi_dispatch(struct input_ctx *ictx) { - uint16_t n; + struct screen_write_ctx *sctx = &ictx->ctx; + struct window_pane *wp = ictx->wp; + struct screen *s = sctx->s; + struct input_table_entry *entry; + int n, m; - if (ictx->private != '\0') - return; + if (ictx->flags & INPUT_DISCARD) + return (0); + if (input_split(ictx) != 0) + return (0); + log_debug("%s: '%c' \"%s\" \"%s\"", + __func__, ictx->ch, ictx->interm_buf, ictx->param_buf); - if (ARRAY_LENGTH(&ictx->args) > 1) - return; - if (input_get_argument(ictx, 0, &n, 0) != 0) - return; - if (n > 2) - return; + entry = bsearch(ictx, input_csi_table, nitems(input_csi_table), + sizeof input_csi_table[0], input_table_compare); + if (entry == NULL) { + log_debug("%s: unknown '%c'", __func__, ictx->ch); + return (0); + } - switch (n) { - case 0: - screen_write_clearendofline(&ictx->ctx); + switch (entry->type) { + case INPUT_CSI_CBT: + /* Find the previous tab point, n times. */ + n = input_get(ictx, 0, 1, 1); + while (s->cx > 0 && n-- > 0) { + do + s->cx--; + while (s->cx > 0 && !bit_test(s->tabs, s->cx)); + } break; - case 1: - screen_write_clearstartofline(&ictx->ctx); + case INPUT_CSI_CUB: + screen_write_cursorleft(sctx, input_get(ictx, 0, 1, 1)); break; - case 2: - screen_write_clearline(&ictx->ctx); + case INPUT_CSI_CUD: + screen_write_cursordown(sctx, input_get(ictx, 0, 1, 1)); break; - } -} - -void -input_handle_sequence_sm(struct input_ctx *ictx) -{ - struct window_pane *wp = ictx->wp; - struct options *oo = &wp->window->options; - struct screen *s = &wp->base; - u_int sx, sy; - uint16_t n; - - if (ARRAY_LENGTH(&ictx->args) > 1) - return; - if (input_get_argument(ictx, 0, &n, 0) != 0) - return; - - if (ictx->private == '?') { - switch (n) { + case INPUT_CSI_CUF: + screen_write_cursorright(sctx, input_get(ictx, 0, 1, 1)); + break; + case INPUT_CSI_CUP: + n = input_get(ictx, 0, 1, 1); + m = input_get(ictx, 1, 1, 1); + screen_write_cursormove(sctx, m - 1, n - 1); + break; + case INPUT_CSI_CUU: + screen_write_cursorup(sctx, input_get(ictx, 0, 1, 1)); + break; + case INPUT_CSI_DA: + switch (input_get(ictx, 0, 0, 0)) { + case 0: + input_reply(ictx, "\033[?1;2c"); + break; + default: + log_debug("%s: unknown '%c'", __func__, ictx->ch); + break; + } + break; + case INPUT_CSI_DCH: + screen_write_deletecharacter(sctx, input_get(ictx, 0, 1, 1)); + break; + case INPUT_CSI_DECSTBM: + n = input_get(ictx, 0, 1, 1); + m = input_get(ictx, 1, 1, screen_size_y(s)); + screen_write_scrollregion(sctx, n - 1, m - 1); + break; + case INPUT_CSI_DL: + screen_write_deleteline(sctx, input_get(ictx, 0, 1, 1)); + break; + case INPUT_CSI_DSR: + switch (input_get(ictx, 0, 0, 0)) { + case 5: + input_reply(ictx, "\033[0n"); + break; + case 6: + input_reply(ictx, "\033[%u;%uR", s->cy + 1, s->cx + 1); + break; + default: + log_debug("%s: unknown '%c'", __func__, ictx->ch); + break; + } + break; + case INPUT_CSI_ED: + switch (input_get(ictx, 0, 0, 0)) { + case 0: + screen_write_clearendofscreen(sctx); + break; + case 1: + screen_write_clearstartofscreen(sctx); + break; + case 2: + screen_write_clearscreen(sctx); + break; + default: + log_debug("%s: unknown '%c'", __func__, ictx->ch); + break; + } + break; + case INPUT_CSI_EL: + switch (input_get(ictx, 0, 0, 0)) { + case 0: + screen_write_clearendofline(sctx); + break; + case 1: + screen_write_clearstartofline(sctx); + break; + case 2: + screen_write_clearline(sctx); + break; + default: + log_debug("%s: unknown '%c'", __func__, ictx->ch); + break; + } + break; + case INPUT_CSI_HPA: + n = input_get(ictx, 0, 1, 1); + screen_write_cursormove(sctx, n - 1, s->cy); + break; + case INPUT_CSI_ICH: + screen_write_insertcharacter(sctx, input_get(ictx, 0, 1, 1)); + break; + case INPUT_CSI_IL: + screen_write_insertline(sctx, input_get(ictx, 0, 1, 1)); + break; + case INPUT_CSI_RM: + switch (input_get(ictx, 0, 0, -1)) { + case 4: /* IRM */ + screen_write_insertmode(&ictx->ctx, 0); + break; + default: + log_debug("%s: unknown '%c'", __func__, ictx->ch); + break; + } + break; + case INPUT_CSI_RM_PRIVATE: + switch (input_get(ictx, 0, 0, -1)) { case 1: /* GATM */ - screen_write_kcursormode(&ictx->ctx, 1); - log_debug("kcursor on"); + screen_write_kcursormode(&ictx->ctx, 0); break; case 3: /* DECCOLM */ screen_write_cursormove(&ictx->ctx, 0, 0); screen_write_clearscreen(&ictx->ctx); break; case 25: /* TCEM */ - screen_write_cursormode(&ictx->ctx, 1); - log_debug("cursor on"); + screen_write_cursormode(&ictx->ctx, 0); break; case 1000: - screen_write_mousemode(&ictx->ctx, 1); - log_debug("mouse on"); + screen_write_mousemode(&ictx->ctx, 0); break; case 1049: - if (wp->saved_grid != NULL) - break; - if (!options_get_number(oo, "alternate-screen")) - break; - sx = screen_size_x(s); - sy = screen_size_y(s); - - /* - * Enter alternative screen mode. A copy of the visible - * screen is saved and the history is not updated - */ - - wp->saved_grid = grid_create(sx, sy, 0); - grid_duplicate_lines( - wp->saved_grid, 0, s->grid, screen_hsize(s), sy); - wp->saved_cx = s->cx; - wp->saved_cy = s->cy; - memcpy(&wp->saved_cell, - &ictx->cell, sizeof wp->saved_cell); - - grid_view_clear(s->grid, 0, 0, sx, sy); - - wp->base.grid->flags &= ~GRID_HISTORY; - - wp->flags |= PANE_REDRAW; + window_pane_alternate_off(wp, &ictx->cell); break; default: - log_debug("unknown SM [%hhu]: %u", ictx->private, n); + log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } - } else { - switch (n) { + break; + case INPUT_CSI_SGR: + input_csi_dispatch_sgr(ictx); + break; + case INPUT_CSI_SM: + switch (input_get(ictx, 0, 0, -1)) { case 4: /* IRM */ screen_write_insertmode(&ictx->ctx, 1); - log_debug("insert on"); - break; - case 34: - /* Cursor high visibility not supported. */ break; default: - log_debug("unknown SM [%hhu]: %u", ictx->private, n); + log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } - } -} - -void -input_handle_sequence_rm(struct input_ctx *ictx) -{ - struct window_pane *wp = ictx->wp; - struct options *oo = &wp->window->options; - struct screen *s = &wp->base; - u_int sx, sy; - uint16_t n; - - if (ARRAY_LENGTH(&ictx->args) > 1) - return; - if (input_get_argument(ictx, 0, &n, 0) != 0) - return; - - if (ictx->private == '?') { - switch (n) { + break; + case INPUT_CSI_SM_PRIVATE: + switch (input_get(ictx, 0, 0, -1)) { case 1: /* GATM */ - screen_write_kcursormode(&ictx->ctx, 0); - log_debug("kcursor off"); + screen_write_kcursormode(&ictx->ctx, 1); break; case 3: /* DECCOLM */ screen_write_cursormove(&ictx->ctx, 0, 0); screen_write_clearscreen(&ictx->ctx); break; case 25: /* TCEM */ - screen_write_cursormode(&ictx->ctx, 0); - log_debug("cursor off"); + screen_write_cursormode(&ictx->ctx, 1); break; case 1000: - screen_write_mousemode(&ictx->ctx, 0); - log_debug("mouse off"); + screen_write_mousemode(&ictx->ctx, 1); break; case 1049: - if (wp->saved_grid == NULL) - break; - if (!options_get_number(oo, "alternate-screen")) - break; - sx = screen_size_x(s); - sy = screen_size_y(s); - - /* - * Exit alternative screen mode and restore the copied - * grid. - */ - - /* - * If the current size is bigger, temporarily resize - * to the old size before copying back. - */ - if (sy > wp->saved_grid->sy) - screen_resize(s, sx, wp->saved_grid->sy); - - /* Restore the grid, cursor position and cell. */ - grid_duplicate_lines( - s->grid, screen_hsize(s), wp->saved_grid, 0, sy); - s->cx = wp->saved_cx; - if (s->cx > screen_size_x(s) - 1) - s->cx = screen_size_x(s) - 1; - s->cy = wp->saved_cy; - if (s->cy > screen_size_y(s) - 1) - s->cy = screen_size_y(s) - 1; - memcpy(&ictx->cell, &wp->saved_cell, sizeof ictx->cell); - - /* - * Turn history back on (so resize can use it) and then - * resize back to the current size. - */ - wp->base.grid->flags |= GRID_HISTORY; - if (sy > wp->saved_grid->sy) - screen_resize(s, sx, sy); - - grid_destroy(wp->saved_grid); - wp->saved_grid = NULL; - - wp->flags |= PANE_REDRAW; + window_pane_alternate_on(wp, &ictx->cell); break; default: - log_debug("unknown RM [%hhu]: %u", ictx->private, n); + log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } - } else if (ictx->private == '\0') { - switch (n) { - case 4: /* IRM */ - screen_write_insertmode(&ictx->ctx, 0); - log_debug("insert off"); + break; + case INPUT_CSI_TBC: + switch (input_get(ictx, 0, 0, 0)) { + case 0: + if (s->cx < screen_size_x(s)) + bit_clear(s->tabs, s->cx); break; - case 34: - /* Cursor high visibility not supported. */ + case 3: + bit_nclear(s->tabs, 0, screen_size_x(s) - 1); break; default: - log_debug("unknown RM [%hhu]: %u", ictx->private, n); - break; - } - } -} - -void -input_handle_sequence_dsr(struct input_ctx *ictx) -{ - struct window_pane *wp = ictx->wp; - struct screen *s = ictx->ctx.s; - uint16_t n; - char reply[32]; - - if (ARRAY_LENGTH(&ictx->args) > 1) - return; - if (input_get_argument(ictx, 0, &n, 0) != 0) - return; - - if (ictx->private == '\0') { - switch (n) { - case 6: /* cursor position */ - xsnprintf(reply, sizeof reply, - "\033[%u;%uR", s->cy + 1, s->cx + 1); - log_debug("cursor request, reply: %s", reply); - bufferevent_write(wp->event, reply, strlen(reply)); + log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } + break; + case INPUT_CSI_VPA: + n = input_get(ictx, 0, 1, 1); + screen_write_cursormove(sctx, s->cx, n - 1); + break; } -} - -void -input_handle_sequence_decstbm(struct input_ctx *ictx) -{ - struct screen *s = ictx->ctx.s; - uint16_t n, m; - - if (ictx->private != '\0') - return; - - if (ARRAY_LENGTH(&ictx->args) > 2) - return; - if (input_get_argument(ictx, 0, &n, 0) != 0) - return; - if (input_get_argument(ictx, 1, &m, 0) != 0) - return; - if (n == 0) - n = 1; - if (m == 0) - m = screen_size_y(s); - screen_write_scrollregion(&ictx->ctx, n - 1, m - 1); + return (0); } +/* Handle CSI SGR. */ void -input_handle_sequence_sgr(struct input_ctx *ictx) +input_csi_dispatch_sgr(struct input_ctx *ictx) { - struct grid_cell *gc = &ictx->cell; - u_int i; - uint16_t m, o; - u_char attr; + struct grid_cell *gc = &ictx->cell; + u_int i; + int n, m; + u_char attr; - if (ARRAY_LENGTH(&ictx->args) == 0) { + if (ictx->param_list_len == 0) { attr = gc->attr; memcpy(gc, &grid_default_cell, sizeof *gc); gc->attr |= (attr & GRID_ATTR_CHARSET); return; } - for (i = 0; i < ARRAY_LENGTH(&ictx->args); i++) { - if (input_get_argument(ictx, i, &m, 0) != 0) - return; + for (i = 0; i < ictx->param_list_len; i++) { + n = input_get(ictx, i, 0, 0); - if (m == 38 || m == 48) { + if (n == 38 || n == 48) { i++; - if (input_get_argument(ictx, i, &o, 0) != 0) - return; - if (o != 5) + if (input_get(ictx, i, 0, -1) != 5) continue; i++; - if (input_get_argument(ictx, i, &o, 0) != 0) - return; - if (m == 38) { - gc->flags |= GRID_FLAG_FG256; - gc->fg = o; - } else if (m == 48) { - gc->flags |= GRID_FLAG_BG256; - gc->bg = o; + m = input_get(ictx, i, 0, -1); + if (m == -1) { + if (n == 38) { + gc->flags &= ~GRID_FLAG_FG256; + gc->fg = 8; + } else if (n == 48) { + gc->flags &= ~GRID_FLAG_BG256; + gc->fg = 8; + } + + } else { + if (n == 38) { + gc->flags |= GRID_FLAG_FG256; + gc->fg = m; + } else if (n == 48) { + gc->flags |= GRID_FLAG_BG256; + gc->bg = m; + } } continue; } - switch (m) { + switch (n) { case 0: case 10: attr = gc->attr; @@ -1458,7 +1322,7 @@ input_handle_sequence_sgr(struct input_ctx *ictx) case 36: case 37: gc->flags &= ~GRID_FLAG_FG256; - gc->fg = m - 30; + gc->fg = n - 30; break; case 39: gc->flags &= ~GRID_FLAG_FG256; @@ -1473,7 +1337,7 @@ input_handle_sequence_sgr(struct input_ctx *ictx) case 46: case 47: gc->flags &= ~GRID_FLAG_BG256; - gc->bg = m - 40; + gc->bg = n - 40; break; case 49: gc->flags &= ~GRID_FLAG_BG256; @@ -1488,7 +1352,7 @@ input_handle_sequence_sgr(struct input_ctx *ictx) case 96: case 97: gc->flags &= ~GRID_FLAG_FG256; - gc->fg = m; + gc->fg = n; break; case 100: case 101: @@ -1499,8 +1363,135 @@ input_handle_sequence_sgr(struct input_ctx *ictx) case 106: case 107: gc->flags &= ~GRID_FLAG_BG256; - gc->bg = m; + gc->bg = n; break; } } } + +/* DCS string started. */ +void +input_enter_dcs(struct input_ctx *ictx) +{ + log_debug("%s", __func__); + + ictx->input_len = 0; +} + +/* DCS terminator (ST) received. */ +void +input_exit_dcs(unused struct input_ctx *ictx) +{ + log_debug("%s", __func__); +} + +/* OSC string started. */ +void +input_enter_osc(struct input_ctx *ictx) +{ + log_debug("%s", __func__); + + ictx->input_len = 0; +} + +/* OSC terminator (ST) received. */ +void +input_exit_osc(struct input_ctx *ictx) +{ + if (ictx->flags & INPUT_DISCARD) + return; + log_debug("%s: \"%s\"", __func__, ictx->input_buf); + + if (ictx->input_len < 2 || ictx->input_buf[1] != ';') + return; + if (ictx->input_buf[0] != '0' && ictx->input_buf[0] != '2') + return; + + screen_set_title(ictx->ctx.s, ictx->input_buf + 2); + server_status_window(ictx->wp->window); +} + +/* APC string started. */ +void +input_enter_apc(struct input_ctx *ictx) +{ + log_debug("%s", __func__); + + ictx->input_len = 0; +} + +/* APC terminator (ST) received. */ +void +input_exit_apc(struct input_ctx *ictx) +{ + if (ictx->flags & INPUT_DISCARD) + return; + log_debug("%s: \"%s\"", __func__, ictx->input_buf); + + screen_set_title(ictx->ctx.s, ictx->input_buf); + server_status_window(ictx->wp->window); +} + +/* Rename string started. */ +void +input_enter_rename(struct input_ctx *ictx) +{ + log_debug("%s", __func__); + + ictx->input_len = 0; +} + +/* Rename terminator (ST) received. */ +void +input_exit_rename(struct input_ctx *ictx) +{ + if (ictx->flags & INPUT_DISCARD) + return; + log_debug("%s: \"%s\"", __func__, ictx->input_buf); + + xfree(ictx->wp->window->name); + ictx->wp->window->name = xstrdup(ictx->input_buf); + options_set_number(&ictx->wp->window->options, "automatic-rename", 0); + + server_status_window(ictx->wp->window); +} + +/* Open UTF-8 character. */ +int +input_utf8_open(struct input_ctx *ictx) +{ + if (!options_get_number(&ictx->wp->window->options, "utf8")) { + /* Print, and do not switch state. */ + input_print(ictx); + return (-1); + } + log_debug("%s", __func__); + + utf8_open(&ictx->utf8data, ictx->ch); + return (0); +} + +/* Append to UTF-8 character. */ +int +input_utf8_add(struct input_ctx *ictx) +{ + log_debug("%s", __func__); + + utf8_append(&ictx->utf8data, ictx->ch); + return (0); +} + +/* Close UTF-8 string. */ +int +input_utf8_close(struct input_ctx *ictx) +{ + log_debug("%s", __func__); + + utf8_append(&ictx->utf8data, ictx->ch); + + ictx->cell.flags |= GRID_FLAG_UTF8; + screen_write_cell(&ictx->ctx, &ictx->cell, &ictx->utf8data); + ictx->cell.flags &= ~GRID_FLAG_UTF8; + + return (0); +} diff --git a/usr.bin/tmux/tmux.h b/usr.bin/tmux/tmux.h index 3104133a0c3..d2f4ec3bcbc 100644 --- a/usr.bin/tmux/tmux.h +++ b/usr.bin/tmux/tmux.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tmux.h,v 1.209 2010/03/02 00:32:41 nicm Exp $ */ +/* $OpenBSD: tmux.h,v 1.210 2010/03/22 19:02:54 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> @@ -719,43 +719,36 @@ struct screen_write_ctx { #define screen_hsize(s) ((s)->grid->hsize) #define screen_hlimit(s) ((s)->grid->hlimit) -/* Input parser sequence argument. */ -struct input_arg { - u_char data[64]; - size_t used; -}; - /* Input parser context. */ struct input_ctx { - struct window_pane *wp; + struct window_pane *wp; struct screen_write_ctx ctx; - u_char *buf; - size_t len; - size_t off; - size_t was; + struct grid_cell cell; - struct grid_cell cell; + struct grid_cell old_cell; + u_int old_cx; + u_int old_cy; - struct grid_cell saved_cell; - u_int saved_cx; - u_int saved_cy; + u_char interm_buf[4]; + size_t interm_len; + + u_char param_buf[64]; + size_t param_len; + + u_char input_buf[256]; + size_t input_len; -#define MAXSTRINGLEN 1024 - u_char *string_buf; - size_t string_len; - int string_type; -#define STRING_SYSTEM 0 -#define STRING_APPLICATION 1 -#define STRING_NAME 2 + int param_list[24]; /* -1 not present */ + u_int param_list_len; - struct utf8_data utf8data; + struct utf8_data utf8data; - u_char intermediate; - void *(*state)(u_char, struct input_ctx *); + int ch; + int flags; +#define INPUT_DISCARD 0x1 - u_char private; - ARRAY_DECL(, struct input_arg) args; + const struct input_state *state; }; /* @@ -1825,6 +1818,10 @@ int window_pane_spawn(struct window_pane *, const char *, const char *, const char *, struct environ *, struct termios *, char **); void window_pane_resize(struct window_pane *, u_int, u_int); +void window_pane_alternate_on( + struct window_pane *, struct grid_cell *); +void window_pane_alternate_off( + struct window_pane *, struct grid_cell *); int window_pane_set_mode( struct window_pane *, const struct window_mode *); void window_pane_reset_mode(struct window_pane *); diff --git a/usr.bin/tmux/window.c b/usr.bin/tmux/window.c index 128ff27c1a9..10e99070e6a 100644 --- a/usr.bin/tmux/window.c +++ b/usr.bin/tmux/window.c @@ -1,4 +1,4 @@ -/* $OpenBSD: window.c,v 1.43 2010/02/06 17:15:33 nicm Exp $ */ +/* $OpenBSD: window.c,v 1.44 2010/03/22 19:02:54 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> @@ -623,6 +623,81 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) fatal("ioctl failed"); } +/* + * Enter alternative screen mode. A copy of the visible screen is saved and the + * history is not updated + */ +void +window_pane_alternate_on(struct window_pane *wp, struct grid_cell *gc) +{ + struct screen *s = &wp->base; + u_int sx, sy; + + if (wp->saved_grid != NULL) + return; + if (!options_get_number(&wp->window->options, "alternate-screen")) + return; + sx = screen_size_x(s); + sy = screen_size_y(s); + + wp->saved_grid = grid_create(sx, sy, 0); + grid_duplicate_lines(wp->saved_grid, 0, s->grid, screen_hsize(s), sy); + wp->saved_cx = s->cx; + wp->saved_cy = s->cy; + memcpy(&wp->saved_cell, gc, sizeof wp->saved_cell); + + grid_view_clear(s->grid, 0, 0, sx, sy); + + wp->base.grid->flags &= ~GRID_HISTORY; + + wp->flags |= PANE_REDRAW; +} + +/* Exit alternate screen mode and restore the copied grid. */ +void +window_pane_alternate_off(struct window_pane *wp, struct grid_cell *gc) +{ + struct screen *s = &wp->base; + u_int sx, sy; + + if (wp->saved_grid == NULL) + return; + if (!options_get_number(&wp->window->options, "alternate-screen")) + return; + sx = screen_size_x(s); + sy = screen_size_y(s); + + /* + * If the current size is bigger, temporarily resize to the old size + * before copying back. + */ + if (sy > wp->saved_grid->sy) + screen_resize(s, sx, wp->saved_grid->sy); + + /* Restore the grid, cursor position and cell. */ + grid_duplicate_lines(s->grid, screen_hsize(s), wp->saved_grid, 0, sy); + s->cx = wp->saved_cx; + if (s->cx > screen_size_x(s) - 1) + s->cx = screen_size_x(s) - 1; + s->cy = wp->saved_cy; + if (s->cy > screen_size_y(s) - 1) + s->cy = screen_size_y(s) - 1; + memcpy(gc, &wp->saved_cell, sizeof *gc); + + /* + * Turn history back on (so resize can use it) and then resize back to + * the current size. + */ + wp->base.grid->flags |= GRID_HISTORY; + if (sy > wp->saved_grid->sy) + screen_resize(s, sx, sy); + + grid_destroy(wp->saved_grid); + wp->saved_grid = NULL; + + wp->flags |= PANE_REDRAW; +} + int window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode) { |