mirror of
https://github.com/tmate-io/tmate.git
synced 2024-12-25 08:09:24 +01:00
1651 lines
39 KiB
C
1651 lines
39 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 <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "tmux.h"
|
|
|
|
/*
|
|
* 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.
|
|
*
|
|
* - Special handling for ESC inside a DCS to allow arbitrary byte sequences to
|
|
* be passed to the underlying teminal(s).
|
|
*/
|
|
|
|
/* Helper functions. */
|
|
struct input_transition;
|
|
int input_split(struct input_ctx *);
|
|
int input_get(struct input_ctx *, u_int, int, int);
|
|
void input_reply(struct input_ctx *, const char *, ...);
|
|
void input_set_state(struct window_pane *, const struct input_transition *);
|
|
|
|
/* Transition entry/exit handlers. */
|
|
void input_clear(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_dcs_dispatch(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;
|
|
};
|
|
|
|
/* 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,
|
|
};
|
|
|
|
/* 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 },
|
|
};
|
|
|
|
/* Control (CSI) commands. */
|
|
enum input_csi_type {
|
|
INPUT_CSI_CBT,
|
|
INPUT_CSI_CNL,
|
|
INPUT_CSI_CPL,
|
|
INPUT_CSI_CUB,
|
|
INPUT_CSI_CUD,
|
|
INPUT_CSI_CUF,
|
|
INPUT_CSI_CUP,
|
|
INPUT_CSI_CUU,
|
|
INPUT_CSI_DA,
|
|
INPUT_CSI_DA_TWO,
|
|
INPUT_CSI_DCH,
|
|
INPUT_CSI_DECSCUSR,
|
|
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_RCP,
|
|
INPUT_CSI_RM,
|
|
INPUT_CSI_RM_PRIVATE,
|
|
INPUT_CSI_SCP,
|
|
INPUT_CSI_SGR,
|
|
INPUT_CSI_SM,
|
|
INPUT_CSI_SM_PRIVATE,
|
|
INPUT_CSI_TBC,
|
|
INPUT_CSI_VPA,
|
|
};
|
|
|
|
/* 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 },
|
|
{ 'E', "", INPUT_CSI_CNL },
|
|
{ 'F', "", INPUT_CSI_CPL },
|
|
{ '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 },
|
|
{ 'c', ">", INPUT_CSI_DA_TWO },
|
|
{ '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 },
|
|
{ 'q', " ", INPUT_CSI_DECSCUSR },
|
|
{ 'r', "", INPUT_CSI_DECSTBM },
|
|
{ 's', "", INPUT_CSI_SCP },
|
|
{ 'u', "", INPUT_CSI_RCP },
|
|
};
|
|
|
|
/* Input transition. */
|
|
struct input_transition {
|
|
int first;
|
|
int last;
|
|
|
|
int (*handler)(struct input_ctx *);
|
|
const struct input_state *state;
|
|
};
|
|
|
|
/* Input state. */
|
|
struct input_state {
|
|
const char *name;
|
|
void (*enter)(struct input_ctx *);
|
|
void (*exit)(struct input_ctx *);
|
|
const struct input_transition *transitions;
|
|
};
|
|
|
|
/* 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_escape_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
|
|
};
|
|
|
|
/* esc_enter state definition. */
|
|
const struct input_state input_state_esc_enter = {
|
|
"esc_enter",
|
|
input_clear, NULL,
|
|
input_state_esc_enter_table
|
|
};
|
|
|
|
/* esc_intermediate state definition. */
|
|
const struct input_state input_state_esc_intermediate = {
|
|
"esc_intermediate",
|
|
NULL, NULL,
|
|
input_state_esc_intermediate_table
|
|
};
|
|
|
|
/* csi_enter state definition. */
|
|
const struct input_state input_state_csi_enter = {
|
|
"csi_enter",
|
|
input_clear, NULL,
|
|
input_state_csi_enter_table
|
|
};
|
|
|
|
/* csi_parameter state definition. */
|
|
const struct input_state input_state_csi_parameter = {
|
|
"csi_parameter",
|
|
NULL, NULL,
|
|
input_state_csi_parameter_table
|
|
};
|
|
|
|
/* csi_intermediate state definition. */
|
|
const struct input_state input_state_csi_intermediate = {
|
|
"csi_intermediate",
|
|
NULL, NULL,
|
|
input_state_csi_intermediate_table
|
|
};
|
|
|
|
/* csi_ignore state definition. */
|
|
const struct input_state input_state_csi_ignore = {
|
|
"csi_ignore",
|
|
NULL, NULL,
|
|
input_state_csi_ignore_table
|
|
};
|
|
|
|
/* dcs_enter state definition. */
|
|
const struct input_state input_state_dcs_enter = {
|
|
"dcs_enter",
|
|
input_clear, NULL,
|
|
input_state_dcs_enter_table
|
|
};
|
|
|
|
/* dcs_parameter state definition. */
|
|
const struct input_state input_state_dcs_parameter = {
|
|
"dcs_parameter",
|
|
NULL, NULL,
|
|
input_state_dcs_parameter_table
|
|
};
|
|
|
|
/* dcs_intermediate state definition. */
|
|
const struct input_state input_state_dcs_intermediate = {
|
|
"dcs_intermediate",
|
|
NULL, NULL,
|
|
input_state_dcs_intermediate_table
|
|
};
|
|
|
|
/* dcs_handler state definition. */
|
|
const struct input_state input_state_dcs_handler = {
|
|
"dcs_handler",
|
|
NULL, NULL,
|
|
input_state_dcs_handler_table
|
|
};
|
|
|
|
/* dcs_escape state definition. */
|
|
const struct input_state input_state_dcs_escape = {
|
|
"dcs_escape",
|
|
NULL, NULL,
|
|
input_state_dcs_escape_table
|
|
};
|
|
|
|
/* dcs_ignore state definition. */
|
|
const struct input_state input_state_dcs_ignore = {
|
|
"dcs_ignore",
|
|
NULL, NULL,
|
|
input_state_dcs_ignore_table
|
|
};
|
|
|
|
/* 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
|
|
};
|
|
|
|
/* 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
|
|
};
|
|
|
|
/* 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
|
|
};
|
|
|
|
/* consume_st state definition. */
|
|
const struct input_state input_state_consume_st = {
|
|
"consume_st",
|
|
NULL, NULL,
|
|
input_state_consume_st_table
|
|
};
|
|
|
|
/* utf8_three state definition. */
|
|
const struct input_state input_state_utf8_three = {
|
|
"utf8_three",
|
|
NULL, NULL,
|
|
input_state_utf8_three_table
|
|
};
|
|
|
|
/* utf8_two state definition. */
|
|
const struct input_state input_state_utf8_two = {
|
|
"utf8_two",
|
|
NULL, NULL,
|
|
input_state_utf8_two_table
|
|
};
|
|
|
|
/* utf8_one state definition. */
|
|
const struct input_state input_state_utf8_one = {
|
|
"utf8_one",
|
|
NULL, NULL,
|
|
input_state_utf8_one_table
|
|
};
|
|
|
|
/* 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 }
|
|
};
|
|
|
|
/* 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, 0x7e, input_esc_dispatch, &input_state_ground },
|
|
{ 0x7f, 0xff, NULL, NULL },
|
|
|
|
{ -1, -1, NULL, NULL }
|
|
};
|
|
|
|
/* esc_interm state table. */
|
|
const struct input_transition input_state_esc_intermediate_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, NULL },
|
|
{ 0x30, 0x7e, input_esc_dispatch, &input_state_ground },
|
|
{ 0x7f, 0xff, NULL, NULL },
|
|
|
|
{ -1, -1, NULL, NULL }
|
|
};
|
|
|
|
/* 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 }
|
|
};
|
|
|
|
/* 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 }
|
|
};
|
|
|
|
/* csi_intermediate state table. */
|
|
const struct input_transition input_state_csi_intermediate_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, NULL },
|
|
{ 0x30, 0x3f, NULL, &input_state_csi_ignore },
|
|
{ 0x40, 0x7e, input_csi_dispatch, &input_state_ground },
|
|
{ 0x7f, 0xff, NULL, NULL },
|
|
|
|
{ -1, -1, NULL, NULL }
|
|
};
|
|
|
|
/* csi_ignore state table. */
|
|
const struct input_transition input_state_csi_ignore_table[] = {
|
|
INPUT_STATE_ANYWHERE,
|
|
|
|
{ 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 },
|
|
|
|
{ -1, -1, NULL, NULL }
|
|
};
|
|
|
|
/* 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, input_input, &input_state_dcs_handler },
|
|
{ 0x7f, 0xff, NULL, NULL },
|
|
|
|
{ -1, -1, NULL, NULL }
|
|
};
|
|
|
|
/* 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, input_input, &input_state_dcs_handler },
|
|
{ 0x7f, 0xff, NULL, NULL },
|
|
|
|
{ -1, -1, NULL, NULL }
|
|
};
|
|
|
|
/* dcs_interm state table. */
|
|
const struct input_transition input_state_dcs_intermediate_table[] = {
|
|
INPUT_STATE_ANYWHERE,
|
|
|
|
{ 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, input_input, &input_state_dcs_handler },
|
|
{ 0x7f, 0xff, NULL, NULL },
|
|
|
|
{ -1, -1, NULL, NULL }
|
|
};
|
|
|
|
/* dcs_handler state table. */
|
|
const struct input_transition input_state_dcs_handler_table[] = {
|
|
/* No INPUT_STATE_ANYWHERE */
|
|
|
|
{ 0x00, 0x1a, input_input, NULL },
|
|
{ 0x1b, 0x1b, NULL, &input_state_dcs_escape },
|
|
{ 0x1c, 0xff, input_input, NULL },
|
|
|
|
{ -1, -1, NULL, NULL }
|
|
};
|
|
|
|
/* dcs_escape state table. */
|
|
const struct input_transition input_state_dcs_escape_table[] = {
|
|
/* No INPUT_STATE_ANYWHERE */
|
|
|
|
{ 0x00, 0x5b, input_input, &input_state_dcs_handler },
|
|
{ 0x5c, 0x5c, input_dcs_dispatch, &input_state_ground },
|
|
{ 0x5d, 0xff, input_input, &input_state_dcs_handler },
|
|
|
|
{ -1, -1, NULL, NULL }
|
|
};
|
|
|
|
/* dcs_ignore state table. */
|
|
const struct input_transition input_state_dcs_ignore_table[] = {
|
|
INPUT_STATE_ANYWHERE,
|
|
|
|
{ 0x00, 0x17, NULL, NULL },
|
|
{ 0x19, 0x19, NULL, NULL },
|
|
{ 0x1c, 0x1f, NULL, NULL },
|
|
{ 0x20, 0xff, NULL, NULL },
|
|
|
|
{ -1, -1, NULL, NULL }
|
|
};
|
|
|
|
/* osc_string state table. */
|
|
const struct input_transition input_state_osc_string_table[] = {
|
|
INPUT_STATE_ANYWHERE,
|
|
|
|
{ 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 },
|
|
|
|
{ -1, -1, NULL, NULL }
|
|
};
|
|
|
|
/* apc_string state table. */
|
|
const struct input_transition input_state_apc_string_table[] = {
|
|
INPUT_STATE_ANYWHERE,
|
|
|
|
{ 0x00, 0x17, NULL, NULL },
|
|
{ 0x19, 0x19, NULL, NULL },
|
|
{ 0x1c, 0x1f, NULL, NULL },
|
|
{ 0x20, 0xff, input_input, NULL },
|
|
|
|
{ -1, -1, NULL, NULL }
|
|
};
|
|
|
|
/* rename_string state table. */
|
|
const struct input_transition input_state_rename_string_table[] = {
|
|
INPUT_STATE_ANYWHERE,
|
|
|
|
{ 0x00, 0x17, NULL, NULL },
|
|
{ 0x19, 0x19, NULL, NULL },
|
|
{ 0x1c, 0x1f, NULL, NULL },
|
|
{ 0x20, 0xff, input_input, NULL },
|
|
|
|
{ -1, -1, NULL, NULL }
|
|
};
|
|
|
|
/* consume_st state table. */
|
|
const struct input_transition input_state_consume_st_table[] = {
|
|
INPUT_STATE_ANYWHERE,
|
|
|
|
{ 0x00, 0x17, NULL, NULL },
|
|
{ 0x19, 0x19, NULL, NULL },
|
|
{ 0x1c, 0x1f, NULL, NULL },
|
|
{ 0x20, 0xff, NULL, NULL },
|
|
|
|
{ -1, -1, NULL, NULL }
|
|
};
|
|
|
|
/* utf8_three state table. */
|
|
const struct input_transition input_state_utf8_three_table[] = {
|
|
/* No INPUT_STATE_ANYWHERE */
|
|
|
|
{ 0x00, 0x7f, NULL, &input_state_ground },
|
|
{ 0x80, 0xbf, input_utf8_add, &input_state_utf8_two },
|
|
{ 0xc0, 0xff, NULL, &input_state_ground },
|
|
|
|
{ -1, -1, NULL, NULL }
|
|
};
|
|
|
|
/* utf8_two state table. */
|
|
const struct input_transition input_state_utf8_two_table[] = {
|
|
/* No INPUT_STATE_ANYWHERE */
|
|
|
|
{ 0x00, 0x7f, NULL, &input_state_ground },
|
|
{ 0x80, 0xbf, input_utf8_add, &input_state_utf8_one },
|
|
{ 0xc0, 0xff, NULL, &input_state_ground },
|
|
|
|
{ -1, -1, NULL, NULL }
|
|
};
|
|
|
|
/* utf8_one state table. */
|
|
const struct input_transition input_state_utf8_one_table[] = {
|
|
/* No INPUT_STATE_ANYWHERE */
|
|
|
|
{ 0x00, 0x7f, NULL, &input_state_ground },
|
|
{ 0x80, 0xbf, input_utf8_close, &input_state_ground },
|
|
{ 0xc0, 0xff, NULL, &input_state_ground },
|
|
|
|
{ -1, -1, NULL, NULL }
|
|
};
|
|
|
|
/* Input table compare. */
|
|
int
|
|
input_table_compare(const void *key, const void *value)
|
|
{
|
|
const struct input_ctx *ictx = key;
|
|
const struct input_table_entry *entry = value;
|
|
|
|
if (ictx->ch != entry->ch)
|
|
return (ictx->ch - entry->ch);
|
|
return (strcmp(ictx->interm_buf, entry->interm));
|
|
}
|
|
|
|
/* Initialise input parser. */
|
|
void
|
|
input_init(struct window_pane *wp)
|
|
{
|
|
struct input_ctx *ictx = &wp->ictx;
|
|
|
|
memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell);
|
|
|
|
memcpy(&ictx->old_cell, &grid_default_cell, sizeof ictx->old_cell);
|
|
ictx->old_cx = 0;
|
|
ictx->old_cy = 0;
|
|
|
|
*ictx->interm_buf = '\0';
|
|
ictx->interm_len = 0;
|
|
|
|
*ictx->param_buf = '\0';
|
|
ictx->param_len = 0;
|
|
|
|
ictx->state = &input_state_ground;
|
|
ictx->flags = 0;
|
|
|
|
ictx->since_ground = evbuffer_new();
|
|
}
|
|
|
|
/* Destroy input parser. */
|
|
void
|
|
input_free(struct window_pane *wp)
|
|
{
|
|
if (wp != NULL)
|
|
evbuffer_free(wp->ictx.since_ground);
|
|
}
|
|
|
|
/* Change input state. */
|
|
void
|
|
input_set_state(struct window_pane *wp, const struct input_transition *itr)
|
|
{
|
|
struct input_ctx *ictx = &wp->ictx;
|
|
struct evbuffer *ground_evb = ictx->since_ground;
|
|
|
|
if (ictx->state->exit != NULL)
|
|
ictx->state->exit(ictx);
|
|
|
|
if (itr->state == &input_state_ground)
|
|
evbuffer_drain(ground_evb, EVBUFFER_LENGTH(ground_evb));
|
|
|
|
ictx->state = itr->state;
|
|
if (ictx->state->enter != NULL)
|
|
ictx->state->enter(ictx);
|
|
}
|
|
|
|
/* Parse input. */
|
|
void
|
|
input_parse(struct window_pane *wp)
|
|
{
|
|
struct input_ctx *ictx = &wp->ictx;
|
|
const struct input_transition *itr;
|
|
struct evbuffer *evb = wp->event->input;
|
|
u_char *buf;
|
|
size_t len, off;
|
|
|
|
if (EVBUFFER_LENGTH(evb) == 0)
|
|
return;
|
|
|
|
wp->window->flags |= WINDOW_ACTIVITY;
|
|
wp->window->flags &= ~WINDOW_SILENCE;
|
|
|
|
/*
|
|
* 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;
|
|
|
|
buf = EVBUFFER_DATA(evb);
|
|
len = EVBUFFER_LENGTH(evb);
|
|
off = 0;
|
|
|
|
/* Parse the input. */
|
|
while (off < len) {
|
|
ictx->ch = buf[off++];
|
|
log_debug("%s: '%c' %s", __func__, ictx->ch, ictx->state->name);
|
|
|
|
/* 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++;
|
|
}
|
|
if (itr->first == -1 || itr->last == -1) {
|
|
/* No transition? Eh? */
|
|
fatalx("No transition from state!");
|
|
}
|
|
|
|
/*
|
|
* Execute the handler, if any. Don't switch state if it
|
|
* returns non-zero.
|
|
*/
|
|
if (itr->handler != NULL && itr->handler(ictx) != 0)
|
|
continue;
|
|
|
|
/* And switch state, if necessary. */
|
|
if (itr->state != NULL)
|
|
input_set_state(wp, itr);
|
|
|
|
/* If not in ground state, save input. */
|
|
if (ictx->state != &input_state_ground)
|
|
evbuffer_add(ictx->since_ground, &ictx->ch, 1);
|
|
}
|
|
|
|
/* Close the screen. */
|
|
screen_write_stop(&ictx->ctx);
|
|
|
|
evbuffer_drain(evb, len);
|
|
}
|
|
|
|
/* Split the parameter list (if any). */
|
|
int
|
|
input_split(struct input_ctx *ictx)
|
|
|
|
{
|
|
const char *errstr;
|
|
char *ptr, *out;
|
|
int n;
|
|
|
|
ictx->param_list_len = 0;
|
|
if (ictx->param_len == 0)
|
|
return (0);
|
|
|
|
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);
|
|
}
|
|
|
|
ictx->param_list[ictx->param_list_len++] = n;
|
|
if (ictx->param_list_len == nitems(ictx->param_list))
|
|
return (-1);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* Get an argument or return default value. */
|
|
int
|
|
input_get(struct input_ctx *ictx, u_int validx, int minval, int defval)
|
|
{
|
|
int retval;
|
|
|
|
if (validx >= ictx->param_list_len)
|
|
return (defval);
|
|
|
|
retval = ictx->param_list[validx];
|
|
if (retval == -1)
|
|
return (defval);
|
|
if (retval < minval)
|
|
return (minval);
|
|
return (retval);
|
|
}
|
|
|
|
/* Reply to terminal query. */
|
|
void
|
|
input_reply(struct input_ctx *ictx, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
char *reply;
|
|
|
|
va_start(ap, fmt);
|
|
vasprintf(&reply, fmt, ap);
|
|
va_end(ap);
|
|
|
|
bufferevent_write(ictx->wp->event, reply, strlen(reply));
|
|
free(reply);
|
|
}
|
|
|
|
/* Clear saved state. */
|
|
void
|
|
input_clear(struct input_ctx *ictx)
|
|
{
|
|
*ictx->interm_buf = '\0';
|
|
ictx->interm_len = 0;
|
|
|
|
*ictx->param_buf = '\0';
|
|
ictx->param_len = 0;
|
|
|
|
*ictx->input_buf = '\0';
|
|
ictx->input_len = 0;
|
|
|
|
ictx->flags &= ~INPUT_DISCARD;
|
|
}
|
|
|
|
/* Output this character to the screen. */
|
|
int
|
|
input_print(struct input_ctx *ictx)
|
|
{
|
|
ictx->cell.data = ictx->ch;
|
|
screen_write_cell(&ictx->ctx, &ictx->cell, NULL);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* Collect intermediate string. */
|
|
int
|
|
input_intermediate(struct input_ctx *ictx)
|
|
{
|
|
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';
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* Collect parameter string. */
|
|
int
|
|
input_parameter(struct input_ctx *ictx)
|
|
{
|
|
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';
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* Collect input string. */
|
|
int
|
|
input_input(struct input_ctx *ictx)
|
|
{
|
|
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';
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* Execute C0 control sequence. */
|
|
int
|
|
input_c0_dispatch(struct input_ctx *ictx)
|
|
{
|
|
struct screen_write_ctx *sctx = &ictx->ctx;
|
|
struct window_pane *wp = ictx->wp;
|
|
struct screen *s = sctx->s;
|
|
u_int trigger;
|
|
|
|
log_debug("%s: '%c", __func__, ictx->ch);
|
|
|
|
switch (ictx->ch) {
|
|
case '\000': /* NUL */
|
|
break;
|
|
case '\007': /* BEL */
|
|
wp->window->flags |= WINDOW_BELL;
|
|
break;
|
|
case '\010': /* BS */
|
|
screen_write_backspace(sctx);
|
|
goto count_c0;
|
|
case '\011': /* HT */
|
|
/* 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 '\012': /* LF */
|
|
case '\013': /* VT */
|
|
case '\014': /* FF */
|
|
screen_write_linefeed(sctx, 0);
|
|
goto count_c0;
|
|
case '\015': /* CR */
|
|
screen_write_carriagereturn(sctx);
|
|
goto count_c0;
|
|
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;
|
|
}
|
|
|
|
return (0);
|
|
|
|
count_c0:
|
|
trigger = options_get_number(&wp->window->options, "c0-change-trigger");
|
|
if (++wp->changes == trigger) {
|
|
wp->flags |= PANE_DROP;
|
|
window_pane_timer_start(wp);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* Execute escape sequence. */
|
|
int
|
|
input_esc_dispatch(struct input_ctx *ictx)
|
|
{
|
|
struct screen_write_ctx *sctx = &ictx->ctx;
|
|
struct screen *s = sctx->s;
|
|
struct input_table_entry *entry;
|
|
|
|
if (ictx->flags & INPUT_DISCARD)
|
|
return (0);
|
|
log_debug("%s: '%c', %s", __func__, ictx->ch, ictx->interm_buf);
|
|
|
|
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);
|
|
}
|
|
|
|
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;
|
|
|
|
screen_write_reset(sctx);
|
|
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_set(s->tabs, s->cx);
|
|
break;
|
|
case INPUT_ESC_RI:
|
|
screen_write_reverseindex(sctx);
|
|
break;
|
|
case INPUT_ESC_DECKPAM:
|
|
screen_write_kkeypadmode(sctx, 1);
|
|
break;
|
|
case INPUT_ESC_DECKPNM:
|
|
screen_write_kkeypadmode(sctx, 0);
|
|
break;
|
|
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 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);
|
|
}
|
|
|
|
/* Execute control sequence. */
|
|
int
|
|
input_csi_dispatch(struct input_ctx *ictx)
|
|
{
|
|
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->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);
|
|
|
|
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 (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 INPUT_CSI_CUB:
|
|
screen_write_cursorleft(sctx, input_get(ictx, 0, 1, 1));
|
|
break;
|
|
case INPUT_CSI_CUD:
|
|
screen_write_cursordown(sctx, input_get(ictx, 0, 1, 1));
|
|
break;
|
|
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_CNL:
|
|
screen_write_carriagereturn(sctx);
|
|
screen_write_cursordown(sctx, input_get(ictx, 0, 1, 1));
|
|
break;
|
|
case INPUT_CSI_CPL:
|
|
screen_write_carriagereturn(sctx);
|
|
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_DA_TWO:
|
|
switch (input_get(ictx, 0, 0, 0)) {
|
|
case 0:
|
|
input_reply(ictx, "\033[>0;95;0c");
|
|
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;
|
|
case 3:
|
|
switch (input_get(ictx, 1, 0, 0)) {
|
|
case 0:
|
|
/*
|
|
* Linux console extension to clear history
|
|
* (for example before locking the screen).
|
|
*/
|
|
screen_write_clearhistory(sctx);
|
|
break;
|
|
}
|
|
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_RCP:
|
|
memcpy(&ictx->cell, &ictx->old_cell, sizeof ictx->cell);
|
|
screen_write_cursormove(sctx, ictx->old_cx, ictx->old_cy);
|
|
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, 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, 0);
|
|
break;
|
|
case 1000:
|
|
case 1001:
|
|
case 1002:
|
|
case 1003:
|
|
screen_write_mousemode_off(&ictx->ctx);
|
|
break;
|
|
case 1005:
|
|
screen_write_utf8mousemode(&ictx->ctx, 0);
|
|
break;
|
|
case 1049:
|
|
window_pane_alternate_off(wp, &ictx->cell);
|
|
break;
|
|
case 2004:
|
|
screen_write_bracketpaste(&ictx->ctx, 0);
|
|
break;
|
|
default:
|
|
log_debug("%s: unknown '%c'", __func__, ictx->ch);
|
|
break;
|
|
}
|
|
break;
|
|
case INPUT_CSI_SCP:
|
|
memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell);
|
|
ictx->old_cx = s->cx;
|
|
ictx->old_cy = s->cy;
|
|
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);
|
|
break;
|
|
default:
|
|
log_debug("%s: unknown '%c'", __func__, ictx->ch);
|
|
break;
|
|
}
|
|
break;
|
|
case INPUT_CSI_SM_PRIVATE:
|
|
switch (input_get(ictx, 0, 0, -1)) {
|
|
case 1: /* GATM */
|
|
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, 1);
|
|
break;
|
|
case 1000:
|
|
screen_write_mousemode_on(
|
|
&ictx->ctx, MODE_MOUSE_STANDARD);
|
|
break;
|
|
case 1002:
|
|
screen_write_mousemode_on(
|
|
&ictx->ctx, MODE_MOUSE_BUTTON);
|
|
break;
|
|
case 1003:
|
|
screen_write_mousemode_on(&ictx->ctx, MODE_MOUSE_ANY);
|
|
break;
|
|
case 1005:
|
|
screen_write_utf8mousemode(&ictx->ctx, 1);
|
|
break;
|
|
case 1049:
|
|
window_pane_alternate_on(wp, &ictx->cell);
|
|
break;
|
|
case 2004:
|
|
screen_write_bracketpaste(&ictx->ctx, 1);
|
|
break;
|
|
default:
|
|
log_debug("%s: unknown '%c'", __func__, ictx->ch);
|
|
break;
|
|
}
|
|
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 3:
|
|
bit_nclear(s->tabs, 0, screen_size_x(s) - 1);
|
|
break;
|
|
default:
|
|
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;
|
|
case INPUT_CSI_DECSCUSR:
|
|
n = input_get(ictx, 0, 0, 0);
|
|
screen_set_cursor_style(s, n);
|
|
break;
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* Handle CSI SGR. */
|
|
void
|
|
input_csi_dispatch_sgr(struct input_ctx *ictx)
|
|
{
|
|
struct grid_cell *gc = &ictx->cell;
|
|
u_int i;
|
|
int n, m;
|
|
u_char attr;
|
|
|
|
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 < ictx->param_list_len; i++) {
|
|
n = input_get(ictx, i, 0, 0);
|
|
|
|
if (n == 38 || n == 48) {
|
|
i++;
|
|
if (input_get(ictx, i, 0, -1) != 5)
|
|
continue;
|
|
|
|
i++;
|
|
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->bg = 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 (n) {
|
|
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 = n - 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 = n - 40;
|
|
break;
|
|
case 49:
|
|
gc->flags &= ~GRID_FLAG_BG256;
|
|
gc->bg = 8;
|
|
break;
|
|
case 90:
|
|
case 91:
|
|
case 92:
|
|
case 93:
|
|
case 94:
|
|
case 95:
|
|
case 96:
|
|
case 97:
|
|
gc->flags &= ~GRID_FLAG_FG256;
|
|
gc->fg = n;
|
|
break;
|
|
case 100:
|
|
case 101:
|
|
case 102:
|
|
case 103:
|
|
case 104:
|
|
case 105:
|
|
case 106:
|
|
case 107:
|
|
gc->flags &= ~GRID_FLAG_BG256;
|
|
gc->bg = n - 10;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* DCS terminator (ST) received. */
|
|
int
|
|
input_dcs_dispatch(struct input_ctx *ictx)
|
|
{
|
|
const char prefix[] = "tmux;";
|
|
const u_int prefix_len = (sizeof prefix) - 1;
|
|
|
|
if (ictx->flags & INPUT_DISCARD)
|
|
return (0);
|
|
|
|
log_debug("%s: \"%s\"", __func__, ictx->input_buf);
|
|
|
|
/* Check for tmux prefix. */
|
|
if (ictx->input_len >= prefix_len &&
|
|
strncmp(ictx->input_buf, prefix, prefix_len) == 0) {
|
|
screen_write_rawstring(&ictx->ctx,
|
|
ictx->input_buf + prefix_len, ictx->input_len - prefix_len);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* OSC string started. */
|
|
void
|
|
input_enter_osc(struct input_ctx *ictx)
|
|
{
|
|
log_debug("%s", __func__);
|
|
|
|
input_clear(ictx);
|
|
}
|
|
|
|
/* OSC terminator (ST) received. */
|
|
void
|
|
input_exit_osc(struct input_ctx *ictx)
|
|
{
|
|
u_char *p = ictx->input_buf;
|
|
int option;
|
|
|
|
if (ictx->flags & INPUT_DISCARD)
|
|
return;
|
|
if (ictx->input_len < 1 || *p < '0' || *p > '9')
|
|
return;
|
|
|
|
log_debug("%s: \"%s\"", __func__, p);
|
|
|
|
option = 0;
|
|
while (*p >= '0' && *p <= '9')
|
|
option = option * 10 + *p++ - '0';
|
|
if (*p == ';')
|
|
p++;
|
|
|
|
switch (option) {
|
|
case 0:
|
|
case 2:
|
|
screen_set_title(ictx->ctx.s, p);
|
|
server_status_window(ictx->wp->window);
|
|
break;
|
|
case 12:
|
|
screen_set_cursor_colour(ictx->ctx.s, p);
|
|
break;
|
|
case 112:
|
|
if (*p == '\0') /* No arguments allowed. */
|
|
screen_set_cursor_colour(ictx->ctx.s, "");
|
|
break;
|
|
default:
|
|
log_debug("%s: unknown '%u'", __func__, option);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* APC string started. */
|
|
void
|
|
input_enter_apc(struct input_ctx *ictx)
|
|
{
|
|
log_debug("%s", __func__);
|
|
|
|
input_clear(ictx);
|
|
}
|
|
|
|
/* 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__);
|
|
|
|
input_clear(ictx);
|
|
}
|
|
|
|
/* Rename terminator (ST) received. */
|
|
void
|
|
input_exit_rename(struct input_ctx *ictx)
|
|
{
|
|
if (ictx->flags & INPUT_DISCARD)
|
|
return;
|
|
if (!options_get_number(&ictx->wp->window->options, "allow-rename"))
|
|
return;
|
|
log_debug("%s: \"%s\"", __func__, ictx->input_buf);
|
|
|
|
window_set_name(ictx->wp->window, 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);
|
|
}
|