| /* $OpenBSD$ */ |
| |
| /* |
| * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> |
| * |
| * Permission to use, copy, modify, and distribute this software for any |
| * purpose with or without fee is hereby granted, provided that the above |
| * copyright notice and this permission notice appear in all copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER |
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
| * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| #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 }, |
| }; |
| |
| 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; |
| |
| return (ai - bi); |
| } |
| |
| void |
| input_new_argument(struct input_ctx *ictx) |
| { |
| struct input_arg *arg; |
| |
| ARRAY_EXPAND(&ictx->args, 1); |
| |
| arg = &ARRAY_LAST(&ictx->args); |
| arg->used = 0; |
| } |
| |
| int |
| input_add_argument(struct input_ctx *ictx, u_char ch) |
| { |
| struct input_arg *arg; |
| |
| if (ARRAY_LENGTH(&ictx->args) == 0) |
| return (0); |
| |
| arg = &ARRAY_LAST(&ictx->args); |
| if (arg->used > (sizeof arg->data) - 1) |
| return (-1); |
| arg->data[arg->used++] = ch; |
| |
| return (0); |
| } |
| |
| int |
| input_get_argument(struct input_ctx *ictx, u_int i, uint16_t *n, uint16_t d) |
| { |
| struct input_arg *arg; |
| const char *errstr; |
| |
| *n = d; |
| if (i >= ARRAY_LENGTH(&ictx->args)) |
| return (0); |
| |
| arg = &ARRAY_ITEM(&ictx->args, i); |
| if (*arg->data == '\0') |
| return (0); |
| |
| *n = strtonum(arg->data, 0, UINT16_MAX, &errstr); |
| if (errstr != NULL) |
| return (-1); |
| return (0); |
| } |
| |
| void |
| input_start_string(struct input_ctx *ictx, int type) |
| { |
| ictx->string_type = type; |
| ictx->string_len = 0; |
| } |
| |
| void |
| input_abort_string(struct input_ctx *ictx) |
| { |
| if (ictx->string_buf != NULL) |
| xfree(ictx->string_buf); |
| ictx->string_buf = NULL; |
| } |
| |
| 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; |
| |
| if (ictx->string_len >= MAXSTRINGLEN) { |
| input_abort_string(ictx); |
| return (1); |
| } |
| |
| return (0); |
| } |
| |
| char * |
| input_get_string(struct input_ctx *ictx) |
| { |
| char *s; |
| |
| if (ictx->string_buf == NULL || input_add_string(ictx, '\0') != 0) |
| return (xstrdup("")); |
| |
| s = ictx->string_buf; |
| ictx->string_buf = NULL; |
| return (s); |
| } |
| |
| void |
| input_state(struct input_ctx *ictx, void *state) |
| { |
| ictx->state = state; |
| } |
| |
| void |
| input_init(struct window_pane *wp) |
| { |
| struct input_ctx *ictx = &wp->ictx; |
| |
| ARRAY_INIT(&ictx->args); |
| |
| ictx->string_len = 0; |
| ictx->string_buf = NULL; |
| |
| memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell); |
| |
| memcpy(&ictx->saved_cell, &grid_default_cell, sizeof ictx->saved_cell); |
| ictx->saved_cx = 0; |
| ictx->saved_cy = 0; |
| |
| input_state(ictx, input_state_first); |
| |
| ictx->was = 0; |
| } |
| |
| void |
| input_free(struct window_pane *wp) |
| { |
| if (wp->ictx.string_buf != NULL) |
| xfree(wp->ictx.string_buf); |
| |
| ARRAY_FREE(&wp->ictx.args); |
| } |
| |
| void |
| input_parse(struct window_pane *wp) |
| { |
| struct input_ctx *ictx = &wp->ictx; |
| u_char ch; |
| |
| if (BUFFER_USED(wp->in) == ictx->was) |
| return; |
| wp->window->flags |= WINDOW_ACTIVITY; |
| |
| ictx->buf = BUFFER_OUT(wp->in); |
| ictx->len = BUFFER_USED(wp->in); |
| ictx->off = 0; |
| |
| ictx->wp = wp; |
| |
| if (wp->mode == NULL) |
| screen_write_start(&ictx->ctx, wp, &wp->base); |
| else |
| screen_write_start(&ictx->ctx, NULL, &wp->base); |
| |
| while (ictx->off < ictx->len) { |
| ch = ictx->buf[ictx->off++]; |
| ictx->state(ch, ictx); |
| } |
| |
| screen_write_stop(&ictx->ctx); |
| |
| buffer_remove(wp->in, ictx->len); |
| ictx->was = BUFFER_USED(wp->in); |
| } |
| |
| void |
| input_state_first(u_char ch, struct input_ctx *ictx) |
| { |
| ictx->intermediate = '\0'; |
| |
| if (INPUT_C0CONTROL(ch)) { |
| if (ch == 0x1b) |
| input_state(ictx, input_state_escape); |
| else |
| input_handle_c0_control(ch, ictx); |
| return; |
| } |
| |
| #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 |
| |
| if (INPUT_DELETE(ch)) |
| return; |
| |
| input_handle_character(ch, ictx); |
| } |
| |
| 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; |
| |
| if (INPUT_C0CONTROL(ch)) { |
| input_handle_c0_control(ch, ictx); |
| return; |
| } |
| |
| if (INPUT_INTERMEDIATE(ch)) { |
| log_debug2(":: in1 %zu: %hhu (%c)", ictx->off, ch, ch); |
| ictx->intermediate = ch; |
| input_state(ictx, input_state_intermediate); |
| return; |
| } |
| |
| if (INPUT_PARAMETER(ch)) { |
| input_state(ictx, input_state_first); |
| input_handle_private_two(ch, ictx); |
| return; |
| } |
| |
| 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; |
| } |
| |
| if (INPUT_LOWERCASE(ch)) { |
| input_state(ictx, input_state_first); |
| input_handle_standard_two(ch, ictx); |
| return; |
| } |
| |
| input_state(ictx, input_state_first); |
| } |
| |
| 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; |
| } |
| |
| if (INPUT_PARAMETER(ch)) { |
| input_state(ictx, input_state_first); |
| input_handle_private_two(ch, ictx); |
| return; |
| } |
| |
| if (INPUT_UPPERCASE(ch) || INPUT_LOWERCASE(ch)) { |
| input_state(ictx, input_state_first); |
| input_handle_standard_two(ch, ictx); |
| return; |
| } |
| |
| input_state(ictx, input_state_first); |
| } |
| |
| 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; |
| } |
| |
| /* Handle C0 immediately. */ |
| input_handle_c0_control(ch, ictx); |
| |
| /* |
| * Just come back to this state, in case the next character |
| * is the start of a private sequence. |
| */ |
| return; |
| } |
| |
| input_state(ictx, input_state_sequence_next); |
| |
| /* Private sequence: always the first character. */ |
| if (ch >= 0x3c && ch <= 0x3f) { |
| ictx->private = ch; |
| return; |
| } |
| |
| /* Pass character on directly. */ |
| input_state_sequence_next(ch, ictx); |
| } |
| |
| 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; |
| } |
| |
| 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; |
| } |
| |
| 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; |
| } |
| |
| /* 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; |
| } |
| |
| /* Handle C0 immediately. */ |
| input_handle_c0_control(ch, ictx); |
| |
| return; |
| } |
| |
| input_state(ictx, input_state_first); |
| } |
| |
| 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; |
| } |
| |
| input_state(ictx, input_state_first); |
| } |
| |
| 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; |
| } |
| |
| if (ch >= 0x20) { |
| if (input_add_string(ictx, ch) != 0) |
| input_state(ictx, input_state_first); |
| return; |
| } |
| } |
| |
| void |
| input_state_string_escape(u_char ch, struct input_ctx *ictx) |
| { |
| 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; |
| } |
| |
| input_state(ictx, input_state_string_next); |
| input_state_string_next(ch, ictx); |
| } |
| |
| void |
| input_state_utf8(u_char ch, struct input_ctx *ictx) |
| { |
| log_debug2("-- un %zu: %hhu (%c)", ictx->off, ch, ch); |
| |
| ictx->utf8_buf[ictx->utf8_off++] = ch; |
| if (--ictx->utf8_len != 0) |
| return; |
| input_state(ictx, input_state_first); |
| |
| ictx->cell.flags |= GRID_FLAG_UTF8; |
| screen_write_cell(&ictx->ctx, &ictx->cell, ictx->utf8_buf); |
| ictx->cell.flags &= ~GRID_FLAG_UTF8; |
| } |
| |
| void |
| input_handle_character(u_char ch, struct input_ctx *ictx) |
| { |
| struct window_pane *wp = ictx->wp; |
| |
| if (ch > 0x7f && options_get_number(&wp->window->options, "utf8")) { |
| /* |
| * UTF-8 sequence. |
| * |
| * 11000010-11011111 C2-DF start of 2-byte sequence |
| * 11100000-11101111 E0-EF start of 3-byte sequence |
| * 11110000-11110100 F0-F4 start of 4-byte sequence |
| */ |
| memset(ictx->utf8_buf, 0xff, sizeof ictx->utf8_buf); |
| ictx->utf8_buf[0] = ch; |
| ictx->utf8_off = 1; |
| |
| if (ch >= 0xc2 && ch <= 0xdf) { |
| log_debug2("-- u2 %zu: %hhu (%c)", ictx->off, ch, ch); |
| input_state(ictx, input_state_utf8); |
| ictx->utf8_len = 1; |
| return; |
| } |
| if (ch >= 0xe0 && ch <= 0xef) { |
| log_debug2("-- u3 %zu: %hhu (%c)", ictx->off, ch, ch); |
| input_state(ictx, input_state_utf8); |
| ictx->utf8_len = 2; |
| return; |
| } |
| if (ch >= 0xf0 && ch <= 0xf4) { |
| log_debug2("-- u4 %zu: %hhu (%c)", ictx->off, ch, ch); |
| input_state(ictx, input_state_utf8); |
| ictx->utf8_len = 3; |
| return; |
| } |
| } |
| log_debug2("-- ch %zu: %hhu (%c)", ictx->off, ch, ch); |
| |
| ictx->cell.data = ch; |
| screen_write_cell(&ictx->ctx, &ictx->cell, ictx->utf8_buf); |
| } |
| |
| void |
| input_handle_c0_control(u_char ch, struct input_ctx *ictx) |
| { |
| 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; |
| } |
| } |
| |
| void |
| input_handle_c1_control(u_char ch, struct input_ctx *ictx) |
| { |
| struct screen *s = ictx->ctx.s; |
| |
| log_debug2("-- c1 %zu: %hhu (%c)", ictx->off, ch, ch); |
| |
| 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; |
| } |
| } |
| |
| void |
| input_handle_private_two(u_char ch, struct input_ctx *ictx) |
| { |
| struct screen *s = ictx->ctx.s; |
| |
| log_debug2( |
| "-- p2 %zu: %hhu (%c) %hhu", ictx->off, ch, ch, ictx->intermediate); |
| |
| 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; |
| } |
| 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; |
| } |
| 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). |
| */ |
| 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); |
| |
| 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; |
| } |
| } |
| |
| 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); |
| } |
| |
| 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); |
| } |
| |
| 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); |
| } |
| |
| 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; |
| |
| screen_write_cursordown(&ictx->ctx, n); |
| } |
| |
| void |
| input_handle_sequence_cuf(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_cursorright(&ictx->ctx, n); |
| } |
| |
| void |
| input_handle_sequence_cub(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_cursorleft(&ictx->ctx, n); |
| } |
| |
| void |
| input_handle_sequence_dch(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_deletecharacter(&ictx->ctx, n); |
| } |
| |
| void |
| input_handle_sequence_cbt(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; |
| |
| /* 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)); |
| } |
| } |
| |
| void |
| input_handle_sequence_da(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 != 0) |
| return; |
| |
| buffer_write(ictx->wp->out, "\033[?1;2c", (sizeof "\033[?1;2c") - 1); |
| } |
| |
| void |
| input_handle_sequence_dl(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_deleteline(&ictx->ctx, n); |
| } |
| |
| void |
| input_handle_sequence_ich(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_insertcharacter(&ictx->ctx, n); |
| } |
| |
| void |
| input_handle_sequence_il(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_insertline(&ictx->ctx, n); |
| } |
| |
| void |
| input_handle_sequence_vpa(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); |
| } |
| |
| void |
| input_handle_sequence_hpa(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, n - 1, s->cy); |
| } |
| |
| void |
| input_handle_sequence_cup(struct input_ctx *ictx) |
| { |
| uint16_t n, m; |
| |
| if (ictx->private != '\0') |
| return; |
| |
| 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; |
| |
| screen_write_cursormove(&ictx->ctx, m - 1, n - 1); |
| } |
| |
| void |
| input_handle_sequence_tbc(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; |
| |
| switch (n) { |
| case 0: |
| if (s->cx < screen_size_x(s)) |
| bit_clear(s->tabs, s->cx); |
| break; |
| case 3: |
| bit_nclear(s->tabs, 0, screen_size_x(s) - 1); |
| 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); |
| break; |
| case 1: |
| screen_write_clearstartofscreen(&ictx->ctx); |
| break; |
| case 2: |
| screen_write_clearscreen(&ictx->ctx); |
| break; |
| } |
| } |
| |
| void |
| input_handle_sequence_el(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_clearendofline(&ictx->ctx); |
| break; |
| case 1: |
| screen_write_clearstartofline(&ictx->ctx); |
| break; |
| case 2: |
| screen_write_clearline(&ictx->ctx); |
| break; |
| } |
| } |
| |
| void |
| input_handle_sequence_sm(struct input_ctx *ictx) |
| { |
| struct window_pane *wp = ictx->wp; |
| 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 1: /* GATM */ |
| screen_write_kcursormode(&ictx->ctx, 1); |
| log_debug("kcursor on"); |
| break; |
| case 25: /* TCEM */ |
| screen_write_cursormode(&ictx->ctx, 1); |
| log_debug("cursor on"); |
| break; |
| case 1000: |
| screen_write_mousemode(&ictx->ctx, 1); |
| log_debug("mouse on"); |
| break; |
| case 1049: |
| if (wp->saved_grid != NULL) |
| 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; |
| break; |
| default: |
| log_debug("unknown SM [%hhu]: %u", ictx->private, n); |
| break; |
| } |
| } else { |
| switch (n) { |
| 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); |
| break; |
| } |
| } |
| } |
| |
| void |
| input_handle_sequence_rm(struct input_ctx *ictx) |
| { |
| struct window_pane *wp = ictx->wp; |
| 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 1: /* GATM */ |
| screen_write_kcursormode(&ictx->ctx, 0); |
| log_debug("kcursor off"); |
| break; |
| case 25: /* TCEM */ |
| screen_write_cursormode(&ictx->ctx, 0); |
| log_debug("cursor off"); |
| break; |
| case 1000: |
| screen_write_mousemode(&ictx->ctx, 0); |
| log_debug("mouse off"); |
| break; |
| case 1049: |
| if (wp->saved_grid == NULL) |
| 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; |
| break; |
| default: |
| log_debug("unknown RM [%hhu]: %u", ictx->private, n); |
| break; |
| } |
| } else if (ictx->private == '\0') { |
| switch (n) { |
| case 4: /* IRM */ |
| screen_write_insertmode(&ictx->ctx, 0); |
| log_debug("insert off"); |
| break; |
| case 34: |
| /* Cursor high visibility not supported. */ |
| break; |
| default: |
| log_debug("unknown RM [%hhu]: %u", ictx->private, n); |
| break; |
| } |
| } |
| } |
| |
| void |
| input_handle_sequence_dsr(struct input_ctx *ictx) |
| { |
| 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); |
| buffer_write(ictx->wp->out, reply, strlen(reply)); |
| 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); |
| } |
| |
| void |
| input_handle_sequence_sgr(struct input_ctx *ictx) |
| { |
| struct grid_cell *gc = &ictx->cell; |
| u_int i; |
| uint16_t m, o; |
| u_char attr; |
| |
| if (ARRAY_LENGTH(&ictx->args) == 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; |
| |
| if (m == 38 || m == 48) { |
| i++; |
| if (input_get_argument(ictx, i, &o, 0) != 0) |
| return; |
| if (o != 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; |
| } |
| continue; |
| } |
| |
| switch (m) { |
| case 0: |
| case 10: |
| attr = gc->attr; |
| memcpy(gc, &grid_default_cell, sizeof *gc); |
| gc->attr |= (attr & GRID_ATTR_CHARSET); |
| break; |
| case 1: |
| gc->attr |= GRID_ATTR_BRIGHT; |
| break; |
| case 2: |
| gc->attr |= GRID_ATTR_DIM; |
| break; |
| case 3: |
| gc->attr |= GRID_ATTR_ITALICS; |
| break; |
| case 4: |
| gc->attr |= GRID_ATTR_UNDERSCORE; |
| break; |
| case 5: |
| gc->attr |= GRID_ATTR_BLINK; |
| break; |
| case 7: |
| gc->attr |= GRID_ATTR_REVERSE; |
| break; |
| case 8: |
| gc->attr |= GRID_ATTR_HIDDEN; |
| break; |
| case 22: |
| gc->attr &= ~(GRID_ATTR_BRIGHT|GRID_ATTR_DIM); |
| break; |
| case 23: |
| gc->attr &= ~GRID_ATTR_ITALICS; |
| break; |
| case 24: |
| gc->attr &= ~GRID_ATTR_UNDERSCORE; |
| break; |
| case 25: |
| gc->attr &= ~GRID_ATTR_BLINK; |
| break; |
| case 27: |
| gc->attr &= ~GRID_ATTR_REVERSE; |
| break; |
| case 30: |
| case 31: |
| case 32: |
| case 33: |
| case 34: |
| case 35: |
| case 36: |
| case 37: |
| gc->flags &= ~GRID_FLAG_FG256; |
| gc->fg = m - 30; |
| break; |
| case 39: |
| gc->flags &= ~GRID_FLAG_FG256; |
| gc->fg = 8; |
| break; |
| case 40: |
| case 41: |
| case 42: |
| case 43: |
| case 44: |
| case 45: |
| case 46: |
| case 47: |
| gc->flags &= ~GRID_FLAG_BG256; |
| gc->bg = m - 40; |
| break; |
| case 49: |
| gc->flags &= ~GRID_FLAG_BG256; |
| gc->bg = 8; |
| break; |
| } |
| } |
| } |