| /* $Id: input.c,v 1.95 2009-08-21 21:07:20 tcunha Exp $ */ | 
 |  | 
 | /* | 
 |  * 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_cursorleft(&ictx->ctx, 1); | 
 | 		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; | 
 | 		} | 
 | 	} | 
 | } |