tmux/input.c
Nicholas Marriott 8608c6970d When backspace is received at the beginning of a line and the previous line was
wrapped, move the cursor back up to the end of the previous line.

Another one of the forgotten persons requested this quite a while ago (I need
to start noting names on todo items...) when it was quite hard to
implement. Now it is easy and I don't see it can do any harm, so hey presto...
2009-10-12 16:59:55 +00:00

1492 lines
32 KiB
C

/* $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;
}
}
}