mirror of
https://github.com/vim/vim
synced 2025-03-16 14:57:52 +01:00
Problem: unexpected DCS responses may cause out of bounds reads (after v9.1.1054) Solution: check that the parsed value is '=' as expected (Julio B) Signed-off-by: Julio B <julio.bacel@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
7603 lines
190 KiB
C
7603 lines
190 KiB
C
/* vi:set ts=8 sts=4 sw=4 noet:
|
|
*
|
|
* VIM - Vi IMproved by Bram Moolenaar
|
|
*
|
|
* Do ":help uganda" in Vim to read copying and usage conditions.
|
|
* Do ":help credits" in Vim to see a list of people who contributed.
|
|
* See README.txt for an overview of the Vim source code.
|
|
*/
|
|
/*
|
|
*
|
|
* term.c: functions for controlling the terminal
|
|
*
|
|
* primitive termcap support for Amiga and Win32 included
|
|
*
|
|
* NOTE: padding and variable substitution is not performed,
|
|
* when compiling without HAVE_TGETENT, we use tputs() and tgoto() dummies.
|
|
*/
|
|
|
|
/*
|
|
* Some systems have a prototype for tgetstr() with (char *) instead of
|
|
* (char **). This define removes that prototype. We include our own prototype
|
|
* below.
|
|
*/
|
|
#define tgetstr tgetstr_defined_wrong
|
|
|
|
#include "vim.h"
|
|
|
|
#ifdef HAVE_TGETENT
|
|
# ifdef HAVE_TERMIOS_H
|
|
# include <termios.h> // seems to be required for some Linux
|
|
# endif
|
|
# ifdef HAVE_TERMCAP_H
|
|
# include <termcap.h>
|
|
# endif
|
|
|
|
/*
|
|
* A few linux systems define outfuntype in termcap.h to be used as the third
|
|
* argument for tputs().
|
|
*/
|
|
# if defined(VMS) || defined(AMIGA)
|
|
# define TPUTSFUNCAST (void (*)(unsigned int))
|
|
# else
|
|
# ifdef HAVE_OUTFUNTYPE
|
|
# define TPUTSFUNCAST (outfuntype)
|
|
# else
|
|
# define TPUTSFUNCAST (int (*)(int))
|
|
# endif
|
|
# endif
|
|
#endif
|
|
|
|
#undef tgetstr
|
|
|
|
// start of keys that are not directly used by Vim but can be mapped
|
|
#define BT_EXTRA_KEYS 0x101
|
|
|
|
static void parse_builtin_tcap(char_u *s);
|
|
static void gather_termleader(void);
|
|
#ifdef FEAT_TERMRESPONSE
|
|
static void req_codes_from_term(void);
|
|
static void req_more_codes_from_term(void);
|
|
static void got_code_from_term(char_u *code, int len);
|
|
static void check_for_codes_from_term(void);
|
|
#endif
|
|
static void del_termcode_idx(int idx);
|
|
static int find_term_bykeys(char_u *src);
|
|
static int term_is_builtin(char_u *name);
|
|
static int term_7to8bit(char_u *p);
|
|
static void accept_modifiers_for_function_keys(void);
|
|
|
|
// Change this to "if 1" to debug what happens with termresponse.
|
|
# if 0
|
|
# define DEBUG_TERMRESPONSE
|
|
static void log_tr(const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(1, 2);
|
|
# define LOG_TR(msg) log_tr msg
|
|
# else
|
|
# define LOG_TR(msg) do { /**/ } while (0)
|
|
# endif
|
|
|
|
#ifdef HAVE_TGETENT
|
|
static char *invoke_tgetent(char_u *, char_u *);
|
|
|
|
/*
|
|
* Here is our own prototype for tgetstr(), any prototypes from the include
|
|
* files have been disabled by the define at the start of this file.
|
|
*/
|
|
char *tgetstr(char *, char **);
|
|
#endif
|
|
|
|
typedef enum {
|
|
STATUS_GET, // send request when switching to RAW mode
|
|
STATUS_SENT, // did send request, checking for response
|
|
STATUS_GOT, // received response
|
|
STATUS_FAIL // timed out
|
|
} request_progress_T;
|
|
|
|
typedef struct {
|
|
request_progress_T tr_progress;
|
|
time_t tr_start; // when request was sent, -1 for never
|
|
} termrequest_T;
|
|
|
|
# define TERMREQUEST_INIT {STATUS_GET, -1}
|
|
|
|
// Request Terminal Version status:
|
|
static termrequest_T crv_status = TERMREQUEST_INIT;
|
|
|
|
// Request Cursor position report:
|
|
static termrequest_T u7_status = TERMREQUEST_INIT;
|
|
|
|
// Request xterm compatibility check:
|
|
static termrequest_T xcc_status = TERMREQUEST_INIT;
|
|
|
|
#ifdef FEAT_TERMRESPONSE
|
|
# ifdef FEAT_TERMINAL
|
|
// Request foreground color report:
|
|
static termrequest_T rfg_status = TERMREQUEST_INIT;
|
|
static int fg_r = 0;
|
|
static int fg_g = 0;
|
|
static int fg_b = 0;
|
|
static int bg_r = 255;
|
|
static int bg_g = 255;
|
|
static int bg_b = 255;
|
|
# endif
|
|
|
|
// Request background color report:
|
|
static termrequest_T rbg_status = TERMREQUEST_INIT;
|
|
|
|
// Request cursor blinking mode report:
|
|
static termrequest_T rbm_status = TERMREQUEST_INIT;
|
|
|
|
// Request cursor style report:
|
|
static termrequest_T rcs_status = TERMREQUEST_INIT;
|
|
|
|
// Request window's position report:
|
|
static termrequest_T winpos_status = TERMREQUEST_INIT;
|
|
|
|
static termrequest_T *all_termrequests[] = {
|
|
&crv_status,
|
|
&u7_status,
|
|
&xcc_status,
|
|
# ifdef FEAT_TERMINAL
|
|
&rfg_status,
|
|
# endif
|
|
&rbg_status,
|
|
&rbm_status,
|
|
&rcs_status,
|
|
&winpos_status,
|
|
NULL
|
|
};
|
|
|
|
// The t_8u code may default to a value but get reset when the term response is
|
|
// received. To avoid redrawing too often, only redraw when t_8u is not reset
|
|
// and it was supposed to be written. Unless t_8u was set explicitly.
|
|
// FALSE -> don't output t_8u yet
|
|
// MAYBE -> tried outputting t_8u while FALSE
|
|
// OK -> can write t_8u
|
|
int write_t_8u_state = FALSE;
|
|
#endif
|
|
|
|
#ifdef HAVE_TGETENT
|
|
/*
|
|
* Don't declare these variables if termcap.h contains them.
|
|
* Autoconf checks if these variables should be declared extern (not all
|
|
* systems have them).
|
|
* Some versions define ospeed to be speed_t, but that is incompatible with
|
|
* BSD, where ospeed is short and speed_t is long.
|
|
*/
|
|
# ifndef HAVE_OSPEED
|
|
# ifdef OSPEED_EXTERN
|
|
extern short ospeed;
|
|
# else
|
|
short ospeed;
|
|
# endif
|
|
# endif
|
|
# ifndef HAVE_UP_BC_PC
|
|
# ifdef UP_BC_PC_EXTERN
|
|
extern char *UP, *BC, PC;
|
|
# else
|
|
char *UP, *BC, PC;
|
|
# endif
|
|
# endif
|
|
|
|
# define TGETSTR(s, p) vim_tgetstr((s), (p))
|
|
# define TGETENT(b, t) tgetent((char *)(b), (char *)(t))
|
|
static char_u *vim_tgetstr(char *s, char_u **pp);
|
|
#endif // HAVE_TGETENT
|
|
|
|
static int detected_8bit = FALSE; // detected 8-bit terminal
|
|
|
|
#if (defined(UNIX) || defined(VMS))
|
|
static int focus_state = MAYBE; // TRUE if the Vim window has focus
|
|
#endif
|
|
|
|
#ifdef FEAT_TERMRESPONSE
|
|
// When the cursor shape was detected these values are used:
|
|
// 1: block, 2: underline, 3: vertical bar
|
|
static int initial_cursor_shape = 0;
|
|
|
|
// The blink flag from the style response may be inverted from the actual
|
|
// blinking state, xterm XORs the flags.
|
|
static int initial_cursor_shape_blink = FALSE;
|
|
|
|
// The blink flag from the blinking-cursor mode response
|
|
static int initial_cursor_blink = FALSE;
|
|
#endif
|
|
|
|
/*
|
|
* The builtin termcap entries.
|
|
*
|
|
* The entries are also included when HAVE_TGETENT is defined, the system
|
|
* termcap may be incomplete and a few Vim-specific entries are added.
|
|
*
|
|
* When HAVE_TGETENT is defined, the builtin entries can be accessed with
|
|
* "builtin_amiga", "builtin_ansi", "builtin_debug", etc.
|
|
*
|
|
* Each termcap is a list of tcap_entry_T. See parse_builtin_tcap() for all
|
|
* details.
|
|
*
|
|
* Entries marked with "guessed" may be wrong.
|
|
*/
|
|
typedef struct
|
|
{
|
|
int bt_entry; // either a KS_xxx code (>= 0), or a K_xxx code.
|
|
char *bt_string; // value
|
|
} tcap_entry_T;
|
|
|
|
/*
|
|
* Standard ANSI terminal, default for Unix.
|
|
*/
|
|
static tcap_entry_T builtin_ansi[] = {
|
|
{(int)KS_CE, "\033[K"},
|
|
{(int)KS_AL, "\033[L"},
|
|
# ifdef TERMINFO
|
|
{(int)KS_CAL, "\033[%p1%dL"},
|
|
# else
|
|
{(int)KS_CAL, "\033[%dL"},
|
|
# endif
|
|
{(int)KS_DL, "\033[M"},
|
|
# ifdef TERMINFO
|
|
{(int)KS_CDL, "\033[%p1%dM"},
|
|
# else
|
|
{(int)KS_CDL, "\033[%dM"},
|
|
# endif
|
|
{(int)KS_CL, "\033[H\033[2J"},
|
|
{(int)KS_ME, "\033[0m"},
|
|
{(int)KS_MR, "\033[7m"},
|
|
{(int)KS_MS, "y"},
|
|
{(int)KS_UT, "y"}, // guessed
|
|
{(int)KS_LE, "\b"},
|
|
# ifdef TERMINFO
|
|
{(int)KS_CM, "\033[%i%p1%d;%p2%dH"},
|
|
# else
|
|
{(int)KS_CM, "\033[%i%d;%dH"},
|
|
# endif
|
|
# ifdef TERMINFO
|
|
{(int)KS_CRI, "\033[%p1%dC"},
|
|
# else
|
|
{(int)KS_CRI, "\033[%dC"},
|
|
# endif
|
|
|
|
{(int)KS_NAME, NULL} // end marker
|
|
};
|
|
|
|
/*
|
|
* VT320 is working as an ANSI terminal compatible DEC terminal.
|
|
* (it covers VT1x0, VT2x0 and VT3x0 up to VT320 on VMS as well)
|
|
* TODO:- rewrite ESC[ codes to CSI
|
|
* - keyboard languages (CSI ? 26 n)
|
|
*/
|
|
static tcap_entry_T builtin_vt320[] = {
|
|
{(int)KS_CE, "\033[K"},
|
|
{(int)KS_AL, "\033[L"},
|
|
# ifdef TERMINFO
|
|
{(int)KS_CAL, "\033[%p1%dL"},
|
|
# else
|
|
{(int)KS_CAL, "\033[%dL"},
|
|
# endif
|
|
{(int)KS_DL, "\033[M"},
|
|
# ifdef TERMINFO
|
|
{(int)KS_CDL, "\033[%p1%dM"},
|
|
# else
|
|
{(int)KS_CDL, "\033[%dM"},
|
|
# endif
|
|
{(int)KS_CL, "\033[H\033[2J"},
|
|
{(int)KS_CD, "\033[J"},
|
|
{(int)KS_CCO, "8"}, // allow 8 colors
|
|
{(int)KS_ME, "\033[0m"},
|
|
{(int)KS_MR, "\033[7m"},
|
|
{(int)KS_MD, "\033[1m"}, // bold mode
|
|
{(int)KS_SE, "\033[22m"},// normal mode
|
|
{(int)KS_UE, "\033[24m"},// exit underscore mode
|
|
{(int)KS_US, "\033[4m"}, // underscore mode
|
|
{(int)KS_CZH, "\033[34;43m"}, // italic mode: blue text on yellow
|
|
{(int)KS_CZR, "\033[0m"}, // italic mode end
|
|
{(int)KS_CAB, "\033[4%dm"}, // set background color (ANSI)
|
|
{(int)KS_CAF, "\033[3%dm"}, // set foreground color (ANSI)
|
|
{(int)KS_CSB, "\033[102;%dm"}, // set screen background color
|
|
{(int)KS_CSF, "\033[101;%dm"}, // set screen foreground color
|
|
{(int)KS_MS, "y"},
|
|
{(int)KS_UT, "y"},
|
|
{(int)KS_XN, "y"},
|
|
{(int)KS_LE, "\b"},
|
|
# ifdef TERMINFO
|
|
{(int)KS_CM, "\033[%i%p1%d;%p2%dH"},
|
|
# else
|
|
{(int)KS_CM, "\033[%i%d;%dH"},
|
|
# endif
|
|
# ifdef TERMINFO
|
|
{(int)KS_CRI, "\033[%p1%dC"},
|
|
# else
|
|
{(int)KS_CRI, "\033[%dC"},
|
|
# endif
|
|
{K_UP, "\033[A"},
|
|
{K_DOWN, "\033[B"},
|
|
{K_RIGHT, "\033[C"},
|
|
{K_LEFT, "\033[D"},
|
|
// Note: cursor key sequences for application cursor mode are omitted,
|
|
// because they interfere with typed commands: <Esc>OA.
|
|
{K_F1, "\033[11~"},
|
|
{K_F2, "\033[12~"},
|
|
{K_F3, "\033[13~"},
|
|
{K_F4, "\033[14~"},
|
|
{K_F5, "\033[15~"},
|
|
{K_F6, "\033[17~"},
|
|
{K_F7, "\033[18~"},
|
|
{K_F8, "\033[19~"},
|
|
{K_F9, "\033[20~"},
|
|
{K_F10, "\033[21~"},
|
|
{K_F11, "\033[23~"},
|
|
{K_F12, "\033[24~"},
|
|
{K_F13, "\033[25~"},
|
|
{K_F14, "\033[26~"},
|
|
{K_F15, "\033[28~"}, // Help
|
|
{K_F16, "\033[29~"}, // Select
|
|
{K_F17, "\033[31~"},
|
|
{K_F18, "\033[32~"},
|
|
{K_F19, "\033[33~"},
|
|
{K_F20, "\033[34~"},
|
|
{K_INS, "\033[2~"},
|
|
{K_DEL, "\033[3~"},
|
|
{K_HOME, "\033[1~"},
|
|
{K_END, "\033[4~"},
|
|
{K_PAGEUP, "\033[5~"},
|
|
{K_PAGEDOWN, "\033[6~"},
|
|
// These sequences starting with <Esc> O may interfere with what the user
|
|
// is typing. Remove these if that bothers you.
|
|
{K_KPLUS, "\033Ok"}, // keypad plus
|
|
{K_KMINUS, "\033Om"}, // keypad minus
|
|
{K_KDIVIDE, "\033Oo"}, // keypad /
|
|
{K_KMULTIPLY, "\033Oj"}, // keypad *
|
|
{K_KENTER, "\033OM"}, // keypad Enter
|
|
{K_K0, "\033Op"}, // keypad 0
|
|
{K_K1, "\033Oq"}, // keypad 1
|
|
{K_K2, "\033Or"}, // keypad 2
|
|
{K_K3, "\033Os"}, // keypad 3
|
|
{K_K4, "\033Ot"}, // keypad 4
|
|
{K_K5, "\033Ou"}, // keypad 5
|
|
{K_K6, "\033Ov"}, // keypad 6
|
|
{K_K7, "\033Ow"}, // keypad 7
|
|
{K_K8, "\033Ox"}, // keypad 8
|
|
{K_K9, "\033Oy"}, // keypad 9
|
|
{K_BS, "\x7f"}, // for some reason 0177 doesn't work
|
|
|
|
{(int)KS_NAME, NULL} // end marker
|
|
};
|
|
|
|
/*
|
|
* Ordinary vt52
|
|
*/
|
|
static tcap_entry_T builtin_vt52[] = {
|
|
{(int)KS_CE, "\033K"},
|
|
{(int)KS_CD, "\033J"},
|
|
# ifdef TERMINFO
|
|
{(int)KS_CM, "\033Y%p1%' '%+%c%p2%' '%+%c"},
|
|
# else
|
|
{(int)KS_CM, "\033Y%+ %+ "},
|
|
# endif
|
|
{(int)KS_LE, "\b"},
|
|
{(int)KS_SR, "\033I"},
|
|
{(int)KS_AL, "\033L"},
|
|
{(int)KS_DL, "\033M"},
|
|
{K_UP, "\033A"},
|
|
{K_DOWN, "\033B"},
|
|
{K_LEFT, "\033D"},
|
|
{K_RIGHT, "\033C"},
|
|
{K_F1, "\033P"},
|
|
{K_F2, "\033Q"},
|
|
{K_F3, "\033R"},
|
|
{(int)KS_CL, "\033H\033J"},
|
|
{(int)KS_MS, "y"},
|
|
|
|
{(int)KS_NAME, NULL} // end marker
|
|
};
|
|
|
|
/*
|
|
* Builtin xterm with Vim-specific entries.
|
|
*/
|
|
static tcap_entry_T builtin_xterm[] = {
|
|
{(int)KS_CE, "\033[K"},
|
|
{(int)KS_AL, "\033[L"},
|
|
# ifdef TERMINFO
|
|
{(int)KS_CAL, "\033[%p1%dL"},
|
|
# else
|
|
{(int)KS_CAL, "\033[%dL"},
|
|
# endif
|
|
{(int)KS_DL, "\033[M"},
|
|
# ifdef TERMINFO
|
|
{(int)KS_CDL, "\033[%p1%dM"},
|
|
# else
|
|
{(int)KS_CDL, "\033[%dM"},
|
|
# endif
|
|
# ifdef TERMINFO
|
|
{(int)KS_CS, "\033[%i%p1%d;%p2%dr"},
|
|
# else
|
|
{(int)KS_CS, "\033[%i%d;%dr"},
|
|
# endif
|
|
{(int)KS_CL, "\033[H\033[2J"},
|
|
{(int)KS_CD, "\033[J"},
|
|
{(int)KS_ME, "\033[m"},
|
|
{(int)KS_MR, "\033[7m"},
|
|
{(int)KS_MD, "\033[1m"},
|
|
{(int)KS_UE, "\033[m"},
|
|
{(int)KS_US, "\033[4m"},
|
|
{(int)KS_STE, "\033[29m"},
|
|
{(int)KS_STS, "\033[9m"},
|
|
{(int)KS_MS, "y"},
|
|
{(int)KS_UT, "y"},
|
|
{(int)KS_LE, "\b"},
|
|
{(int)KS_VI, "\033[?25l"},
|
|
{(int)KS_VE, "\033[?25h"},
|
|
{(int)KS_VS, "\033[?12h"},
|
|
{(int)KS_CVS, "\033[?12l"},
|
|
# ifdef TERMINFO
|
|
{(int)KS_CSH, "\033[%p1%d q"},
|
|
# else
|
|
{(int)KS_CSH, "\033[%d q"},
|
|
# endif
|
|
{(int)KS_CRC, "\033[?12$p"},
|
|
{(int)KS_CRS, "\033P$q q\033\\"},
|
|
# ifdef TERMINFO
|
|
{(int)KS_CM, "\033[%i%p1%d;%p2%dH"},
|
|
# else
|
|
{(int)KS_CM, "\033[%i%d;%dH"},
|
|
# endif
|
|
{(int)KS_SR, "\033M"},
|
|
# ifdef TERMINFO
|
|
{(int)KS_CRI, "\033[%p1%dC"},
|
|
# else
|
|
{(int)KS_CRI, "\033[%dC"},
|
|
# endif
|
|
{(int)KS_KS, "\033[?1h\033="},
|
|
{(int)KS_KE, "\033[?1l\033>"},
|
|
# ifdef FEAT_XTERM_SAVE
|
|
{(int)KS_TI, "\0337\033[?47h"},
|
|
{(int)KS_TE, "\033[?47l\0338"},
|
|
# endif
|
|
// These are now under control of the 'keyprotocol' option, see
|
|
// "builtin_mok2".
|
|
// {(int)KS_CTI, "\033[>4;2m"},
|
|
// {(int)KS_CRK, "\033[?4m"},
|
|
// {(int)KS_CTE, "\033[>4;m"},
|
|
{(int)KS_CIS, "\033]1;"},
|
|
{(int)KS_CIE, "\007"},
|
|
{(int)KS_TS, "\033]2;"},
|
|
{(int)KS_FS, "\007"},
|
|
{(int)KS_CSC, "\033]12;"},
|
|
{(int)KS_CEC, "\007"},
|
|
# ifdef TERMINFO
|
|
{(int)KS_CWS, "\033[8;%p1%d;%p2%dt"},
|
|
{(int)KS_CWP, "\033[3;%p1%d;%p2%dt"},
|
|
{(int)KS_CGP, "\033[13t"},
|
|
# else
|
|
{(int)KS_CWS, "\033[8;%d;%dt"},
|
|
{(int)KS_CWP, "\033[3;%d;%dt"},
|
|
{(int)KS_CGP, "\033[13t"},
|
|
# endif
|
|
{(int)KS_CRV, "\033[>c"},
|
|
{(int)KS_CXM, "\033[?1006;1000%?%p1%{1}%=%th%el%;"},
|
|
{(int)KS_RFG, "\033]10;?\007"},
|
|
{(int)KS_RBG, "\033]11;?\007"},
|
|
{(int)KS_U7, "\033[6n"},
|
|
{(int)KS_CAU, "\033[58;5;%dm"},
|
|
{(int)KS_CBE, "\033[?2004h"},
|
|
{(int)KS_CBD, "\033[?2004l"},
|
|
{(int)KS_CST, "\033[22;2t"},
|
|
{(int)KS_CRT, "\033[23;2t"},
|
|
{(int)KS_SSI, "\033[22;1t"},
|
|
{(int)KS_SRI, "\033[23;1t"},
|
|
# if (defined(UNIX) || defined(VMS))
|
|
{(int)KS_FD, "\033[?1004l"},
|
|
{(int)KS_FE, "\033[?1004h"},
|
|
# endif
|
|
|
|
{K_UP, "\033O*A"},
|
|
{K_DOWN, "\033O*B"},
|
|
{K_RIGHT, "\033O*C"},
|
|
{K_LEFT, "\033O*D"},
|
|
// An extra set of cursor keys for vt100 mode
|
|
{K_XUP, "\033[@;*A"}, // Esc [ A or Esc [ 1 ; A
|
|
{K_XDOWN, "\033[@;*B"}, // Esc [ B or Esc [ 1 ; B
|
|
{K_XRIGHT, "\033[@;*C"}, // Esc [ C or Esc [ 1 ; C
|
|
{K_XLEFT, "\033[@;*D"}, // Esc [ D or Esc [ 1 ; D
|
|
// An extra set of function keys for vt100 mode
|
|
{K_XF1, "\033O*P"},
|
|
{K_XF2, "\033O*Q"},
|
|
{K_XF3, "\033O*R"},
|
|
{K_XF4, "\033O*S"},
|
|
{K_F1, "\033[11;*~"},
|
|
{K_F2, "\033[12;*~"},
|
|
{K_F3, "\033[13;*~"},
|
|
{K_F4, "\033[14;*~"},
|
|
{K_F5, "\033[15;*~"},
|
|
{K_F6, "\033[17;*~"},
|
|
{K_F7, "\033[18;*~"},
|
|
{K_F8, "\033[19;*~"},
|
|
{K_F9, "\033[20;*~"},
|
|
{K_F10, "\033[21;*~"},
|
|
{K_F11, "\033[23;*~"},
|
|
{K_F12, "\033[24;*~"},
|
|
{K_S_TAB, "\033[Z"},
|
|
{K_HELP, "\033[28;*~"},
|
|
{K_UNDO, "\033[26;*~"},
|
|
{K_INS, "\033[2;*~"},
|
|
{K_HOME, "\033[@;*H"}, // Esc [ H or Esc 1 ; H
|
|
// {K_S_HOME, "\033O2H"},
|
|
// {K_C_HOME, "\033O5H"},
|
|
{K_KHOME, "\033[1;*~"},
|
|
{K_XHOME, "\033O*H"}, // other Home
|
|
{K_ZHOME, "\033[7;*~"}, // other Home
|
|
{K_END, "\033[@;*F"}, // Esc [ F or Esc 1 ; F
|
|
// {K_S_END, "\033O2F"},
|
|
// {K_C_END, "\033O5F"},
|
|
{K_KEND, "\033[4;*~"},
|
|
{K_XEND, "\033O*F"}, // other End
|
|
{K_ZEND, "\033[8;*~"},
|
|
{K_PAGEUP, "\033[5;*~"},
|
|
{K_PAGEDOWN, "\033[6;*~"},
|
|
{K_KPLUS, "\033O*k"}, // keypad plus
|
|
{K_KMINUS, "\033O*m"}, // keypad minus
|
|
{K_KDIVIDE, "\033O*o"}, // keypad /
|
|
{K_KMULTIPLY, "\033O*j"}, // keypad *
|
|
{K_KENTER, "\033O*M"}, // keypad Enter
|
|
{K_KPOINT, "\033O*n"}, // keypad .
|
|
{K_K0, "\033O*p"}, // keypad 0
|
|
{K_K1, "\033O*q"}, // keypad 1
|
|
{K_K2, "\033O*r"}, // keypad 2
|
|
{K_K3, "\033O*s"}, // keypad 3
|
|
{K_K4, "\033O*t"}, // keypad 4
|
|
{K_K5, "\033O*u"}, // keypad 5
|
|
{K_K6, "\033O*v"}, // keypad 6
|
|
{K_K7, "\033O*w"}, // keypad 7
|
|
{K_K8, "\033O*x"}, // keypad 8
|
|
{K_K9, "\033O*y"}, // keypad 9
|
|
{K_KDEL, "\033[3;*~"}, // keypad Del
|
|
{K_PS, "\033[200~"}, // paste start
|
|
{K_PE, "\033[201~"}, // paste end
|
|
|
|
{BT_EXTRA_KEYS, ""},
|
|
{TERMCAP2KEY('k', '0'), "\033[10;*~"}, // F0
|
|
{TERMCAP2KEY('F', '3'), "\033[25;*~"}, // F13
|
|
// F14 and F15 are missing, because they send the same codes as the undo
|
|
// and help key, although they don't work on all keyboards.
|
|
{TERMCAP2KEY('F', '6'), "\033[29;*~"}, // F16
|
|
{TERMCAP2KEY('F', '7'), "\033[31;*~"}, // F17
|
|
{TERMCAP2KEY('F', '8'), "\033[32;*~"}, // F18
|
|
{TERMCAP2KEY('F', '9'), "\033[33;*~"}, // F19
|
|
{TERMCAP2KEY('F', 'A'), "\033[34;*~"}, // F20
|
|
|
|
{TERMCAP2KEY('F', 'B'), "\033[42;*~"}, // F21
|
|
{TERMCAP2KEY('F', 'C'), "\033[43;*~"}, // F22
|
|
{TERMCAP2KEY('F', 'D'), "\033[44;*~"}, // F23
|
|
{TERMCAP2KEY('F', 'E'), "\033[45;*~"}, // F24
|
|
{TERMCAP2KEY('F', 'F'), "\033[46;*~"}, // F25
|
|
{TERMCAP2KEY('F', 'G'), "\033[47;*~"}, // F26
|
|
{TERMCAP2KEY('F', 'H'), "\033[48;*~"}, // F27
|
|
{TERMCAP2KEY('F', 'I'), "\033[49;*~"}, // F28
|
|
{TERMCAP2KEY('F', 'J'), "\033[50;*~"}, // F29
|
|
{TERMCAP2KEY('F', 'K'), "\033[51;*~"}, // F30
|
|
|
|
{TERMCAP2KEY('F', 'L'), "\033[52;*~"}, // F31
|
|
{TERMCAP2KEY('F', 'M'), "\033[53;*~"}, // F32
|
|
{TERMCAP2KEY('F', 'N'), "\033[54;*~"}, // F33
|
|
{TERMCAP2KEY('F', 'O'), "\033[55;*~"}, // F34
|
|
{TERMCAP2KEY('F', 'P'), "\033[56;*~"}, // F35
|
|
{TERMCAP2KEY('F', 'Q'), "\033[57;*~"}, // F36
|
|
{TERMCAP2KEY('F', 'R'), "\033[58;*~"}, // F37
|
|
|
|
{(int)KS_NAME, NULL} // end marker
|
|
};
|
|
|
|
/*
|
|
* Additions for using modifyOtherKeys level 2. Same as what is used for
|
|
* xterm.
|
|
*/
|
|
static tcap_entry_T builtin_mok2[] = {
|
|
// t_TI enables modifyOtherKeys level 2
|
|
{(int)KS_CTI, "\033[>4;2m"},
|
|
|
|
// XTQMODKEYS was added in xterm version 377: "CSI ? 4 m" which should
|
|
// return "{lead} > 4 ; Pv m". Before version 377 we expect it to have no
|
|
// effect.
|
|
{(int)KS_CRK, "\033[?4m"},
|
|
|
|
// t_TE disables modifyOtherKeys
|
|
{(int)KS_CTE, "\033[>4;m"},
|
|
|
|
{(int)KS_NAME, NULL} // end marker
|
|
};
|
|
|
|
/*
|
|
* Additions for using the Kitty keyboard protocol.
|
|
*/
|
|
static tcap_entry_T builtin_kitty[] = {
|
|
// t_TI enables the kitty keyboard protocol.
|
|
{(int)KS_CTI, "\033[=1;1u"},
|
|
|
|
// t_RK requests the kitty keyboard protocol state
|
|
{(int)KS_CRK, "\033[?u"},
|
|
|
|
// t_TE also disables modifyOtherKeys, because t_TI from xterm may already
|
|
// have been used.
|
|
{(int)KS_CTE, "\033[>4;m\033[=0;1u"},
|
|
|
|
{(int)KS_NAME, NULL} // end marker
|
|
};
|
|
|
|
#ifdef FEAT_TERMGUICOLORS
|
|
/*
|
|
* Additions for using the RGB colors and terminal font
|
|
*/
|
|
static tcap_entry_T builtin_rgb[] = {
|
|
// These are printf strings, not terminal codes.
|
|
{(int)KS_8F, "\033[38;2;%lu;%lu;%lum"},
|
|
{(int)KS_8B, "\033[48;2;%lu;%lu;%lum"},
|
|
{(int)KS_8U, "\033[58;2;%lu;%lu;%lum"},
|
|
|
|
{(int)KS_NAME, NULL} // end marker
|
|
};
|
|
#endif
|
|
|
|
#ifdef HAVE_TGETENT
|
|
static tcap_entry_T special_term[] = {
|
|
// These are printf strings, not terminal codes.
|
|
{(int)KS_CF, "\033[%dm"},
|
|
{(int)KS_NAME, NULL} // end marker
|
|
};
|
|
#endif
|
|
|
|
/*
|
|
* iris-ansi for Silicon Graphics machines.
|
|
*/
|
|
static tcap_entry_T builtin_iris_ansi[] = {
|
|
{(int)KS_CE, "\033[K"},
|
|
{(int)KS_CD, "\033[J"},
|
|
{(int)KS_AL, "\033[L"},
|
|
# ifdef TERMINFO
|
|
{(int)KS_CAL, "\033[%p1%dL"},
|
|
# else
|
|
{(int)KS_CAL, "\033[%dL"},
|
|
# endif
|
|
{(int)KS_DL, "\033[M"},
|
|
# ifdef TERMINFO
|
|
{(int)KS_CDL, "\033[%p1%dM"},
|
|
# else
|
|
{(int)KS_CDL, "\033[%dM"},
|
|
# endif
|
|
#if 0 // The scroll region is not working as Vim expects.
|
|
# ifdef TERMINFO
|
|
{(int)KS_CS, "\033[%i%p1%d;%p2%dr"},
|
|
# else
|
|
{(int)KS_CS, "\033[%i%d;%dr"},
|
|
# endif
|
|
#endif
|
|
{(int)KS_CL, "\033[H\033[2J"},
|
|
{(int)KS_VE, "\033[9/y\033[12/y"}, // These aren't documented
|
|
{(int)KS_VS, "\033[10/y\033[=1h\033[=2l"}, // These aren't documented
|
|
{(int)KS_TI, "\033[=6h"},
|
|
{(int)KS_TE, "\033[=6l"},
|
|
{(int)KS_SE, "\033[21;27m"},
|
|
{(int)KS_SO, "\033[1;7m"},
|
|
{(int)KS_ME, "\033[m"},
|
|
{(int)KS_MR, "\033[7m"},
|
|
{(int)KS_MD, "\033[1m"},
|
|
{(int)KS_CCO, "8"}, // allow 8 colors
|
|
{(int)KS_CZH, "\033[3m"}, // italic mode on
|
|
{(int)KS_CZR, "\033[23m"}, // italic mode off
|
|
{(int)KS_US, "\033[4m"}, // underline on
|
|
{(int)KS_UE, "\033[24m"}, // underline off
|
|
# ifdef TERMINFO
|
|
{(int)KS_CAB, "\033[4%p1%dm"}, // set background color (ANSI)
|
|
{(int)KS_CAF, "\033[3%p1%dm"}, // set foreground color (ANSI)
|
|
{(int)KS_CSB, "\033[102;%p1%dm"}, // set screen background color
|
|
{(int)KS_CSF, "\033[101;%p1%dm"}, // set screen foreground color
|
|
# else
|
|
{(int)KS_CAB, "\033[4%dm"}, // set background color (ANSI)
|
|
{(int)KS_CAF, "\033[3%dm"}, // set foreground color (ANSI)
|
|
{(int)KS_CSB, "\033[102;%dm"}, // set screen background color
|
|
{(int)KS_CSF, "\033[101;%dm"}, // set screen foreground color
|
|
# endif
|
|
{(int)KS_MS, "y"}, // guessed
|
|
{(int)KS_UT, "y"}, // guessed
|
|
{(int)KS_LE, "\b"},
|
|
# ifdef TERMINFO
|
|
{(int)KS_CM, "\033[%i%p1%d;%p2%dH"},
|
|
# else
|
|
{(int)KS_CM, "\033[%i%d;%dH"},
|
|
# endif
|
|
{(int)KS_SR, "\033M"},
|
|
# ifdef TERMINFO
|
|
{(int)KS_CRI, "\033[%p1%dC"},
|
|
# else
|
|
{(int)KS_CRI, "\033[%dC"},
|
|
# endif
|
|
{(int)KS_CIS, "\033P3.y"},
|
|
{(int)KS_CIE, "\234"}, // ST "String Terminator"
|
|
{(int)KS_TS, "\033P1.y"},
|
|
{(int)KS_FS, "\234"}, // ST "String Terminator"
|
|
# ifdef TERMINFO
|
|
{(int)KS_CWS, "\033[203;%p1%d;%p2%d/y"},
|
|
{(int)KS_CWP, "\033[205;%p1%d;%p2%d/y"},
|
|
# else
|
|
{(int)KS_CWS, "\033[203;%d;%d/y"},
|
|
{(int)KS_CWP, "\033[205;%d;%d/y"},
|
|
# endif
|
|
{K_UP, "\033[A"},
|
|
{K_DOWN, "\033[B"},
|
|
{K_LEFT, "\033[D"},
|
|
{K_RIGHT, "\033[C"},
|
|
{K_S_UP, "\033[161q"},
|
|
{K_S_DOWN, "\033[164q"},
|
|
{K_S_LEFT, "\033[158q"},
|
|
{K_S_RIGHT, "\033[167q"},
|
|
{K_F1, "\033[001q"},
|
|
{K_F2, "\033[002q"},
|
|
{K_F3, "\033[003q"},
|
|
{K_F4, "\033[004q"},
|
|
{K_F5, "\033[005q"},
|
|
{K_F6, "\033[006q"},
|
|
{K_F7, "\033[007q"},
|
|
{K_F8, "\033[008q"},
|
|
{K_F9, "\033[009q"},
|
|
{K_F10, "\033[010q"},
|
|
{K_F11, "\033[011q"},
|
|
{K_F12, "\033[012q"},
|
|
{K_S_F1, "\033[013q"},
|
|
{K_S_F2, "\033[014q"},
|
|
{K_S_F3, "\033[015q"},
|
|
{K_S_F4, "\033[016q"},
|
|
{K_S_F5, "\033[017q"},
|
|
{K_S_F6, "\033[018q"},
|
|
{K_S_F7, "\033[019q"},
|
|
{K_S_F8, "\033[020q"},
|
|
{K_S_F9, "\033[021q"},
|
|
{K_S_F10, "\033[022q"},
|
|
{K_S_F11, "\033[023q"},
|
|
{K_S_F12, "\033[024q"},
|
|
{K_INS, "\033[139q"},
|
|
{K_HOME, "\033[H"},
|
|
{K_END, "\033[146q"},
|
|
{K_PAGEUP, "\033[150q"},
|
|
{K_PAGEDOWN, "\033[154q"},
|
|
|
|
{(int)KS_NAME, NULL} // end marker
|
|
};
|
|
|
|
/*
|
|
* These codes are valid when nansi.sys or equivalent has been installed.
|
|
* Function keys on a PC are preceded with a NUL. These are converted into
|
|
* K_NUL '\316' in mch_inchar(), because we cannot handle NULs in key codes.
|
|
* CTRL-arrow is used instead of SHIFT-arrow.
|
|
*/
|
|
static tcap_entry_T builtin_pcansi[] = {
|
|
{(int)KS_DL, "\033[M"},
|
|
{(int)KS_AL, "\033[L"},
|
|
{(int)KS_CE, "\033[K"},
|
|
{(int)KS_CL, "\033[2J"},
|
|
{(int)KS_ME, "\033[0m"},
|
|
{(int)KS_MR, "\033[5m"}, // reverse: black on lightgrey
|
|
{(int)KS_MD, "\033[1m"}, // bold: white text
|
|
{(int)KS_SE, "\033[0m"}, // standout end
|
|
{(int)KS_SO, "\033[31m"}, // standout: white on blue
|
|
{(int)KS_CZH, "\033[34;43m"}, // italic mode: blue text on yellow
|
|
{(int)KS_CZR, "\033[0m"}, // italic mode end
|
|
{(int)KS_US, "\033[36;41m"}, // underscore mode: cyan text on red
|
|
{(int)KS_UE, "\033[0m"}, // underscore mode end
|
|
{(int)KS_CCO, "8"}, // allow 8 colors
|
|
# ifdef TERMINFO
|
|
{(int)KS_CAB, "\033[4%p1%dm"},// set background color
|
|
{(int)KS_CAF, "\033[3%p1%dm"},// set foreground color
|
|
# else
|
|
{(int)KS_CAB, "\033[4%dm"}, // set background color
|
|
{(int)KS_CAF, "\033[3%dm"}, // set foreground color
|
|
# endif
|
|
{(int)KS_OP, "\033[0m"}, // reset colors
|
|
{(int)KS_MS, "y"},
|
|
{(int)KS_UT, "y"}, // guessed
|
|
{(int)KS_LE, "\b"},
|
|
# ifdef TERMINFO
|
|
{(int)KS_CM, "\033[%i%p1%d;%p2%dH"},
|
|
# else
|
|
{(int)KS_CM, "\033[%i%d;%dH"},
|
|
# endif
|
|
# ifdef TERMINFO
|
|
{(int)KS_CRI, "\033[%p1%dC"},
|
|
# else
|
|
{(int)KS_CRI, "\033[%dC"},
|
|
# endif
|
|
{K_UP, "\316H"},
|
|
{K_DOWN, "\316P"},
|
|
{K_LEFT, "\316K"},
|
|
{K_RIGHT, "\316M"},
|
|
{K_S_LEFT, "\316s"},
|
|
{K_S_RIGHT, "\316t"},
|
|
{K_F1, "\316;"},
|
|
{K_F2, "\316<"},
|
|
{K_F3, "\316="},
|
|
{K_F4, "\316>"},
|
|
{K_F5, "\316?"},
|
|
{K_F6, "\316@"},
|
|
{K_F7, "\316A"},
|
|
{K_F8, "\316B"},
|
|
{K_F9, "\316C"},
|
|
{K_F10, "\316D"},
|
|
{K_F11, "\316\205"}, // guessed
|
|
{K_F12, "\316\206"}, // guessed
|
|
{K_S_F1, "\316T"},
|
|
{K_S_F2, "\316U"},
|
|
{K_S_F3, "\316V"},
|
|
{K_S_F4, "\316W"},
|
|
{K_S_F5, "\316X"},
|
|
{K_S_F6, "\316Y"},
|
|
{K_S_F7, "\316Z"},
|
|
{K_S_F8, "\316["},
|
|
{K_S_F9, "\316\\"},
|
|
{K_S_F10, "\316]"},
|
|
{K_S_F11, "\316\207"}, // guessed
|
|
{K_S_F12, "\316\210"}, // guessed
|
|
{K_INS, "\316R"},
|
|
{K_DEL, "\316S"},
|
|
{K_HOME, "\316G"},
|
|
{K_END, "\316O"},
|
|
{K_PAGEDOWN, "\316Q"},
|
|
{K_PAGEUP, "\316I"},
|
|
|
|
{(int)KS_NAME, NULL} // end marker
|
|
};
|
|
|
|
/*
|
|
* These codes are valid for the Win32 Console. The entries that start with
|
|
* ESC | are translated into console calls in os_win32.c. The function keys
|
|
* are also translated in os_win32.c.
|
|
*/
|
|
static tcap_entry_T builtin_win32[] = {
|
|
{(int)KS_CE, "\033|K"}, // clear to end of line
|
|
{(int)KS_AL, "\033|L"}, // add new blank line
|
|
# ifdef TERMINFO
|
|
{(int)KS_CAL, "\033|%p1%dL"}, // add number of new blank lines
|
|
# else
|
|
{(int)KS_CAL, "\033|%dL"}, // add number of new blank lines
|
|
# endif
|
|
{(int)KS_DL, "\033|M"}, // delete line
|
|
# ifdef TERMINFO
|
|
{(int)KS_CDL, "\033|%p1%dM"}, // delete number of lines
|
|
{(int)KS_CSV, "\033|%p1%d;%p2%dV"},
|
|
# else
|
|
{(int)KS_CDL, "\033|%dM"}, // delete number of lines
|
|
{(int)KS_CSV, "\033|%d;%dV"},
|
|
# endif
|
|
{(int)KS_CL, "\033|J"}, // clear screen
|
|
{(int)KS_CD, "\033|j"}, // clear to end of display
|
|
{(int)KS_VI, "\033|v"}, // cursor invisible
|
|
{(int)KS_VE, "\033|V"}, // cursor visible
|
|
|
|
{(int)KS_ME, "\033|0m"}, // normal
|
|
{(int)KS_MR, "\033|112m"}, // reverse: black on lightgray
|
|
{(int)KS_MD, "\033|15m"}, // bold: white on black
|
|
#if 1
|
|
{(int)KS_SO, "\033|31m"}, // standout: white on blue
|
|
{(int)KS_SE, "\033|0m"}, // standout end
|
|
#else
|
|
{(int)KS_SO, "\033|F"}, // standout: high intensity
|
|
{(int)KS_SE, "\033|f"}, // standout end
|
|
#endif
|
|
{(int)KS_CZH, "\033|225m"}, // italic: blue text on yellow
|
|
{(int)KS_CZR, "\033|0m"}, // italic end
|
|
{(int)KS_US, "\033|67m"}, // underscore: cyan text on red
|
|
{(int)KS_UE, "\033|0m"}, // underscore end
|
|
{(int)KS_CCO, "16"}, // allow 16 colors
|
|
# ifdef TERMINFO
|
|
{(int)KS_CAB, "\033|%p1%db"}, // set background color
|
|
{(int)KS_CAF, "\033|%p1%df"}, // set foreground color
|
|
# else
|
|
{(int)KS_CAB, "\033|%db"}, // set background color
|
|
{(int)KS_CAF, "\033|%df"}, // set foreground color
|
|
# endif
|
|
|
|
{(int)KS_MS, "y"}, // save to move cur in reverse mode
|
|
{(int)KS_UT, "y"},
|
|
{(int)KS_XN, "y"},
|
|
{(int)KS_LE, "\b"},
|
|
# ifdef TERMINFO
|
|
{(int)KS_CM, "\033|%i%p1%d;%p2%dH"}, // cursor motion
|
|
# else
|
|
{(int)KS_CM, "\033|%i%d;%dH"}, // cursor motion
|
|
# endif
|
|
{(int)KS_VB, "\033|B"}, // visual bell
|
|
{(int)KS_TI, "\033|S"}, // put terminal in termcap mode
|
|
{(int)KS_TE, "\033|E"}, // out of termcap mode
|
|
# ifdef TERMINFO
|
|
{(int)KS_CS, "\033|%i%p1%d;%p2%dr"}, // scroll region
|
|
# else
|
|
{(int)KS_CS, "\033|%i%d;%dr"}, // scroll region
|
|
# endif
|
|
|
|
{K_UP, "\316H"},
|
|
{K_DOWN, "\316P"},
|
|
{K_LEFT, "\316K"},
|
|
{K_RIGHT, "\316M"},
|
|
{K_S_UP, "\316\304"},
|
|
{K_S_DOWN, "\316\317"},
|
|
{K_S_LEFT, "\316\311"},
|
|
{K_C_LEFT, "\316s"},
|
|
{K_S_RIGHT, "\316\313"},
|
|
{K_C_RIGHT, "\316t"},
|
|
{K_S_TAB, "\316\017"},
|
|
{K_F1, "\316;"},
|
|
{K_F2, "\316<"},
|
|
{K_F3, "\316="},
|
|
{K_F4, "\316>"},
|
|
{K_F5, "\316?"},
|
|
{K_F6, "\316@"},
|
|
{K_F7, "\316A"},
|
|
{K_F8, "\316B"},
|
|
{K_F9, "\316C"},
|
|
{K_F10, "\316D"},
|
|
{K_F11, "\316\205"},
|
|
{K_F12, "\316\206"},
|
|
{K_S_F1, "\316T"},
|
|
{K_S_F2, "\316U"},
|
|
{K_S_F3, "\316V"},
|
|
{K_S_F4, "\316W"},
|
|
{K_S_F5, "\316X"},
|
|
{K_S_F6, "\316Y"},
|
|
{K_S_F7, "\316Z"},
|
|
{K_S_F8, "\316["},
|
|
{K_S_F9, "\316\\"},
|
|
{K_S_F10, "\316]"},
|
|
{K_S_F11, "\316\207"},
|
|
{K_S_F12, "\316\210"},
|
|
{K_INS, "\316R"},
|
|
{K_DEL, "\316S"},
|
|
{K_HOME, "\316G"},
|
|
{K_S_HOME, "\316\302"},
|
|
{K_C_HOME, "\316w"},
|
|
{K_END, "\316O"},
|
|
{K_S_END, "\316\315"},
|
|
{K_C_END, "\316u"},
|
|
{K_PAGEDOWN, "\316Q"},
|
|
{K_PAGEUP, "\316I"},
|
|
{K_KPLUS, "\316N"},
|
|
{K_KMINUS, "\316J"},
|
|
{K_KMULTIPLY, "\316\067"},
|
|
{K_K0, "\316\332"},
|
|
{K_K1, "\316\336"},
|
|
{K_K2, "\316\342"},
|
|
{K_K3, "\316\346"},
|
|
{K_K4, "\316\352"},
|
|
{K_K5, "\316\356"},
|
|
{K_K6, "\316\362"},
|
|
{K_K7, "\316\366"},
|
|
{K_K8, "\316\372"},
|
|
{K_K9, "\316\376"},
|
|
{K_BS, "\316x"},
|
|
{K_S_BS, "\316y"},
|
|
|
|
{(int)KS_NAME, NULL} // end marker
|
|
};
|
|
|
|
#if defined(FEAT_GUI)
|
|
/*
|
|
* GUI uses made-up codes, only used inside Vim.
|
|
*/
|
|
static tcap_entry_T builtin_gui[] = {
|
|
{(int)KS_CE, "\033|$"},
|
|
{(int)KS_AL, "\033|i"},
|
|
# ifdef TERMINFO
|
|
{(int)KS_CAL, "\033|%p1%dI"},
|
|
# else
|
|
{(int)KS_CAL, "\033|%dI"},
|
|
# endif
|
|
{(int)KS_DL, "\033|d"},
|
|
# ifdef TERMINFO
|
|
{(int)KS_CDL, "\033|%p1%dD"},
|
|
{(int)KS_CS, "\033|%p1%d;%p2%dR"},
|
|
{(int)KS_CSV, "\033|%p1%d;%p2%dV"},
|
|
# else
|
|
{(int)KS_CDL, "\033|%dD"},
|
|
{(int)KS_CS, "\033|%d;%dR"},
|
|
{(int)KS_CSV, "\033|%d;%dV"},
|
|
# endif
|
|
{(int)KS_CL, "\033|C"},
|
|
// attributes switched on with 'h', off with * 'H'
|
|
{(int)KS_ME, "\033|31H"}, // HL_ALL
|
|
{(int)KS_MR, "\033|1h"}, // HL_INVERSE
|
|
{(int)KS_MD, "\033|2h"}, // HL_BOLD
|
|
{(int)KS_SE, "\033|16H"}, // HL_STANDOUT
|
|
{(int)KS_SO, "\033|16h"}, // HL_STANDOUT
|
|
{(int)KS_UE, "\033|8H"}, // HL_UNDERLINE
|
|
{(int)KS_US, "\033|8h"}, // HL_UNDERLINE
|
|
{(int)KS_UCE, "\033|8C"}, // HL_UNDERCURL
|
|
{(int)KS_UCS, "\033|8c"}, // HL_UNDERCURL
|
|
{(int)KS_STE, "\033|4C"}, // HL_STRIKETHROUGH
|
|
{(int)KS_STS, "\033|4c"}, // HL_STRIKETHROUGH
|
|
{(int)KS_CZR, "\033|4H"}, // HL_ITALIC
|
|
{(int)KS_CZH, "\033|4h"}, // HL_ITALIC
|
|
{(int)KS_VB, "\033|f"},
|
|
{(int)KS_MS, "y"},
|
|
{(int)KS_UT, "y"},
|
|
{(int)KS_XN, "y"},
|
|
{(int)KS_LE, "\b"}, // cursor-left = BS
|
|
{(int)KS_ND, "\014"}, // cursor-right = CTRL-L
|
|
# ifdef TERMINFO
|
|
{(int)KS_CM, "\033|%p1%d;%p2%dM"},
|
|
# else
|
|
{(int)KS_CM, "\033|%d;%dM"},
|
|
# endif
|
|
// there are no key sequences here, the GUI sequences are recognized
|
|
// in check_termcode()
|
|
|
|
{(int)KS_NAME, NULL} // end marker
|
|
};
|
|
#endif
|
|
|
|
/*
|
|
* Amiga console window, default for Amiga.
|
|
*/
|
|
static tcap_entry_T builtin_amiga[] = {
|
|
{(int)KS_CE, "\033[K"},
|
|
{(int)KS_CD, "\033[J"},
|
|
{(int)KS_AL, "\033[L"},
|
|
# ifdef TERMINFO
|
|
{(int)KS_CAL, "\033[%p1%dL"},
|
|
# else
|
|
{(int)KS_CAL, "\033[%dL"},
|
|
# endif
|
|
{(int)KS_DL, "\033[M"},
|
|
# ifdef TERMINFO
|
|
{(int)KS_CDL, "\033[%p1%dM"},
|
|
# else
|
|
{(int)KS_CDL, "\033[%dM"},
|
|
# endif
|
|
{(int)KS_CL, "\014"},
|
|
{(int)KS_VI, "\033[0 p"},
|
|
{(int)KS_VE, "\033[1 p"},
|
|
{(int)KS_ME, "\033[0m"},
|
|
{(int)KS_MR, "\033[7m"},
|
|
{(int)KS_MD, "\033[1m"},
|
|
{(int)KS_SE, "\033[0m"},
|
|
{(int)KS_SO, "\033[33m"},
|
|
{(int)KS_US, "\033[4m"},
|
|
{(int)KS_UE, "\033[0m"},
|
|
{(int)KS_CZH, "\033[3m"},
|
|
{(int)KS_CZR, "\033[0m"},
|
|
#if defined(__amigaos4__) || defined(__MORPHOS__) || defined(__AROS__)
|
|
{(int)KS_CCO, "8"}, // allow 8 colors
|
|
# ifdef TERMINFO
|
|
{(int)KS_CAB, "\033[4%p1%dm"},// set background color
|
|
{(int)KS_CAF, "\033[3%p1%dm"},// set foreground color
|
|
# else
|
|
{(int)KS_CAB, "\033[4%dm"}, // set background color
|
|
{(int)KS_CAF, "\033[3%dm"}, // set foreground color
|
|
# endif
|
|
{(int)KS_OP, "\033[m"}, // reset colors
|
|
#endif
|
|
{(int)KS_MS, "y"},
|
|
{(int)KS_UT, "y"}, // guessed
|
|
{(int)KS_LE, "\b"},
|
|
# ifdef TERMINFO
|
|
{(int)KS_CM, "\033[%i%p1%d;%p2%dH"},
|
|
# else
|
|
{(int)KS_CM, "\033[%i%d;%dH"},
|
|
# endif
|
|
#if defined(__MORPHOS__)
|
|
{(int)KS_SR, "\033M"},
|
|
#endif
|
|
# ifdef TERMINFO
|
|
{(int)KS_CRI, "\033[%p1%dC"},
|
|
# else
|
|
{(int)KS_CRI, "\033[%dC"},
|
|
# endif
|
|
{K_UP, "\233A"},
|
|
{K_DOWN, "\233B"},
|
|
{K_LEFT, "\233D"},
|
|
{K_RIGHT, "\233C"},
|
|
{K_S_UP, "\233T"},
|
|
{K_S_DOWN, "\233S"},
|
|
{K_S_LEFT, "\233 A"},
|
|
{K_S_RIGHT, "\233 @"},
|
|
{K_S_TAB, "\233Z"},
|
|
{K_F1, "\233\060~"},// some compilers don't dig "\2330"
|
|
{K_F2, "\233\061~"},
|
|
{K_F3, "\233\062~"},
|
|
{K_F4, "\233\063~"},
|
|
{K_F5, "\233\064~"},
|
|
{K_F6, "\233\065~"},
|
|
{K_F7, "\233\066~"},
|
|
{K_F8, "\233\067~"},
|
|
{K_F9, "\233\070~"},
|
|
{K_F10, "\233\071~"},
|
|
{K_S_F1, "\233\061\060~"},
|
|
{K_S_F2, "\233\061\061~"},
|
|
{K_S_F3, "\233\061\062~"},
|
|
{K_S_F4, "\233\061\063~"},
|
|
{K_S_F5, "\233\061\064~"},
|
|
{K_S_F6, "\233\061\065~"},
|
|
{K_S_F7, "\233\061\066~"},
|
|
{K_S_F8, "\233\061\067~"},
|
|
{K_S_F9, "\233\061\070~"},
|
|
{K_S_F10, "\233\061\071~"},
|
|
{K_HELP, "\233?~"},
|
|
{K_INS, "\233\064\060~"}, // 101 key keyboard
|
|
{K_PAGEUP, "\233\064\061~"}, // 101 key keyboard
|
|
{K_PAGEDOWN, "\233\064\062~"}, // 101 key keyboard
|
|
{K_HOME, "\233\064\064~"}, // 101 key keyboard
|
|
{K_END, "\233\064\065~"}, // 101 key keyboard
|
|
|
|
{BT_EXTRA_KEYS, ""},
|
|
{TERMCAP2KEY('#', '2'), "\233\065\064~"}, // shifted home key
|
|
{TERMCAP2KEY('#', '3'), "\233\065\060~"}, // shifted insert key
|
|
{TERMCAP2KEY('*', '7'), "\233\065\065~"}, // shifted end key
|
|
|
|
{(int)KS_NAME, NULL} // end marker
|
|
};
|
|
|
|
/*
|
|
* The most minimal terminal: only clear screen and cursor positioning.
|
|
*/
|
|
static tcap_entry_T builtin_dumb[] = {
|
|
{(int)KS_CL, "\014"},
|
|
#ifdef TERMINFO
|
|
{(int)KS_CM, "\033[%i%p1%d;%p2%dH"},
|
|
#else
|
|
{(int)KS_CM, "\033[%i%d;%dH"},
|
|
#endif
|
|
|
|
{(int)KS_NAME, NULL} // end marker
|
|
};
|
|
|
|
/*
|
|
* Terminal used for debugging.
|
|
*/
|
|
static tcap_entry_T builtin_debug[] = {
|
|
{(int)KS_CE, "[CE]"},
|
|
{(int)KS_CD, "[CD]"},
|
|
{(int)KS_AL, "[AL]"},
|
|
# ifdef TERMINFO
|
|
{(int)KS_CAL, "[CAL%p1%d]"},
|
|
# else
|
|
{(int)KS_CAL, "[CAL%d]"},
|
|
# endif
|
|
{(int)KS_DL, "[DL]"},
|
|
# ifdef TERMINFO
|
|
{(int)KS_CDL, "[CDL%p1%d]"},
|
|
# else
|
|
{(int)KS_CDL, "[CDL%d]"},
|
|
# endif
|
|
# ifdef TERMINFO
|
|
{(int)KS_CS, "[%p1%dCS%p2%d]"},
|
|
# else
|
|
{(int)KS_CS, "[%dCS%d]"},
|
|
# endif
|
|
# ifdef TERMINFO
|
|
{(int)KS_CSV, "[%p1%dCSV%p2%d]"},
|
|
# else
|
|
{(int)KS_CSV, "[%dCSV%d]"},
|
|
# endif
|
|
# ifdef TERMINFO
|
|
{(int)KS_CAB, "[CAB%p1%d]"},
|
|
{(int)KS_CAF, "[CAF%p1%d]"},
|
|
{(int)KS_CSB, "[CSB%p1%d]"},
|
|
{(int)KS_CSF, "[CSF%p1%d]"},
|
|
# else
|
|
{(int)KS_CAB, "[CAB%d]"},
|
|
{(int)KS_CAF, "[CAF%d]"},
|
|
{(int)KS_CSB, "[CSB%d]"},
|
|
{(int)KS_CSF, "[CSF%d]"},
|
|
# endif
|
|
{(int)KS_CAU, "[CAU%d]"},
|
|
{(int)KS_OP, "[OP]"},
|
|
{(int)KS_LE, "[LE]"},
|
|
{(int)KS_CL, "[CL]"},
|
|
{(int)KS_VI, "[VI]"},
|
|
{(int)KS_VE, "[VE]"},
|
|
{(int)KS_VS, "[VS]"},
|
|
{(int)KS_ME, "[ME]"},
|
|
{(int)KS_MR, "[MR]"},
|
|
{(int)KS_MB, "[MB]"},
|
|
{(int)KS_MD, "[MD]"},
|
|
{(int)KS_SE, "[SE]"},
|
|
{(int)KS_SO, "[SO]"},
|
|
{(int)KS_UE, "[UE]"},
|
|
{(int)KS_US, "[US]"},
|
|
{(int)KS_UCE, "[UCE]"},
|
|
{(int)KS_UCS, "[UCS]"},
|
|
{(int)KS_USS, "[USS]"},
|
|
{(int)KS_DS, "[DS]"},
|
|
{(int)KS_CDS, "[CDS]"},
|
|
{(int)KS_STE, "[STE]"},
|
|
{(int)KS_STS, "[STS]"},
|
|
{(int)KS_MS, "[MS]"},
|
|
{(int)KS_UT, "[UT]"},
|
|
{(int)KS_XN, "[XN]"},
|
|
# ifdef TERMINFO
|
|
{(int)KS_CM, "[%p1%dCM%p2%d]"},
|
|
# else
|
|
{(int)KS_CM, "[%dCM%d]"},
|
|
# endif
|
|
{(int)KS_SR, "[SR]"},
|
|
# ifdef TERMINFO
|
|
{(int)KS_CRI, "[CRI%p1%d]"},
|
|
# else
|
|
{(int)KS_CRI, "[CRI%d]"},
|
|
# endif
|
|
{(int)KS_VB, "[VB]"},
|
|
{(int)KS_KS, "[KS]"},
|
|
{(int)KS_KE, "[KE]"},
|
|
{(int)KS_TI, "[TI]"},
|
|
{(int)KS_TE, "[TE]"},
|
|
{(int)KS_CIS, "[CIS]"},
|
|
{(int)KS_CIE, "[CIE]"},
|
|
{(int)KS_CSC, "[CSC]"},
|
|
{(int)KS_CEC, "[CEC]"},
|
|
{(int)KS_TS, "[TS]"},
|
|
{(int)KS_FS, "[FS]"},
|
|
# ifdef TERMINFO
|
|
{(int)KS_CWS, "[%p1%dCWS%p2%d]"},
|
|
{(int)KS_CWP, "[%p1%dCWP%p2%d]"},
|
|
# else
|
|
{(int)KS_CWS, "[%dCWS%d]"},
|
|
{(int)KS_CWP, "[%dCWP%d]"},
|
|
# endif
|
|
{(int)KS_CRV, "[CRV]"},
|
|
{(int)KS_CXM, "[CXM]"},
|
|
{(int)KS_U7, "[U7]"},
|
|
{(int)KS_RFG, "[RFG]"},
|
|
{(int)KS_RBG, "[RBG]"},
|
|
{(int)KS_CF, "[CF%d]"},
|
|
{K_UP, "[KU]"},
|
|
{K_DOWN, "[KD]"},
|
|
{K_LEFT, "[KL]"},
|
|
{K_RIGHT, "[KR]"},
|
|
{K_XUP, "[xKU]"},
|
|
{K_XDOWN, "[xKD]"},
|
|
{K_XLEFT, "[xKL]"},
|
|
{K_XRIGHT, "[xKR]"},
|
|
{K_S_UP, "[S-KU]"},
|
|
{K_S_DOWN, "[S-KD]"},
|
|
{K_S_LEFT, "[S-KL]"},
|
|
{K_C_LEFT, "[C-KL]"},
|
|
{K_S_RIGHT, "[S-KR]"},
|
|
{K_C_RIGHT, "[C-KR]"},
|
|
{K_F1, "[F1]"},
|
|
{K_XF1, "[xF1]"},
|
|
{K_F2, "[F2]"},
|
|
{K_XF2, "[xF2]"},
|
|
{K_F3, "[F3]"},
|
|
{K_XF3, "[xF3]"},
|
|
{K_F4, "[F4]"},
|
|
{K_XF4, "[xF4]"},
|
|
{K_F5, "[F5]"},
|
|
{K_F6, "[F6]"},
|
|
{K_F7, "[F7]"},
|
|
{K_F8, "[F8]"},
|
|
{K_F9, "[F9]"},
|
|
{K_F10, "[F10]"},
|
|
{K_F11, "[F11]"},
|
|
{K_F12, "[F12]"},
|
|
{K_S_F1, "[S-F1]"},
|
|
{K_S_XF1, "[S-xF1]"},
|
|
{K_S_F2, "[S-F2]"},
|
|
{K_S_XF2, "[S-xF2]"},
|
|
{K_S_F3, "[S-F3]"},
|
|
{K_S_XF3, "[S-xF3]"},
|
|
{K_S_F4, "[S-F4]"},
|
|
{K_S_XF4, "[S-xF4]"},
|
|
{K_S_F5, "[S-F5]"},
|
|
{K_S_F6, "[S-F6]"},
|
|
{K_S_F7, "[S-F7]"},
|
|
{K_S_F8, "[S-F8]"},
|
|
{K_S_F9, "[S-F9]"},
|
|
{K_S_F10, "[S-F10]"},
|
|
{K_S_F11, "[S-F11]"},
|
|
{K_S_F12, "[S-F12]"},
|
|
{K_HELP, "[HELP]"},
|
|
{K_UNDO, "[UNDO]"},
|
|
{K_BS, "[BS]"},
|
|
{K_INS, "[INS]"},
|
|
{K_KINS, "[KINS]"},
|
|
{K_DEL, "[DEL]"},
|
|
{K_KDEL, "[KDEL]"},
|
|
{K_HOME, "[HOME]"},
|
|
{K_S_HOME, "[C-HOME]"},
|
|
{K_C_HOME, "[C-HOME]"},
|
|
{K_KHOME, "[KHOME]"},
|
|
{K_XHOME, "[XHOME]"},
|
|
{K_ZHOME, "[ZHOME]"},
|
|
{K_END, "[END]"},
|
|
{K_S_END, "[C-END]"},
|
|
{K_C_END, "[C-END]"},
|
|
{K_KEND, "[KEND]"},
|
|
{K_XEND, "[XEND]"},
|
|
{K_ZEND, "[ZEND]"},
|
|
{K_PAGEUP, "[PAGEUP]"},
|
|
{K_PAGEDOWN, "[PAGEDOWN]"},
|
|
{K_KPAGEUP, "[KPAGEUP]"},
|
|
{K_KPAGEDOWN, "[KPAGEDOWN]"},
|
|
{K_MOUSE, "[MOUSE]"},
|
|
{K_KPLUS, "[KPLUS]"},
|
|
{K_KMINUS, "[KMINUS]"},
|
|
{K_KDIVIDE, "[KDIVIDE]"},
|
|
{K_KMULTIPLY, "[KMULTIPLY]"},
|
|
{K_KENTER, "[KENTER]"},
|
|
{K_KPOINT, "[KPOINT]"},
|
|
{K_PS, "[PASTE-START]"},
|
|
{K_PE, "[PASTE-END]"},
|
|
{K_K0, "[K0]"},
|
|
{K_K1, "[K1]"},
|
|
{K_K2, "[K2]"},
|
|
{K_K3, "[K3]"},
|
|
{K_K4, "[K4]"},
|
|
{K_K5, "[K5]"},
|
|
{K_K6, "[K6]"},
|
|
{K_K7, "[K7]"},
|
|
{K_K8, "[K8]"},
|
|
{K_K9, "[K9]"},
|
|
|
|
{(int)KS_NAME, NULL} // end marker
|
|
};
|
|
|
|
/*
|
|
* List of builtin terminals.
|
|
*/
|
|
typedef struct {
|
|
char *bitc_name; // name, such as "xterm"
|
|
tcap_entry_T *bitc_table; // table with entries for bitc_name
|
|
} builtin_tcap_T;
|
|
|
|
builtin_tcap_T builtin_terminals[] = {
|
|
// Unix and Generic
|
|
{"ansi", builtin_ansi},
|
|
{"vt320", builtin_vt320},
|
|
{"vt52", builtin_vt52},
|
|
{"xterm", builtin_xterm},
|
|
{"iris-ansi", builtin_iris_ansi},
|
|
|
|
// MS-Windows
|
|
{"pcansi", builtin_pcansi},
|
|
{"win32", builtin_win32},
|
|
|
|
// Other systems
|
|
#if defined(FEAT_GUI)
|
|
{"gui", builtin_gui},
|
|
#endif
|
|
{"amiga", builtin_amiga},
|
|
{"dumb", builtin_dumb},
|
|
{"debug", builtin_debug},
|
|
|
|
{NULL, NULL}, // end marker
|
|
};
|
|
|
|
#if defined(FEAT_TERMGUICOLORS) || defined(PROTO)
|
|
static guicolor_T
|
|
termgui_mch_get_color(char_u *name)
|
|
{
|
|
return gui_get_color_cmn(name);
|
|
}
|
|
|
|
guicolor_T
|
|
termgui_get_color(char_u *name)
|
|
{
|
|
guicolor_T t;
|
|
|
|
if (*name == NUL)
|
|
return INVALCOLOR;
|
|
t = termgui_mch_get_color(name);
|
|
|
|
if (t == INVALCOLOR)
|
|
semsg(_(e_cannot_allocate_color_str), name);
|
|
return t;
|
|
}
|
|
|
|
guicolor_T
|
|
termgui_mch_get_rgb(guicolor_T color)
|
|
{
|
|
return color;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* DEFAULT_TERM is used, when no terminal is specified with -T option or $TERM.
|
|
*/
|
|
#ifdef AMIGA
|
|
# define DEFAULT_TERM (char_u *)"amiga"
|
|
#endif
|
|
|
|
#ifdef MSWIN
|
|
# define DEFAULT_TERM (char_u *)"win32"
|
|
#endif
|
|
|
|
#if defined(UNIX)
|
|
# define DEFAULT_TERM (char_u *)"ansi"
|
|
#endif
|
|
|
|
#ifdef VMS
|
|
# define DEFAULT_TERM (char_u *)"vt320"
|
|
#endif
|
|
|
|
#ifdef __HAIKU__
|
|
# undef DEFAULT_TERM
|
|
# define DEFAULT_TERM (char_u *)"xterm"
|
|
#endif
|
|
|
|
#ifndef DEFAULT_TERM
|
|
# define DEFAULT_TERM (char_u *)"dumb"
|
|
#endif
|
|
|
|
/*
|
|
* Term_strings contains currently used terminal output strings.
|
|
* It is initialized with the default values by parse_builtin_tcap().
|
|
* The values can be changed by setting the option with the same name.
|
|
*/
|
|
char_u *(term_strings[(int)KS_LAST + 1]);
|
|
|
|
static int need_gather = FALSE; // need to fill termleader[]
|
|
static char_u termleader[256 + 1]; // for check_termcode()
|
|
#ifdef FEAT_TERMRESPONSE
|
|
static int check_for_codes = FALSE; // check for key code response
|
|
#endif
|
|
|
|
/*
|
|
* Structure and table to store terminal features that can be detected by
|
|
* querying the terminal. Either by inspecting the termresponse or a more
|
|
* specific request. Besides this there are:
|
|
* t_colors - number of colors supported
|
|
*/
|
|
typedef struct {
|
|
char *tpr_name;
|
|
int tpr_set_by_termresponse;
|
|
int tpr_status;
|
|
} termprop_T;
|
|
|
|
// Values for tpr_status.
|
|
#define TPR_UNKNOWN 'u'
|
|
#define TPR_YES 'y'
|
|
#define TPR_NO 'n'
|
|
#define TPR_MOUSE_XTERM 'x' // use "xterm" for 'ttymouse'
|
|
#define TPR_MOUSE_XTERM2 '2' // use "xterm2" for 'ttymouse'
|
|
#define TPR_MOUSE_SGR 's' // use "sgr" for 'ttymouse'
|
|
|
|
// can request the cursor style without messing up the display
|
|
#define TPR_CURSOR_STYLE 0
|
|
// can request the cursor blink mode without messing up the display
|
|
#define TPR_CURSOR_BLINK 1
|
|
// can set the underline color with t_8u without resetting other colors
|
|
#define TPR_UNDERLINE_RGB 2
|
|
// mouse support - TPR_MOUSE_XTERM, TPR_MOUSE_XTERM2 or TPR_MOUSE_SGR
|
|
#define TPR_MOUSE 3
|
|
// term response indicates kitty
|
|
#define TPR_KITTY 4
|
|
// table size
|
|
#define TPR_COUNT 5
|
|
|
|
static termprop_T term_props[TPR_COUNT];
|
|
|
|
/*
|
|
* Initialize the term_props table.
|
|
* When "all" is FALSE only set those that are detected from the version
|
|
* response.
|
|
*/
|
|
void
|
|
init_term_props(int all)
|
|
{
|
|
int i;
|
|
|
|
term_props[TPR_CURSOR_STYLE].tpr_name = "cursor_style";
|
|
term_props[TPR_CURSOR_STYLE].tpr_set_by_termresponse = FALSE;
|
|
term_props[TPR_CURSOR_BLINK].tpr_name = "cursor_blink_mode";
|
|
term_props[TPR_CURSOR_BLINK].tpr_set_by_termresponse = FALSE;
|
|
term_props[TPR_UNDERLINE_RGB].tpr_name = "underline_rgb";
|
|
term_props[TPR_UNDERLINE_RGB].tpr_set_by_termresponse = TRUE;
|
|
term_props[TPR_MOUSE].tpr_name = "mouse";
|
|
term_props[TPR_MOUSE].tpr_set_by_termresponse = TRUE;
|
|
term_props[TPR_KITTY].tpr_name = "kitty";
|
|
term_props[TPR_KITTY].tpr_set_by_termresponse = FALSE;
|
|
|
|
for (i = 0; i < TPR_COUNT; ++i)
|
|
if (all || term_props[i].tpr_set_by_termresponse)
|
|
term_props[i].tpr_status = TPR_UNKNOWN;
|
|
}
|
|
|
|
#if defined(FEAT_EVAL) || defined(PROTO)
|
|
void
|
|
f_terminalprops(typval_T *argvars UNUSED, typval_T *rettv)
|
|
{
|
|
# ifdef FEAT_TERMRESPONSE
|
|
int i;
|
|
# endif
|
|
|
|
if (rettv_dict_alloc(rettv) == FAIL)
|
|
return;
|
|
# ifdef FEAT_TERMRESPONSE
|
|
for (i = 0; i < TPR_COUNT; ++i)
|
|
{
|
|
char_u value[2];
|
|
|
|
value[0] = term_props[i].tpr_status;
|
|
value[1] = NUL;
|
|
dict_add_string(rettv->vval.v_dict, term_props[i].tpr_name, value);
|
|
}
|
|
# endif
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Find the builtin termcap entries for "term".
|
|
* This also recognizes similar names. E.g. "xterm-256color" finds the "xterm"
|
|
* entry.
|
|
* Returns NULL when "term" is not found.
|
|
*/
|
|
static tcap_entry_T *
|
|
find_builtin_term(char_u *term)
|
|
{
|
|
for (int i = 0; ; ++i)
|
|
{
|
|
char_u *name = (char_u *)builtin_terminals[i].bitc_name;
|
|
if (name == NULL) // end marker
|
|
break;
|
|
#ifdef UNIX
|
|
if (STRCMP(name, "iris-ansi") == 0 && vim_is_iris(term))
|
|
return builtin_terminals[i].bitc_table;
|
|
if (STRCMP(name, "xterm") == 0 && vim_is_xterm(term))
|
|
return builtin_terminals[i].bitc_table;
|
|
#endif
|
|
#ifdef VMS
|
|
if (STRCMP(name, "vt320") == 0 && vim_is_vt300(term))
|
|
return builtin_terminals[i].bitc_table;
|
|
#endif
|
|
if (STRCMP(term, name) == 0)
|
|
return builtin_terminals[i].bitc_table;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Apply entries from a builtin termcap.
|
|
*/
|
|
static void
|
|
apply_builtin_tcap(char_u *term, tcap_entry_T *entries, int overwrite)
|
|
{
|
|
int term_8bit = term_is_8bit(term);
|
|
|
|
for (tcap_entry_T *p = entries;
|
|
p->bt_entry != (int)KS_NAME && p->bt_entry != BT_EXTRA_KEYS; ++p)
|
|
{
|
|
if ((int)p->bt_entry >= 0) // KS_xx entry
|
|
{
|
|
// Only set the value if it wasn't set yet or "overwrite" is TRUE.
|
|
if (term_strings[p->bt_entry] == NULL
|
|
|| term_strings[p->bt_entry] == empty_option
|
|
|| overwrite)
|
|
{
|
|
#ifdef FEAT_EVAL
|
|
int opt_idx = -1;
|
|
#endif
|
|
// 8bit terminal: use CSI instead of <Esc>[
|
|
if (term_8bit && term_7to8bit((char_u *)p->bt_string) != 0)
|
|
{
|
|
char_u *s, *t;
|
|
|
|
s = vim_strsave((char_u *)p->bt_string);
|
|
if (s != NULL)
|
|
{
|
|
for (t = s; *t; ++t)
|
|
if (term_7to8bit(t))
|
|
{
|
|
*t = term_7to8bit(t);
|
|
STRMOVE(t + 1, t + 2);
|
|
}
|
|
term_strings[p->bt_entry] = s;
|
|
#ifdef FEAT_EVAL
|
|
opt_idx =
|
|
#endif
|
|
set_term_option_alloced(
|
|
&term_strings[p->bt_entry]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
term_strings[p->bt_entry] = (char_u *)p->bt_string;
|
|
#ifdef FEAT_EVAL
|
|
opt_idx = get_term_opt_idx(&term_strings[p->bt_entry]);
|
|
#endif
|
|
}
|
|
#ifdef FEAT_EVAL
|
|
set_term_option_sctx_idx(NULL, opt_idx);
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
char_u name[2];
|
|
name[0] = KEY2TERMCAP0((int)p->bt_entry);
|
|
name[1] = KEY2TERMCAP1((int)p->bt_entry);
|
|
if (find_termcode(name) == NULL || overwrite)
|
|
add_termcode(name, (char_u *)p->bt_string, term_8bit);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Apply builtin termcap entries for a given keyprotocol.
|
|
*/
|
|
void
|
|
apply_keyprotocol(char_u *term, keyprot_T prot)
|
|
{
|
|
if (prot == KEYPROTOCOL_KITTY)
|
|
apply_builtin_tcap(term, builtin_kitty, TRUE);
|
|
if (prot == KEYPROTOCOL_MOK2)
|
|
apply_builtin_tcap(term, builtin_mok2, TRUE);
|
|
|
|
if (prot != KEYPROTOCOL_NONE)
|
|
// Some function keys may accept modifiers even though the
|
|
// terminfo/termcap entry does not indicate this.
|
|
accept_modifiers_for_function_keys();
|
|
}
|
|
|
|
/*
|
|
* Parsing of the builtin termcap entries.
|
|
* Caller should check if "term" is a valid builtin terminal name.
|
|
* The terminal's name is not set, as this is already done in termcapinit().
|
|
*/
|
|
static void
|
|
parse_builtin_tcap(char_u *term)
|
|
{
|
|
tcap_entry_T *entries = find_builtin_term(term);
|
|
if (entries != NULL)
|
|
apply_builtin_tcap(term, entries, FALSE);
|
|
}
|
|
|
|
/*
|
|
* Set number of colors.
|
|
* Store it as a number in t_colors.
|
|
* Store it as a string in T_CCO (using nr_colors[]).
|
|
*/
|
|
void
|
|
set_color_count(int nr)
|
|
{
|
|
char_u nr_colors[20]; // string for number of colors
|
|
|
|
t_colors = nr;
|
|
if (t_colors > 1)
|
|
sprintf((char *)nr_colors, "%d", t_colors);
|
|
else
|
|
*nr_colors = NUL;
|
|
#ifdef FEAT_TERMGUICOLORS
|
|
// xterm-direct, enable termguicolors, when it wasn't set yet
|
|
if (t_colors == 0x1000000 && !p_tgc_set)
|
|
set_option_value((char_u *)"termguicolors", 1L, NULL, 0);
|
|
#endif
|
|
set_string_option_direct((char_u *)"t_Co", -1, nr_colors, OPT_FREE, 0);
|
|
}
|
|
|
|
/*
|
|
* Set the color count to "val" and redraw if it changed.
|
|
*/
|
|
static void
|
|
may_adjust_color_count(int val)
|
|
{
|
|
if (val == t_colors)
|
|
return;
|
|
|
|
// Nr of colors changed, initialize highlighting and redraw everything.
|
|
// This causes a redraw, which usually clears the message. Try keeping
|
|
// the message if it might work.
|
|
set_keep_msg_from_hist();
|
|
set_color_count(val);
|
|
init_highlight(TRUE, FALSE);
|
|
#ifdef DEBUG_TERMRESPONSE
|
|
{
|
|
int r = redraw_asap(UPD_CLEAR);
|
|
|
|
log_tr("Received t_Co, redraw_asap(): %d", r);
|
|
}
|
|
#else
|
|
redraw_asap(UPD_CLEAR);
|
|
#endif
|
|
}
|
|
|
|
#ifdef HAVE_TGETENT
|
|
static char *(key_names[]) =
|
|
{
|
|
# ifdef FEAT_TERMRESPONSE
|
|
// Do those ones first, both may cause a screen redraw.
|
|
"Co",
|
|
"RGB",
|
|
# endif
|
|
"ku", "kd", "kr", "kl",
|
|
"#2", "#4", "%i", "*7",
|
|
"k1", "k2", "k3", "k4", "k5", "k6",
|
|
"k7", "k8", "k9", "k;", "F1", "F2",
|
|
"%1", "&8", "kb", "kI", "kD", "kh",
|
|
"@7", "kP", "kN", "K1", "K3", "K4", "K5", "kB",
|
|
"PS", "PE",
|
|
NULL
|
|
};
|
|
#endif
|
|
|
|
#if defined(HAVE_TGETENT) || defined(FEAT_TERMGUICOLORS)
|
|
/*
|
|
* Return TRUE if "term_strings[idx]" was not set.
|
|
*/
|
|
static int
|
|
term_strings_not_set(enum SpecialKey idx)
|
|
{
|
|
return TERM_STR(idx) == NULL || TERM_STR(idx) == empty_option;
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_TGETENT
|
|
/*
|
|
* Get the termcap entries we need with tgetstr(), tgetflag() and tgetnum().
|
|
* "invoke_tgetent()" must have been called before.
|
|
* If "*height" or "*width" are not zero then use the "li" and "col" entries to
|
|
* get their value.
|
|
*/
|
|
static void
|
|
get_term_entries(int *height, int *width)
|
|
{
|
|
static struct {
|
|
enum SpecialKey dest; // index in term_strings[]
|
|
char *name; // termcap name for string
|
|
} string_names[] =
|
|
{ {KS_CE, "ce"}, {KS_AL, "al"}, {KS_CAL,"AL"},
|
|
{KS_DL, "dl"}, {KS_CDL,"DL"}, {KS_CS, "cs"},
|
|
{KS_CL, "cl"}, {KS_CD, "cd"},
|
|
{KS_VI, "vi"}, {KS_VE, "ve"}, {KS_MB, "mb"},
|
|
{KS_ME, "me"}, {KS_MR, "mr"},
|
|
{KS_MD, "md"}, {KS_SE, "se"}, {KS_SO, "so"},
|
|
{KS_CZH,"ZH"}, {KS_CZR,"ZR"}, {KS_UE, "ue"},
|
|
{KS_US, "us"}, {KS_UCE, "Ce"}, {KS_UCS, "Cs"},
|
|
{KS_USS, "Us"}, {KS_DS, "ds"}, {KS_CDS, "Ds"},
|
|
{KS_STE,"Te"}, {KS_STS,"Ts"},
|
|
{KS_CM, "cm"}, {KS_SR, "sr"},
|
|
{KS_CRI,"RI"}, {KS_VB, "vb"}, {KS_KS, "ks"},
|
|
{KS_KE, "ke"}, {KS_TI, "ti"}, {KS_TE, "te"},
|
|
{KS_CTI, "TI"}, {KS_CRK, "RK"}, {KS_CTE, "TE"},
|
|
{KS_BC, "bc"}, {KS_CSB,"Sb"}, {KS_CSF,"Sf"},
|
|
{KS_CAB,"AB"}, {KS_CAF,"AF"}, {KS_CAU,"AU"},
|
|
{KS_LE, "le"},
|
|
{KS_ND, "nd"}, {KS_OP, "op"},
|
|
{KS_CRV, "RV"}, {KS_CXM, "XM"},
|
|
{KS_VS, "vs"}, {KS_CVS, "VS"},
|
|
{KS_CIS, "IS"}, {KS_CIE, "IE"},
|
|
{KS_CSC, "SC"}, {KS_CEC, "EC"},
|
|
{KS_TS, "ts"}, {KS_FS, "fs"},
|
|
{KS_CWP, "WP"}, {KS_CWS, "WS"},
|
|
{KS_CSI, "SI"}, {KS_CEI, "EI"},
|
|
{KS_U7, "u7"}, {KS_RFG, "RF"}, {KS_RBG, "RB"},
|
|
{KS_8F, "8f"}, {KS_8B, "8b"}, {KS_8U, "8u"},
|
|
{KS_CBE, "BE"}, {KS_CBD, "BD"},
|
|
{KS_CST, "ST"}, {KS_CRT, "RT"},
|
|
{KS_SSI, "Si"}, {KS_SRI, "Ri"},
|
|
{KS_CF, "CF"},
|
|
{(enum SpecialKey)0, NULL}
|
|
};
|
|
int i;
|
|
static char_u tstrbuf[TBUFSZ];
|
|
char_u *tp = tstrbuf;
|
|
|
|
/*
|
|
* get output strings
|
|
*/
|
|
for (i = 0; string_names[i].name != NULL; ++i)
|
|
{
|
|
if (term_strings_not_set(string_names[i].dest))
|
|
{
|
|
TERM_STR(string_names[i].dest) = TGETSTR(string_names[i].name, &tp);
|
|
#ifdef FEAT_EVAL
|
|
set_term_option_sctx_idx(string_names[i].name, -1);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// tgetflag() returns 1 if the flag is present, 0 if not and
|
|
// possibly -1 if the flag doesn't exist.
|
|
if ((T_MS == NULL || T_MS == empty_option) && tgetflag("ms") > 0)
|
|
T_MS = (char_u *)"y";
|
|
if ((T_XS == NULL || T_XS == empty_option) && tgetflag("xs") > 0)
|
|
T_XS = (char_u *)"y";
|
|
if ((T_XN == NULL || T_XN == empty_option) && tgetflag("xn") > 0)
|
|
T_XN = (char_u *)"y";
|
|
if ((T_DB == NULL || T_DB == empty_option) && tgetflag("db") > 0)
|
|
T_DB = (char_u *)"y";
|
|
if ((T_DA == NULL || T_DA == empty_option) && tgetflag("da") > 0)
|
|
T_DA = (char_u *)"y";
|
|
if ((T_UT == NULL || T_UT == empty_option) && tgetflag("ut") > 0)
|
|
T_UT = (char_u *)"y";
|
|
if ((T_XON == NULL || T_XON == empty_option) && tgetflag("xo") > 0)
|
|
T_XON = (char_u *)"y";
|
|
|
|
/*
|
|
* get key codes
|
|
*/
|
|
for (i = 0; key_names[i] != NULL; ++i)
|
|
if (find_termcode((char_u *)key_names[i]) == NULL)
|
|
{
|
|
char_u *p = TGETSTR(key_names[i], &tp);
|
|
|
|
// if cursor-left == backspace, ignore it (televideo 925)
|
|
if (p != NULL
|
|
&& (*p != Ctrl_H
|
|
|| key_names[i][0] != 'k'
|
|
|| key_names[i][1] != 'l'))
|
|
add_termcode((char_u *)key_names[i], p, FALSE);
|
|
}
|
|
|
|
if (*height == 0)
|
|
*height = tgetnum("li");
|
|
if (*width == 0)
|
|
*width = tgetnum("co");
|
|
|
|
/*
|
|
* Get number of colors (if not done already).
|
|
*/
|
|
if (term_strings_not_set(KS_CCO))
|
|
{
|
|
set_color_count(tgetnum("Co"));
|
|
#ifdef FEAT_EVAL
|
|
set_term_option_sctx_idx("Co", -1);
|
|
#endif
|
|
}
|
|
|
|
# ifndef hpux
|
|
BC = (char *)TGETSTR("bc", &tp);
|
|
UP = (char *)TGETSTR("up", &tp);
|
|
char_u *p = TGETSTR("pc", &tp);
|
|
if (p != NULL)
|
|
PC = *p;
|
|
# endif
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Report "term" is not found and list the ones we do know about.
|
|
*/
|
|
static void
|
|
report_term_error(char *error_msg, char_u *term)
|
|
{
|
|
mch_errmsg("\r\n");
|
|
if (error_msg != NULL)
|
|
{
|
|
mch_errmsg(error_msg);
|
|
mch_errmsg("\r\n");
|
|
}
|
|
mch_errmsg("'");
|
|
mch_errmsg((char *)term);
|
|
mch_errmsg(_("' not known. Available builtin terminals are:"));
|
|
mch_errmsg("\r\n");
|
|
|
|
for (int i = 0; ; ++i)
|
|
{
|
|
char *name = builtin_terminals[i].bitc_name;
|
|
if (name == NULL) // end marker
|
|
break;
|
|
// Do not mention the "gui" entry, the user won't need to type it.
|
|
if (STRCMP(name, "gui") != 0)
|
|
{
|
|
#ifdef HAVE_TGETENT
|
|
mch_errmsg(" builtin_");
|
|
#else
|
|
mch_errmsg(" ");
|
|
#endif
|
|
mch_errmsg(name);
|
|
mch_errmsg("\r\n");
|
|
}
|
|
}
|
|
// Output extra 'cmdheight' line breaks to avoid that the following error
|
|
// message overwrites the last terminal name.
|
|
for (int i = 1; i < p_ch; ++i)
|
|
mch_errmsg("\r\n");
|
|
}
|
|
|
|
static void
|
|
report_default_term(char_u *term)
|
|
{
|
|
mch_errmsg(_("defaulting to '"));
|
|
mch_errmsg((char *)term);
|
|
mch_errmsg("'\r\n");
|
|
if (emsg_silent == 0 && !in_assert_fails)
|
|
{
|
|
screen_start(); // don't know where cursor is now
|
|
out_flush();
|
|
if (!is_not_a_term())
|
|
ui_delay(2007L, TRUE);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Parse the 'keyprotocol' option, match against "term" and return the protocol
|
|
* for the first matching entry.
|
|
* When "term" is NULL then compile all patterns to check for any errors.
|
|
* Returns KEYPROTOCOL_FAIL if a pattern cannot be compiled.
|
|
* Returns KEYPROTOCOL_NONE if there is no match.
|
|
*/
|
|
keyprot_T
|
|
match_keyprotocol(char_u *term)
|
|
{
|
|
int len = (int)STRLEN(p_kpc) + 1;
|
|
char_u *buf = alloc(len);
|
|
if (buf == NULL)
|
|
return KEYPROTOCOL_FAIL;
|
|
|
|
keyprot_T ret = KEYPROTOCOL_FAIL;
|
|
char_u *p = p_kpc;
|
|
while (*p != NUL)
|
|
{
|
|
// Isolate one comma separated item.
|
|
(void)copy_option_part(&p, buf, len, ",");
|
|
char_u *colon = vim_strchr(buf, ':');
|
|
if (colon == NULL || colon == buf || colon[1] == NUL)
|
|
goto theend;
|
|
*colon = NUL;
|
|
|
|
keyprot_T prot;
|
|
// Note: Keep this in sync with p_kpc_protocol_values.
|
|
if (STRCMP(colon + 1, "none") == 0)
|
|
prot = KEYPROTOCOL_NONE;
|
|
else if (STRCMP(colon + 1, "mok2") == 0)
|
|
prot = KEYPROTOCOL_MOK2;
|
|
else if (STRCMP(colon + 1, "kitty") == 0)
|
|
prot = KEYPROTOCOL_KITTY;
|
|
else
|
|
goto theend;
|
|
|
|
regmatch_T regmatch;
|
|
CLEAR_FIELD(regmatch);
|
|
regmatch.rm_ic = TRUE;
|
|
regmatch.regprog = vim_regcomp(buf, RE_MAGIC);
|
|
if (regmatch.regprog == NULL)
|
|
goto theend;
|
|
|
|
int match = term != NULL && vim_regexec(®match, term, (colnr_T)0);
|
|
vim_regfree(regmatch.regprog);
|
|
if (match)
|
|
{
|
|
ret = prot;
|
|
goto theend;
|
|
}
|
|
|
|
}
|
|
|
|
// No match found, use "none".
|
|
ret = KEYPROTOCOL_NONE;
|
|
|
|
theend:
|
|
vim_free(buf);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Set terminal options for terminal "term".
|
|
* Return OK if terminal 'term' was found in a termcap, FAIL otherwise.
|
|
*
|
|
* While doing this, until ttest(), some options may be NULL, be careful.
|
|
*/
|
|
int
|
|
set_termname(char_u *term)
|
|
{
|
|
#ifdef HAVE_TGETENT
|
|
int builtin_first = p_tbi;
|
|
int try;
|
|
int termcap_cleared = FALSE;
|
|
#endif
|
|
int width = 0, height = 0;
|
|
char *error_msg = NULL;
|
|
char_u *bs_p, *del_p;
|
|
|
|
// In silect mode (ex -s) we don't use the 'term' option.
|
|
if (silent_mode)
|
|
return OK;
|
|
|
|
detected_8bit = FALSE; // reset 8-bit detection
|
|
|
|
if (term_is_builtin(term))
|
|
{
|
|
term += 8;
|
|
#ifdef HAVE_TGETENT
|
|
builtin_first = 1;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* If HAVE_TGETENT is not defined, only the builtin termcap is used, otherwise:
|
|
* If builtin_first is TRUE:
|
|
* 0. try builtin termcap
|
|
* 1. try external termcap
|
|
* 2. if both fail default to a builtin terminal
|
|
* If builtin_first is FALSE:
|
|
* 1. try external termcap
|
|
* 2. try builtin termcap, if both fail default to a builtin terminal
|
|
*/
|
|
#ifdef HAVE_TGETENT
|
|
for (try = builtin_first ? 0 : 1; try < 3; ++try)
|
|
{
|
|
/*
|
|
* Use external termcap
|
|
*/
|
|
if (try == 1)
|
|
{
|
|
char_u tbuf[TBUFSZ];
|
|
|
|
/*
|
|
* If the external termcap does not have a matching entry, try the
|
|
* builtin ones.
|
|
*/
|
|
if ((error_msg = invoke_tgetent(tbuf, term)) == NULL)
|
|
{
|
|
if (!termcap_cleared)
|
|
{
|
|
clear_termoptions(); // clear old options
|
|
termcap_cleared = TRUE;
|
|
}
|
|
|
|
get_term_entries(&height, &width);
|
|
}
|
|
}
|
|
else // try == 0 || try == 2
|
|
#endif // HAVE_TGETENT
|
|
/*
|
|
* Use builtin termcap
|
|
*/
|
|
{
|
|
#ifdef HAVE_TGETENT
|
|
/*
|
|
* If builtin termcap was already used, there is no need to search
|
|
* for the builtin termcap again, quit now.
|
|
*/
|
|
if (try == 2 && builtin_first && termcap_cleared)
|
|
break;
|
|
#endif
|
|
/*
|
|
* Search for 'term' in builtin_terminals[].
|
|
*/
|
|
tcap_entry_T *termp = find_builtin_term(term);
|
|
if (termp == NULL) // did not find it
|
|
{
|
|
#ifdef HAVE_TGETENT
|
|
/*
|
|
* If try == 0, first try the external termcap. If that is not
|
|
* found we'll get back here with try == 2.
|
|
* If termcap_cleared is set we used the external termcap,
|
|
* don't complain about not finding the term in the builtin
|
|
* termcap.
|
|
*/
|
|
if (try == 0) // try external one
|
|
continue;
|
|
if (termcap_cleared) // found in external termcap
|
|
break;
|
|
#endif
|
|
report_term_error(error_msg, term);
|
|
|
|
// when user typed :set term=xxx, quit here
|
|
if (starting != NO_SCREEN)
|
|
{
|
|
screen_start(); // don't know where cursor is now
|
|
wait_return(TRUE);
|
|
return FAIL;
|
|
}
|
|
term = DEFAULT_TERM;
|
|
report_default_term(term);
|
|
set_string_option_direct((char_u *)"term", -1, term,
|
|
OPT_FREE, 0);
|
|
display_errors();
|
|
}
|
|
out_flush();
|
|
#ifdef HAVE_TGETENT
|
|
if (!termcap_cleared)
|
|
{
|
|
#endif
|
|
clear_termoptions(); // clear old options
|
|
#ifdef HAVE_TGETENT
|
|
termcap_cleared = TRUE;
|
|
}
|
|
#endif
|
|
parse_builtin_tcap(term);
|
|
|
|
#ifdef FEAT_GUI
|
|
if (term_is_gui(term))
|
|
{
|
|
out_flush();
|
|
gui_init();
|
|
// If starting the GUI failed, don't do any of the other
|
|
// things for this terminal
|
|
if (!gui.in_use)
|
|
return FAIL;
|
|
# ifdef HAVE_TGETENT
|
|
break; // don't try using external termcap
|
|
# endif
|
|
}
|
|
#endif // FEAT_GUI
|
|
}
|
|
#ifdef HAVE_TGETENT
|
|
}
|
|
#endif
|
|
|
|
#ifdef FEAT_GUI
|
|
if (!gui.in_use)
|
|
#endif
|
|
{
|
|
// Use the 'keyprotocol' option to adjust the t_TE and t_TI
|
|
// termcap entries if there is an entry matching "term".
|
|
keyprot_T kpc = match_keyprotocol(term);
|
|
apply_keyprotocol(term, kpc);
|
|
|
|
#ifdef FEAT_TERMGUICOLORS
|
|
// There is no good way to detect that the terminal supports RGB
|
|
// colors. Since these termcap entries are non-standard anyway and
|
|
// only used when the user sets 'termguicolors' we might as well add
|
|
// them. But not when one of them was already set.
|
|
if (term_strings_not_set(KS_8F)
|
|
&& term_strings_not_set(KS_8B)
|
|
&& term_strings_not_set(KS_8U))
|
|
apply_builtin_tcap(term, builtin_rgb, TRUE);
|
|
#endif
|
|
#ifdef HAVE_TGETENT
|
|
if (term_strings_not_set(KS_CF))
|
|
apply_builtin_tcap(term, special_term, TRUE);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* special: There is no info in the termcap about whether the cursor
|
|
* positioning is relative to the start of the screen or to the start of the
|
|
* scrolling region. We just guess here. Only msdos pcterm is known to do it
|
|
* relative.
|
|
*/
|
|
if (STRCMP(term, "pcterm") == 0)
|
|
T_CCS = (char_u *)"yes";
|
|
else
|
|
T_CCS = empty_option;
|
|
|
|
// Special case: "kitty" may not have a "RV" entry in terminfo, but we need
|
|
// to request the version for several other things to work.
|
|
if (strstr((char *)term, "kitty") != NULL
|
|
&& (T_CRV == NULL || *T_CRV == NUL))
|
|
T_CRV = (char_u *)"\033[>c";
|
|
|
|
#ifdef UNIX
|
|
/*
|
|
* Any "stty" settings override the default for t_kb from the termcap.
|
|
* This is in os_unix.c, because it depends a lot on the version of unix that
|
|
* is being used.
|
|
* Don't do this when the GUI is active, it uses "t_kb" and "t_kD" directly.
|
|
*/
|
|
# ifdef FEAT_GUI
|
|
if (!gui.in_use)
|
|
# endif
|
|
get_stty();
|
|
#endif
|
|
|
|
/*
|
|
* If the termcap has no entry for 'bs' and/or 'del' and the ioctl() also
|
|
* didn't work, use the default CTRL-H
|
|
* The default for t_kD is DEL, unless t_kb is DEL.
|
|
* The vim_strsave'd strings are probably lost forever, well it's only two
|
|
* bytes. Don't do this when the GUI is active, it uses "t_kb" and "t_kD"
|
|
* directly.
|
|
*/
|
|
#ifdef FEAT_GUI
|
|
if (!gui.in_use)
|
|
#endif
|
|
{
|
|
bs_p = find_termcode((char_u *)"kb");
|
|
del_p = find_termcode((char_u *)"kD");
|
|
if (bs_p == NULL || *bs_p == NUL)
|
|
add_termcode((char_u *)"kb", (bs_p = (char_u *)CTRL_H_STR), FALSE);
|
|
if ((del_p == NULL || *del_p == NUL) &&
|
|
(bs_p == NULL || *bs_p != DEL))
|
|
add_termcode((char_u *)"kD", (char_u *)DEL_STR, FALSE);
|
|
}
|
|
|
|
#if defined(UNIX) || defined(VMS)
|
|
term_is_xterm = vim_is_xterm(term);
|
|
#endif
|
|
#ifdef FEAT_TERMRESPONSE
|
|
// Reset terminal properties that are set based on the termresponse, which
|
|
// will be sent out soon.
|
|
init_term_props(FALSE);
|
|
#endif
|
|
|
|
#if defined(UNIX) || defined(VMS)
|
|
// If the first number in t_XM is 1006 then the terminal will support SGR
|
|
// mouse reporting.
|
|
int did_set_ttym = FALSE;
|
|
if (T_CXM != NULL && *T_CXM != NUL && !option_was_set((char_u *)"ttym"))
|
|
{
|
|
char_u *p = T_CXM;
|
|
|
|
while (*p != NUL && !VIM_ISDIGIT(*p))
|
|
++p;
|
|
if (getdigits(&p) == 1006)
|
|
{
|
|
did_set_ttym = TRUE;
|
|
set_option_value_give_err((char_u *)"ttym", 0L, (char_u *)"sgr", 0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* For Unix, set the 'ttymouse' option to the type of mouse to be used.
|
|
* The termcode for the mouse is added as a side effect in option.c.
|
|
*/
|
|
{
|
|
char_u *p = (char_u *)"";
|
|
|
|
# ifdef FEAT_MOUSE_XTERM
|
|
if (use_xterm_like_mouse(term))
|
|
{
|
|
if (use_xterm_mouse())
|
|
p = NULL; // keep existing value, might be "xterm2"
|
|
else
|
|
p = (char_u *)"xterm";
|
|
}
|
|
# endif
|
|
if (p != NULL && !did_set_ttym)
|
|
{
|
|
set_option_value_give_err((char_u *)"ttym", 0L, p, 0);
|
|
// Reset the WAS_SET flag, 'ttymouse' can be set to "sgr" or
|
|
// "xterm2" in check_termcode().
|
|
reset_option_was_set((char_u *)"ttym");
|
|
}
|
|
if (p == NULL
|
|
# ifdef FEAT_GUI
|
|
|| gui.in_use
|
|
# endif
|
|
)
|
|
check_mouse_termcode(); // set mouse termcode anyway
|
|
}
|
|
#else
|
|
set_mouse_termcode(KS_MOUSE, (char_u *)"\233M");
|
|
#endif
|
|
|
|
#ifdef FEAT_MOUSE_XTERM
|
|
// Focus reporting is supported by xterm compatible terminals and tmux.
|
|
// We hard-code the received escape sequences here. There are the terminfo
|
|
// entries kxIN and kxOUT, but they are rarely used and do hot have a
|
|
// two-letter termcap name.
|
|
// This used to be done only for xterm-like terminals, but some others also
|
|
// may produce these codes. Always recognize them, as the chance of them
|
|
// being used for something else is very small.
|
|
{
|
|
char_u name[3];
|
|
|
|
// handle focus in event
|
|
name[0] = KS_EXTRA;
|
|
name[1] = KE_FOCUSGAINED;
|
|
name[2] = NUL;
|
|
add_termcode(name, (char_u *)"\033[I", FALSE);
|
|
|
|
// handle focus out event
|
|
name[1] = KE_FOCUSLOST;
|
|
add_termcode(name, (char_u *)"\033[O", FALSE);
|
|
|
|
need_gather = TRUE;
|
|
}
|
|
#endif
|
|
#if (defined(UNIX) || defined(VMS))
|
|
// First time after setting 'term' a focus event is always reported.
|
|
focus_state = MAYBE;
|
|
#endif
|
|
|
|
#ifdef USE_TERM_CONSOLE
|
|
// DEFAULT_TERM indicates that it is the machine console.
|
|
if (STRCMP(term, DEFAULT_TERM) != 0)
|
|
term_console = FALSE;
|
|
else
|
|
{
|
|
term_console = TRUE;
|
|
# ifdef AMIGA
|
|
win_resize_on(); // enable window resizing reports
|
|
# endif
|
|
}
|
|
#endif
|
|
|
|
ttest(TRUE); // make sure we have a valid set of terminal codes
|
|
|
|
full_screen = TRUE; // we can use termcap codes from now on
|
|
set_term_defaults(); // use current values as defaults
|
|
#ifdef FEAT_TERMRESPONSE
|
|
LOG_TR(("setting crv_status to STATUS_GET"));
|
|
crv_status.tr_progress = STATUS_GET; // Get terminal version later
|
|
write_t_8u_state = FALSE;
|
|
#endif
|
|
|
|
/*
|
|
* Initialize the terminal with the appropriate termcap codes.
|
|
* Set the mouse and window title if possible.
|
|
* Don't do this when starting, need to parse the .vimrc first, because it
|
|
* may redefine t_TI etc.
|
|
*/
|
|
if (starting != NO_SCREEN)
|
|
{
|
|
starttermcap(); // may change terminal mode
|
|
setmouse(); // may start using the mouse
|
|
maketitle(); // may display window title
|
|
}
|
|
|
|
// display initial screen after ttest() checking. jw.
|
|
if (width <= 0 || height <= 0)
|
|
{
|
|
// termcap failed to report size
|
|
// set defaults, in case ui_get_shellsize() also fails
|
|
width = 80;
|
|
#if defined(MSWIN)
|
|
height = 25; // console is often 25 lines
|
|
#else
|
|
height = 24; // most terminals are 24 lines
|
|
#endif
|
|
}
|
|
set_shellsize(width, height, FALSE); // may change Rows
|
|
if (starting != NO_SCREEN)
|
|
{
|
|
if (scroll_region)
|
|
scroll_region_reset(); // In case Rows changed
|
|
check_map_keycodes(); // check mappings for terminal codes used
|
|
|
|
{
|
|
buf_T *buf;
|
|
aco_save_T aco;
|
|
|
|
/*
|
|
* Execute the TermChanged autocommands for each buffer that is
|
|
* loaded.
|
|
*/
|
|
FOR_ALL_BUFFERS(buf)
|
|
{
|
|
if (curbuf->b_ml.ml_mfp != NULL)
|
|
{
|
|
aucmd_prepbuf(&aco, buf);
|
|
if (curbuf == buf)
|
|
{
|
|
apply_autocmds(EVENT_TERMCHANGED, NULL, NULL, FALSE,
|
|
curbuf);
|
|
// restore curwin/curbuf and a few other things
|
|
aucmd_restbuf(&aco);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef FEAT_TERMRESPONSE
|
|
may_req_termresponse();
|
|
#endif
|
|
|
|
return OK;
|
|
}
|
|
|
|
#if defined(EXITFREE) || defined(PROTO)
|
|
|
|
# ifdef HAVE_DEL_CURTERM
|
|
# include <term.h> // declares cur_term
|
|
# endif
|
|
|
|
/*
|
|
* If supported, delete "cur_term", which caches terminal related entries.
|
|
* Avoids that valgrind reports possibly lost memory.
|
|
*/
|
|
void
|
|
free_cur_term(void)
|
|
{
|
|
# ifdef HAVE_DEL_CURTERM
|
|
if (cur_term)
|
|
del_curterm(cur_term);
|
|
# endif
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_TGETENT
|
|
/*
|
|
* Call tgetent()
|
|
* Return error message if it fails, NULL if it's OK.
|
|
*/
|
|
static char *
|
|
invoke_tgetent(char_u *tbuf, char_u *term)
|
|
{
|
|
int i;
|
|
|
|
// Note: Valgrind may report a leak here, because the library keeps one
|
|
// buffer around that we can't ever free.
|
|
i = TGETENT(tbuf, term);
|
|
if (i < 0 // -1 is always an error
|
|
# ifdef TGETENT_ZERO_ERR
|
|
|| i == 0 // sometimes zero is also an error
|
|
# endif
|
|
)
|
|
{
|
|
// On FreeBSD tputs() gets a SEGV after a tgetent() which fails. Call
|
|
// tgetent() with the always existing "dumb" entry to avoid a crash or
|
|
// hang.
|
|
(void)TGETENT(tbuf, "dumb");
|
|
|
|
if (i < 0)
|
|
# ifdef TGETENT_ZERO_ERR
|
|
return _(e_cannot_open_termcap_file);
|
|
if (i == 0)
|
|
# endif
|
|
#ifdef TERMINFO
|
|
return _(e_terminal_entry_not_found_in_terminfo);
|
|
#else
|
|
return _(e_terminal_entry_not_found_in_termcap);
|
|
#endif
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Some versions of tgetstr() have been reported to return -1 instead of NULL.
|
|
* Fix that here.
|
|
*/
|
|
static char_u *
|
|
vim_tgetstr(char *s, char_u **pp)
|
|
{
|
|
char *p;
|
|
|
|
p = tgetstr(s, (char **)pp);
|
|
if (p == (char *)-1)
|
|
p = NULL;
|
|
return (char_u *)p;
|
|
}
|
|
#endif // HAVE_TGETENT
|
|
|
|
#if defined(HAVE_TGETENT) && (defined(UNIX) || defined(VMS) || defined(MACOS_X))
|
|
/*
|
|
* Get Columns and Rows from the termcap. Used after a window signal if the
|
|
* ioctl() fails. It doesn't make sense to call tgetent each time if the "co"
|
|
* and "li" entries never change. But on some systems this works.
|
|
* Errors while getting the entries are ignored.
|
|
*/
|
|
void
|
|
getlinecol(
|
|
long *cp, // pointer to columns
|
|
long *rp) // pointer to rows
|
|
{
|
|
char_u tbuf[TBUFSZ];
|
|
|
|
if (T_NAME == NULL || *T_NAME == NUL
|
|
|| invoke_tgetent(tbuf, T_NAME) != NULL)
|
|
return;
|
|
|
|
if (*cp == 0)
|
|
*cp = tgetnum("co");
|
|
if (*rp == 0)
|
|
*rp = tgetnum("li");
|
|
}
|
|
#endif // defined(HAVE_TGETENT) && defined(UNIX)
|
|
|
|
/*
|
|
* Get a string entry from the termcap and add it to the list of termcodes.
|
|
* Used for <t_xx> special keys.
|
|
* Give an error message for failure when not sourcing.
|
|
* If force given, replace an existing entry.
|
|
* Return FAIL if the entry was not found, OK if the entry was added.
|
|
*/
|
|
int
|
|
add_termcap_entry(char_u *name, int force)
|
|
{
|
|
char_u *term;
|
|
int key;
|
|
#ifdef HAVE_TGETENT
|
|
char_u *string;
|
|
int i;
|
|
int builtin_first;
|
|
char_u tbuf[TBUFSZ];
|
|
char_u tstrbuf[TBUFSZ];
|
|
char_u *tp = tstrbuf;
|
|
char *error_msg = NULL;
|
|
#endif
|
|
|
|
/*
|
|
* If the GUI is running or will start in a moment, we only support the keys
|
|
* that the GUI can produce.
|
|
*/
|
|
#ifdef FEAT_GUI
|
|
if (gui.in_use || gui.starting)
|
|
return gui_mch_haskey(name);
|
|
#endif
|
|
|
|
if (!force && find_termcode(name) != NULL) // it's already there
|
|
return OK;
|
|
|
|
term = T_NAME;
|
|
if (term == NULL || *term == NUL) // 'term' not defined yet
|
|
return FAIL;
|
|
|
|
if (term_is_builtin(term)) // name starts with "builtin_"
|
|
{
|
|
term += 8;
|
|
#ifdef HAVE_TGETENT
|
|
builtin_first = TRUE;
|
|
#endif
|
|
}
|
|
#ifdef HAVE_TGETENT
|
|
else
|
|
builtin_first = p_tbi;
|
|
#endif
|
|
|
|
#ifdef HAVE_TGETENT
|
|
/*
|
|
* We can get the entry from the builtin termcap and from the external one.
|
|
* If 'ttybuiltin' is on or the terminal name starts with "builtin_", try
|
|
* builtin termcap first.
|
|
* If 'ttybuiltin' is off, try external termcap first.
|
|
*/
|
|
for (i = 0; i < 2; ++i)
|
|
{
|
|
if ((!builtin_first) == i)
|
|
#endif
|
|
/*
|
|
* Search in builtin termcaps
|
|
*/
|
|
{
|
|
tcap_entry_T *termp = find_builtin_term(term);
|
|
if (termp != NULL) // found it
|
|
{
|
|
key = TERMCAP2KEY(name[0], name[1]);
|
|
++termp;
|
|
while (termp->bt_entry != (int)KS_NAME)
|
|
{
|
|
if ((int)termp->bt_entry == key)
|
|
{
|
|
add_termcode(name, (char_u *)termp->bt_string,
|
|
term_is_8bit(term));
|
|
return OK;
|
|
}
|
|
++termp;
|
|
}
|
|
}
|
|
}
|
|
#ifdef HAVE_TGETENT
|
|
else
|
|
/*
|
|
* Search in external termcap
|
|
*/
|
|
{
|
|
error_msg = invoke_tgetent(tbuf, term);
|
|
if (error_msg == NULL)
|
|
{
|
|
string = TGETSTR((char *)name, &tp);
|
|
if (string != NULL && *string != NUL)
|
|
{
|
|
add_termcode(name, string, FALSE);
|
|
return OK;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (SOURCING_NAME == NULL)
|
|
{
|
|
#ifdef HAVE_TGETENT
|
|
if (error_msg != NULL)
|
|
emsg(error_msg);
|
|
else
|
|
#endif
|
|
semsg(_(e_no_str_entry_in_termcap), name);
|
|
}
|
|
return FAIL;
|
|
}
|
|
|
|
static int
|
|
term_is_builtin(char_u *name)
|
|
{
|
|
return (STRNCMP(name, "builtin_", (size_t)8) == 0);
|
|
}
|
|
|
|
/*
|
|
* Return TRUE if terminal "name" uses CSI instead of <Esc>[.
|
|
* Assume that the terminal is using 8-bit controls when the name contains
|
|
* "8bit", like in "xterm-8bit".
|
|
*/
|
|
int
|
|
term_is_8bit(char_u *name)
|
|
{
|
|
return (detected_8bit || strstr((char *)name, "8bit") != NULL);
|
|
}
|
|
|
|
/*
|
|
* Translate terminal control chars from 7-bit to 8-bit:
|
|
* <Esc>[ -> CSI <M_C_[>
|
|
* <Esc>] -> OSC <M-C-]>
|
|
* <Esc>O -> <M-C-O>
|
|
*/
|
|
static int
|
|
term_7to8bit(char_u *p)
|
|
{
|
|
if (*p != ESC)
|
|
return 0;
|
|
|
|
if (p[1] == '[')
|
|
return CSI;
|
|
else if (p[1] == ']')
|
|
return OSC;
|
|
else if (p[1] == 'O')
|
|
return 0x8f;
|
|
return 0;
|
|
}
|
|
|
|
#if defined(FEAT_GUI) || defined(PROTO)
|
|
int
|
|
term_is_gui(char_u *name)
|
|
{
|
|
return (STRCMP(name, "builtin_gui") == 0 || STRCMP(name, "gui") == 0);
|
|
}
|
|
#endif
|
|
|
|
#if !defined(HAVE_TGETENT) || defined(AMIGA) || defined(PROTO)
|
|
|
|
char_u *
|
|
tltoa(unsigned long i)
|
|
{
|
|
static char_u buf[16];
|
|
char_u *p;
|
|
|
|
p = buf + 15;
|
|
*p = '\0';
|
|
do
|
|
{
|
|
--p;
|
|
*p = (char_u) (i % 10 + '0');
|
|
i /= 10;
|
|
}
|
|
while (i > 0 && p > buf);
|
|
return p;
|
|
}
|
|
#endif
|
|
|
|
#ifndef HAVE_TGETENT
|
|
|
|
/*
|
|
* minimal tgoto() implementation.
|
|
* no padding and we only parse for %i %d and %+char
|
|
*/
|
|
static char *
|
|
tgoto(char *cm, int x, int y)
|
|
{
|
|
static char buf[30];
|
|
char *p, *s, *e;
|
|
|
|
if (!cm)
|
|
return "OOPS";
|
|
e = buf + 29;
|
|
for (s = buf; s < e && *cm; cm++)
|
|
{
|
|
if (*cm != '%')
|
|
{
|
|
*s++ = *cm;
|
|
continue;
|
|
}
|
|
switch (*++cm)
|
|
{
|
|
case 'd':
|
|
p = (char *)tltoa((unsigned long)y);
|
|
y = x;
|
|
while (*p)
|
|
*s++ = *p++;
|
|
break;
|
|
case 'i':
|
|
x++;
|
|
y++;
|
|
break;
|
|
case '+':
|
|
*s++ = (char)(*++cm + y);
|
|
y = x;
|
|
break;
|
|
case '%':
|
|
*s++ = *cm;
|
|
break;
|
|
default:
|
|
return "OOPS";
|
|
}
|
|
}
|
|
*s = '\0';
|
|
return buf;
|
|
}
|
|
|
|
#endif // HAVE_TGETENT
|
|
|
|
/*
|
|
* Set the terminal name and initialize the terminal options.
|
|
* If "name" is NULL or empty, get the terminal name from the environment.
|
|
* If that fails, use the default terminal name.
|
|
*/
|
|
void
|
|
termcapinit(char_u *name)
|
|
{
|
|
char_u *term = name;
|
|
|
|
if (term != NULL && *term == NUL)
|
|
term = NULL; // empty name is equal to no name
|
|
|
|
#ifndef MSWIN
|
|
if (term == NULL)
|
|
term = mch_getenv((char_u *)"TERM");
|
|
#endif
|
|
if (term == NULL || *term == NUL)
|
|
term = DEFAULT_TERM;
|
|
set_string_option_direct((char_u *)"term", -1, term, OPT_FREE, 0);
|
|
|
|
// Set the default terminal name.
|
|
set_string_default("term", term);
|
|
set_string_default("ttytype", term);
|
|
|
|
// Avoid using "term" here, because the next mch_getenv() may overwrite it.
|
|
set_termname(T_NAME != NULL ? T_NAME : term);
|
|
}
|
|
|
|
/*
|
|
* The number of calls to ui_write is reduced by using "out_buf".
|
|
*/
|
|
#define OUT_SIZE 2047
|
|
|
|
// add one to allow mch_write() in os_win32.c to append a NUL
|
|
static char_u out_buf[OUT_SIZE + 1];
|
|
|
|
static int out_pos = 0; // number of chars in out_buf
|
|
|
|
// Since the maximum number of SGR parameters shown as a normal value range is
|
|
// 16, the escape sequence length can be 4 * 16 + lead + tail.
|
|
#define MAX_ESC_SEQ_LEN 80
|
|
|
|
/*
|
|
* out_flush(): flush the output buffer
|
|
*/
|
|
void
|
|
out_flush(void)
|
|
{
|
|
int len;
|
|
|
|
if (out_pos == 0)
|
|
return;
|
|
|
|
// set out_pos to 0 before ui_write, to avoid recursiveness
|
|
len = out_pos;
|
|
out_pos = 0;
|
|
ui_write(out_buf, len, FALSE);
|
|
#ifdef FEAT_EVAL
|
|
if (ch_log_output != FALSE)
|
|
{
|
|
out_buf[len] = NUL;
|
|
ch_log(NULL, "raw %s output: \"%s\"",
|
|
# ifdef FEAT_GUI
|
|
(gui.in_use && !gui.dying && !gui.starting) ? "GUI" :
|
|
# endif
|
|
"terminal",
|
|
out_buf);
|
|
if (ch_log_output == TRUE)
|
|
ch_log_output = FALSE; // only log once
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* out_flush_cursor(): flush the output buffer and redraw the cursor.
|
|
* Does not flush recursively in the GUI to avoid slow drawing.
|
|
*/
|
|
void
|
|
out_flush_cursor(
|
|
int force UNUSED, // when TRUE, update cursor even when not moved
|
|
int clear_selection UNUSED) // clear selection under cursor
|
|
{
|
|
mch_disable_flush();
|
|
out_flush();
|
|
mch_enable_flush();
|
|
#ifdef FEAT_GUI
|
|
if (gui.in_use)
|
|
{
|
|
gui_update_cursor(force, clear_selection);
|
|
gui_may_flush();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
* Sometimes a byte out of a multi-byte character is written with out_char().
|
|
* To avoid flushing half of the character, call this function first.
|
|
*/
|
|
void
|
|
out_flush_check(void)
|
|
{
|
|
if (enc_dbcs != 0 && out_pos >= OUT_SIZE - MB_MAXBYTES)
|
|
out_flush();
|
|
}
|
|
|
|
#ifdef FEAT_GUI
|
|
/*
|
|
* out_trash(): Throw away the contents of the output buffer
|
|
*/
|
|
void
|
|
out_trash(void)
|
|
{
|
|
out_pos = 0;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* out_char(c): put a byte into the output buffer.
|
|
* Flush it if it becomes full.
|
|
* This should not be used for outputting text on the screen (use functions
|
|
* like msg_puts() and screen_putchar() for that).
|
|
*/
|
|
void
|
|
out_char(unsigned c)
|
|
{
|
|
#if defined(UNIX) || defined(VMS) || defined(AMIGA) || defined(MACOS_X)
|
|
if (c == '\n') // turn LF into CR-LF (CRMOD doesn't seem to do this)
|
|
out_char('\r');
|
|
#endif
|
|
|
|
out_buf[out_pos++] = c;
|
|
|
|
// For testing we flush each time.
|
|
if (out_pos >= OUT_SIZE || p_wd)
|
|
out_flush();
|
|
}
|
|
|
|
/*
|
|
* Output "c" like out_char(), but don't flush when p_wd is set.
|
|
*/
|
|
static int
|
|
out_char_nf(int c)
|
|
{
|
|
out_buf[out_pos++] = (unsigned)c;
|
|
|
|
if (out_pos >= OUT_SIZE)
|
|
out_flush();
|
|
return (unsigned)c;
|
|
}
|
|
|
|
/*
|
|
* A never-padding out_str().
|
|
* Use this whenever you don't want to run the string through tputs().
|
|
* tputs() above is harmless, but tputs() from the termcap library
|
|
* is likely to strip off leading digits, that it mistakes for padding
|
|
* information, and "%i", "%d", etc.
|
|
* This should only be used for writing terminal codes, not for outputting
|
|
* normal text (use functions like msg_puts() and screen_putchar() for that).
|
|
*/
|
|
void
|
|
out_str_nf(char_u *s)
|
|
{
|
|
// avoid terminal strings being split up
|
|
if (out_pos > OUT_SIZE - MAX_ESC_SEQ_LEN)
|
|
out_flush();
|
|
|
|
for (char_u *p = s; *p != NUL; ++p)
|
|
out_char_nf(*p);
|
|
|
|
// For testing we write one string at a time.
|
|
if (p_wd)
|
|
out_flush();
|
|
}
|
|
|
|
/*
|
|
* A conditional-flushing out_str, mainly for visualbell.
|
|
* Handles a delay internally, because termlib may not respect the delay or do
|
|
* it at the wrong time.
|
|
* Note: Only for terminal strings.
|
|
*/
|
|
void
|
|
out_str_cf(char_u *s)
|
|
{
|
|
if (s == NULL || *s == NUL)
|
|
return;
|
|
|
|
#ifdef HAVE_TGETENT
|
|
char_u *p;
|
|
#endif
|
|
|
|
#ifdef FEAT_GUI
|
|
// Don't use tputs() when GUI is used, ncurses crashes.
|
|
if (gui.in_use)
|
|
{
|
|
out_str_nf(s);
|
|
return;
|
|
}
|
|
#endif
|
|
if (out_pos > OUT_SIZE - MAX_ESC_SEQ_LEN)
|
|
out_flush();
|
|
#ifdef HAVE_TGETENT
|
|
for (p = s; *s; ++s)
|
|
{
|
|
// flush just before delay command
|
|
if (*s == '$' && *(s + 1) == '<')
|
|
{
|
|
char_u save_c = *s;
|
|
int duration = atoi((char *)s + 2);
|
|
|
|
*s = NUL;
|
|
tputs((char *)p, 1, TPUTSFUNCAST out_char_nf);
|
|
*s = save_c;
|
|
out_flush();
|
|
# ifdef ELAPSED_FUNC
|
|
// Only sleep here if we can limit this happening in
|
|
// vim_beep().
|
|
p = vim_strchr(s, '>');
|
|
if (p == NULL || duration <= 0)
|
|
{
|
|
// can't parse the time, don't sleep here
|
|
p = s;
|
|
}
|
|
else
|
|
{
|
|
++p;
|
|
do_sleep(duration, FALSE);
|
|
}
|
|
# else
|
|
// Rely on the terminal library to sleep.
|
|
p = s;
|
|
# endif
|
|
break;
|
|
}
|
|
}
|
|
tputs((char *)p, 1, TPUTSFUNCAST out_char_nf);
|
|
#else
|
|
while (*s)
|
|
out_char_nf(*s++);
|
|
#endif
|
|
|
|
// For testing we write one string at a time.
|
|
if (p_wd)
|
|
out_flush();
|
|
}
|
|
|
|
/*
|
|
* out_str(s): Put a character string a byte at a time into the output buffer.
|
|
* If HAVE_TGETENT is defined use tputs(), the termcap parser. (jw)
|
|
* This should only be used for writing terminal codes, not for outputting
|
|
* normal text (use functions like msg_puts() and screen_putchar() for that).
|
|
*/
|
|
void
|
|
out_str(char_u *s)
|
|
{
|
|
if (s == NULL || *s == NUL)
|
|
return;
|
|
|
|
#ifdef FEAT_GUI
|
|
// Don't use tputs() when GUI is used, ncurses crashes.
|
|
if (gui.in_use)
|
|
{
|
|
out_str_nf(s);
|
|
return;
|
|
}
|
|
#endif
|
|
// avoid terminal strings being split up
|
|
if (out_pos > OUT_SIZE - MAX_ESC_SEQ_LEN)
|
|
out_flush();
|
|
#ifdef HAVE_TGETENT
|
|
tputs((char *)s, 1, TPUTSFUNCAST out_char_nf);
|
|
#else
|
|
while (*s)
|
|
out_char_nf(*s++);
|
|
#endif
|
|
|
|
// For testing we write one string at a time.
|
|
if (p_wd)
|
|
out_flush();
|
|
}
|
|
|
|
/*
|
|
* cursor positioning using termcap parser. (jw)
|
|
*/
|
|
void
|
|
term_windgoto(int row, int col)
|
|
{
|
|
OUT_STR(tgoto((char *)T_CM, col, row));
|
|
}
|
|
|
|
void
|
|
term_cursor_right(int i)
|
|
{
|
|
OUT_STR(tgoto((char *)T_CRI, 0, i));
|
|
}
|
|
|
|
void
|
|
term_append_lines(int line_count)
|
|
{
|
|
OUT_STR(tgoto((char *)T_CAL, 0, line_count));
|
|
}
|
|
|
|
void
|
|
term_delete_lines(int line_count)
|
|
{
|
|
OUT_STR(tgoto((char *)T_CDL, 0, line_count));
|
|
}
|
|
|
|
#if defined(UNIX) || defined(VMS) || defined(PROTO)
|
|
void
|
|
term_enable_mouse(int enable)
|
|
{
|
|
int on = enable ? 1 : 0;
|
|
OUT_STR(tgoto((char *)T_CXM, 0, on));
|
|
}
|
|
#endif
|
|
|
|
#if defined(HAVE_TGETENT) || defined(PROTO)
|
|
void
|
|
term_set_winpos(int x, int y)
|
|
{
|
|
// Can't handle a negative value here
|
|
if (x < 0)
|
|
x = 0;
|
|
if (y < 0)
|
|
y = 0;
|
|
OUT_STR(tgoto((char *)T_CWP, y, x));
|
|
}
|
|
|
|
# if defined(FEAT_TERMRESPONSE) || defined(PROTO)
|
|
/*
|
|
* Return TRUE if we can request the terminal for a response.
|
|
*/
|
|
static int
|
|
can_get_termresponse(void)
|
|
{
|
|
return cur_tmode == TMODE_RAW
|
|
&& termcap_active
|
|
# ifdef UNIX
|
|
&& (is_not_a_term() || (isatty(1) && isatty(read_cmd_fd)))
|
|
# endif
|
|
&& p_ek;
|
|
}
|
|
|
|
/*
|
|
* Set "status" to STATUS_SENT.
|
|
*/
|
|
static void
|
|
termrequest_sent(termrequest_T *status)
|
|
{
|
|
status->tr_progress = STATUS_SENT;
|
|
status->tr_start = time(NULL);
|
|
}
|
|
|
|
/*
|
|
* Return TRUE if any of the requests are in STATUS_SENT.
|
|
*/
|
|
static int
|
|
termrequest_any_pending(void)
|
|
{
|
|
int i;
|
|
time_t now = time(NULL);
|
|
|
|
for (i = 0; all_termrequests[i] != NULL; ++i)
|
|
{
|
|
if (all_termrequests[i]->tr_progress == STATUS_SENT)
|
|
{
|
|
if (all_termrequests[i]->tr_start > 0 && now > 0
|
|
&& all_termrequests[i]->tr_start + 2 < now)
|
|
// Sent the request more than 2 seconds ago and didn't get a
|
|
// response, assume it failed.
|
|
all_termrequests[i]->tr_progress = STATUS_FAIL;
|
|
else
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static int winpos_x = -1;
|
|
static int winpos_y = -1;
|
|
static int did_request_winpos = 0;
|
|
|
|
# if defined(FEAT_EVAL) || defined(FEAT_TERMINAL) || defined(PROTO)
|
|
/*
|
|
* Try getting the Vim window position from the terminal.
|
|
* Returns OK or FAIL.
|
|
*/
|
|
int
|
|
term_get_winpos(int *x, int *y, varnumber_T timeout)
|
|
{
|
|
int count = 0;
|
|
int prev_winpos_x = winpos_x;
|
|
int prev_winpos_y = winpos_y;
|
|
|
|
if (*T_CGP == NUL || !can_get_termresponse())
|
|
return FAIL;
|
|
winpos_x = -1;
|
|
winpos_y = -1;
|
|
++did_request_winpos;
|
|
termrequest_sent(&winpos_status);
|
|
OUT_STR(T_CGP);
|
|
out_flush();
|
|
|
|
// Try reading the result for "timeout" msec.
|
|
while (count++ <= timeout / 10 && !got_int)
|
|
{
|
|
(void)vpeekc_nomap();
|
|
if (winpos_x >= 0 && winpos_y >= 0)
|
|
{
|
|
*x = winpos_x;
|
|
*y = winpos_y;
|
|
return OK;
|
|
}
|
|
ui_delay(10L, FALSE);
|
|
}
|
|
// Do not reset "did_request_winpos", if we timed out the response might
|
|
// still come later and we must consume it.
|
|
|
|
winpos_x = prev_winpos_x;
|
|
winpos_y = prev_winpos_y;
|
|
if (timeout < 10 && prev_winpos_y >= 0 && prev_winpos_x >= 0)
|
|
{
|
|
// Polling: return previous values if we have them.
|
|
*x = winpos_x;
|
|
*y = winpos_y;
|
|
return OK;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
# endif
|
|
# endif
|
|
|
|
void
|
|
term_set_winsize(int height, int width)
|
|
{
|
|
OUT_STR(tgoto((char *)T_CWS, width, height));
|
|
}
|
|
#endif
|
|
|
|
void
|
|
term_font(int n)
|
|
{
|
|
if (*T_CFO)
|
|
{
|
|
char buf[20];
|
|
sprintf(buf, (char *)T_CFO, 9 + n);
|
|
OUT_STR(buf);
|
|
}
|
|
}
|
|
|
|
static void
|
|
term_color(char_u *s, int n)
|
|
{
|
|
char buf[20];
|
|
int i = *s == CSI ? 1 : 2;
|
|
// index in s[] just after <Esc>[ or CSI
|
|
|
|
// Special handling of 16 colors, because termcap can't handle it
|
|
// Also accept "\e[3%dm" for TERMINFO, it is sometimes used
|
|
// Also accept CSI instead of <Esc>[
|
|
if (n >= 8 && t_colors >= 16
|
|
&& ((s[0] == ESC && s[1] == '[')
|
|
#if defined(FEAT_VTP) && defined(FEAT_TERMGUICOLORS)
|
|
|| (s[0] == ESC && s[1] == '|')
|
|
#endif
|
|
|| (s[0] == CSI && (i = 1) == 1))
|
|
&& s[i] != NUL
|
|
&& (STRCMP(s + i + 1, "%p1%dm") == 0
|
|
|| STRCMP(s + i + 1, "%dm") == 0)
|
|
&& (s[i] == '3' || s[i] == '4'))
|
|
{
|
|
#ifdef TERMINFO
|
|
char *format = "%s%s%%p1%%dm";
|
|
#else
|
|
char *format = "%s%s%%dm";
|
|
#endif
|
|
char *lead = i == 2 ? (
|
|
#if defined(FEAT_VTP) && defined(FEAT_TERMGUICOLORS)
|
|
s[1] == '|' ? "\033|" :
|
|
#endif
|
|
"\033[") : "\233";
|
|
char *tail = s[i] == '3' ? (n >= 16 ? "38;5;" : "9")
|
|
: (n >= 16 ? "48;5;" : "10");
|
|
|
|
sprintf(buf, format, lead, tail);
|
|
OUT_STR(tgoto(buf, 0, n >= 16 ? n : n - 8));
|
|
}
|
|
else
|
|
OUT_STR(tgoto((char *)s, 0, n));
|
|
}
|
|
|
|
void
|
|
term_fg_color(int n)
|
|
{
|
|
// Use "AF" termcap entry if present, "Sf" entry otherwise
|
|
if (*T_CAF)
|
|
term_color(T_CAF, n);
|
|
else if (*T_CSF)
|
|
term_color(T_CSF, n);
|
|
}
|
|
|
|
void
|
|
term_bg_color(int n)
|
|
{
|
|
// Use "AB" termcap entry if present, "Sb" entry otherwise
|
|
if (*T_CAB)
|
|
term_color(T_CAB, n);
|
|
else if (*T_CSB)
|
|
term_color(T_CSB, n);
|
|
}
|
|
|
|
void
|
|
term_ul_color(int n)
|
|
{
|
|
if (*T_CAU)
|
|
term_color(T_CAU, n);
|
|
}
|
|
|
|
/*
|
|
* Return "dark" or "light" depending on the kind of terminal.
|
|
* This is just guessing! Recognized are:
|
|
* "linux" Linux console
|
|
* "screen.linux" Linux console with screen
|
|
* "cygwin.*" Cygwin shell
|
|
* "putty.*" Putty program
|
|
* We also check the COLORFGBG environment variable, which is set by
|
|
* rxvt and derivatives. This variable contains either two or three
|
|
* values separated by semicolons; we want the last value in either
|
|
* case. If this value is 0-6 or 8, our background is dark.
|
|
*/
|
|
char_u *
|
|
term_bg_default(void)
|
|
{
|
|
#if defined(MSWIN)
|
|
// DOS console is nearly always black
|
|
return (char_u *)"dark";
|
|
#else
|
|
char_u *p;
|
|
|
|
if (STRCMP(T_NAME, "linux") == 0
|
|
|| STRCMP(T_NAME, "screen.linux") == 0
|
|
|| STRNCMP(T_NAME, "cygwin", 6) == 0
|
|
|| STRNCMP(T_NAME, "putty", 5) == 0
|
|
|| ((p = mch_getenv((char_u *)"COLORFGBG")) != NULL
|
|
&& (p = vim_strrchr(p, ';')) != NULL
|
|
&& ((p[1] >= '0' && p[1] <= '6') || p[1] == '8')
|
|
&& p[2] == NUL))
|
|
return (char_u *)"dark";
|
|
return (char_u *)"light";
|
|
#endif
|
|
}
|
|
|
|
#if defined(FEAT_TERMGUICOLORS) || defined(PROTO)
|
|
|
|
#define RED(rgb) (((long_u)(rgb) >> 16) & 0xFF)
|
|
#define GREEN(rgb) (((long_u)(rgb) >> 8) & 0xFF)
|
|
#define BLUE(rgb) (((long_u)(rgb) ) & 0xFF)
|
|
|
|
static void
|
|
term_rgb_color(char_u *s, guicolor_T rgb)
|
|
{
|
|
#define MAX_COLOR_STR_LEN 100
|
|
char buf[MAX_COLOR_STR_LEN];
|
|
|
|
if (*s == NUL)
|
|
return;
|
|
vim_snprintf(buf, MAX_COLOR_STR_LEN,
|
|
(char *)s, RED(rgb), GREEN(rgb), BLUE(rgb));
|
|
#ifdef FEAT_VTP
|
|
if (use_vtp() && (p_tgc || t_colors >= 256))
|
|
{
|
|
out_flush();
|
|
buf[1] = '[';
|
|
vtp_printf(buf);
|
|
}
|
|
else
|
|
#endif
|
|
OUT_STR(buf);
|
|
}
|
|
|
|
void
|
|
term_fg_rgb_color(guicolor_T rgb)
|
|
{
|
|
if (rgb != INVALCOLOR)
|
|
term_rgb_color(T_8F, rgb);
|
|
}
|
|
|
|
void
|
|
term_bg_rgb_color(guicolor_T rgb)
|
|
{
|
|
if (rgb != INVALCOLOR)
|
|
term_rgb_color(T_8B, rgb);
|
|
}
|
|
|
|
void
|
|
term_ul_rgb_color(guicolor_T rgb)
|
|
{
|
|
# ifdef FEAT_TERMRESPONSE
|
|
// If the user explicitly sets t_8u then use it. Otherwise wait for
|
|
// termresponse to be received, which is when t_8u would be set and a
|
|
// redraw is needed if it was used.
|
|
if (!option_was_set((char_u *)"t_8u") && write_t_8u_state != OK)
|
|
write_t_8u_state = MAYBE;
|
|
else
|
|
# endif
|
|
term_rgb_color(T_8U, rgb);
|
|
}
|
|
#endif
|
|
|
|
#if (defined(UNIX) || defined(VMS) || defined(MACOS_X)) || defined(PROTO)
|
|
/*
|
|
* Generic function to set window title, using t_ts and t_fs.
|
|
*/
|
|
void
|
|
term_settitle(char_u *title)
|
|
{
|
|
MAY_WANT_TO_LOG_THIS;
|
|
|
|
// t_ts takes one argument: column in status line
|
|
OUT_STR(tgoto((char *)T_TS, 0, 0)); // set title start
|
|
out_str_nf(title);
|
|
out_str(T_FS); // set title end
|
|
out_flush();
|
|
}
|
|
|
|
/*
|
|
* Tell the terminal to push (save) the title and/or icon, so that it can be
|
|
* popped (restored) later.
|
|
*/
|
|
void
|
|
term_push_title(int which)
|
|
{
|
|
if ((which & SAVE_RESTORE_TITLE) && T_CST != NULL && *T_CST != NUL)
|
|
{
|
|
OUT_STR(T_CST);
|
|
out_flush();
|
|
}
|
|
|
|
if ((which & SAVE_RESTORE_ICON) && T_SSI != NULL && *T_SSI != NUL)
|
|
{
|
|
OUT_STR(T_SSI);
|
|
out_flush();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Tell the terminal to pop the title and/or icon.
|
|
*/
|
|
void
|
|
term_pop_title(int which)
|
|
{
|
|
if ((which & SAVE_RESTORE_TITLE) && T_CRT != NULL && *T_CRT != NUL)
|
|
{
|
|
OUT_STR(T_CRT);
|
|
out_flush();
|
|
}
|
|
|
|
if ((which & SAVE_RESTORE_ICON) && T_SRI != NULL && *T_SRI != NUL)
|
|
{
|
|
OUT_STR(T_SRI);
|
|
out_flush();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Make sure we have a valid set or terminal options.
|
|
* Replace all entries that are NULL by empty_option
|
|
*/
|
|
void
|
|
ttest(int pairs)
|
|
{
|
|
char_u *env_colors;
|
|
|
|
check_options(); // make sure no options are NULL
|
|
|
|
/*
|
|
* MUST have "cm": cursor motion.
|
|
*/
|
|
if (*T_CM == NUL)
|
|
emsg(_(e_terminal_capability_cm_required));
|
|
|
|
/*
|
|
* if "cs" defined, use a scroll region, it's faster.
|
|
*/
|
|
if (*T_CS != NUL)
|
|
scroll_region = TRUE;
|
|
else
|
|
scroll_region = FALSE;
|
|
|
|
if (pairs)
|
|
{
|
|
/*
|
|
* optional pairs
|
|
*/
|
|
// TP goes to normal mode for TI (invert) and TB (bold)
|
|
if (*T_ME == NUL)
|
|
T_ME = T_MR = T_MD = T_MB = empty_option;
|
|
if (*T_SO == NUL || *T_SE == NUL)
|
|
T_SO = T_SE = empty_option;
|
|
if (*T_US == NUL || *T_UE == NUL)
|
|
T_US = T_UE = empty_option;
|
|
if (*T_CZH == NUL || *T_CZR == NUL)
|
|
T_CZH = T_CZR = empty_option;
|
|
|
|
// T_VE is needed even though T_VI is not defined
|
|
if (*T_VE == NUL)
|
|
T_VI = empty_option;
|
|
|
|
// if 'mr' or 'me' is not defined use 'so' and 'se'
|
|
if (*T_ME == NUL)
|
|
{
|
|
T_ME = T_SE;
|
|
T_MR = T_SO;
|
|
T_MD = T_SO;
|
|
}
|
|
|
|
// if 'so' or 'se' is not defined use 'mr' and 'me'
|
|
if (*T_SO == NUL)
|
|
{
|
|
T_SE = T_ME;
|
|
if (*T_MR == NUL)
|
|
T_SO = T_MD;
|
|
else
|
|
T_SO = T_MR;
|
|
}
|
|
|
|
// if 'ZH' or 'ZR' is not defined use 'mr' and 'me'
|
|
if (*T_CZH == NUL)
|
|
{
|
|
T_CZR = T_ME;
|
|
if (*T_MR == NUL)
|
|
T_CZH = T_MD;
|
|
else
|
|
T_CZH = T_MR;
|
|
}
|
|
|
|
// "Sb" and "Sf" come in pairs
|
|
if (*T_CSB == NUL || *T_CSF == NUL)
|
|
{
|
|
T_CSB = empty_option;
|
|
T_CSF = empty_option;
|
|
}
|
|
|
|
// "AB" and "AF" come in pairs
|
|
if (*T_CAB == NUL || *T_CAF == NUL)
|
|
{
|
|
T_CAB = empty_option;
|
|
T_CAF = empty_option;
|
|
}
|
|
|
|
// if 'Sb' and 'AB' are not defined, reset "Co"
|
|
if (*T_CSB == NUL && *T_CAB == NUL)
|
|
free_one_termoption(T_CCO);
|
|
|
|
// Set 'weirdinvert' according to value of 't_xs'
|
|
p_wiv = (*T_XS != NUL);
|
|
}
|
|
need_gather = TRUE;
|
|
|
|
// Set t_colors to the value of $COLORS or t_Co. Ignore $COLORS in the
|
|
// GUI.
|
|
t_colors = atoi((char *)T_CCO);
|
|
#ifdef FEAT_GUI
|
|
if (!gui.in_use)
|
|
#endif
|
|
{
|
|
env_colors = mch_getenv((char_u *)"COLORS");
|
|
if (env_colors != NULL && SAFE_isdigit(*env_colors))
|
|
{
|
|
int colors = atoi((char *)env_colors);
|
|
|
|
if (colors != t_colors)
|
|
set_color_count(colors);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if (defined(FEAT_GUI) && (defined(FEAT_MENU) || !defined(USE_ON_FLY_SCROLL))) \
|
|
|| defined(PROTO)
|
|
/*
|
|
* Represent the given long_u as individual bytes, with the most significant
|
|
* byte first, and store them in dst.
|
|
*/
|
|
void
|
|
add_long_to_buf(long_u val, char_u *dst)
|
|
{
|
|
int i;
|
|
int shift;
|
|
|
|
for (i = 1; i <= (int)sizeof(long_u); i++)
|
|
{
|
|
shift = 8 * (sizeof(long_u) - i);
|
|
dst[i - 1] = (char_u) ((val >> shift) & 0xff);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Interpret the next string of bytes in buf as a long integer, with the most
|
|
* significant byte first. Note that it is assumed that buf has been through
|
|
* inchar(), so that NUL and K_SPECIAL will be represented as three bytes each.
|
|
* Puts result in val, and returns the number of bytes read from buf
|
|
* (between sizeof(long_u) and 2 * sizeof(long_u)), or -1 if not enough bytes
|
|
* were present.
|
|
*/
|
|
static int
|
|
get_long_from_buf(char_u *buf, long_u *val)
|
|
{
|
|
int len;
|
|
char_u bytes[sizeof(long_u)];
|
|
int i;
|
|
int shift;
|
|
|
|
*val = 0;
|
|
len = get_bytes_from_buf(buf, bytes, (int)sizeof(long_u));
|
|
if (len == -1)
|
|
return -1;
|
|
for (i = 0; i < (int)sizeof(long_u); i++)
|
|
{
|
|
shift = 8 * (sizeof(long_u) - 1 - i);
|
|
*val += (long_u)bytes[i] << shift;
|
|
}
|
|
return len;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Read the next num_bytes bytes from buf, and store them in bytes. Assume
|
|
* that buf has been through inchar(). Returns the actual number of bytes used
|
|
* from buf (between num_bytes and num_bytes*2), or -1 if not enough bytes were
|
|
* available.
|
|
*/
|
|
int
|
|
get_bytes_from_buf(char_u *buf, char_u *bytes, int num_bytes)
|
|
{
|
|
int len = 0;
|
|
int i;
|
|
char_u c;
|
|
|
|
for (i = 0; i < num_bytes; i++)
|
|
{
|
|
if ((c = buf[len++]) == NUL)
|
|
return -1;
|
|
if (c == K_SPECIAL)
|
|
{
|
|
if (buf[len] == NUL || buf[len + 1] == NUL) // cannot happen?
|
|
return -1;
|
|
if (buf[len++] == (int)KS_ZERO)
|
|
c = NUL;
|
|
// else it should be KS_SPECIAL; when followed by KE_FILLER c is
|
|
// K_SPECIAL, or followed by KE_CSI and c must be CSI.
|
|
if (buf[len++] == (int)KE_CSI)
|
|
c = CSI;
|
|
}
|
|
else if (c == CSI && buf[len] == KS_EXTRA
|
|
&& buf[len + 1] == (int)KE_CSI)
|
|
// CSI is stored as CSI KS_SPECIAL KE_CSI to avoid confusion with
|
|
// the start of a special key, see add_to_input_buf_csi().
|
|
len += 2;
|
|
bytes[i] = c;
|
|
}
|
|
return len;
|
|
}
|
|
|
|
/*
|
|
* Check if the new shell size is valid, correct it if it's too small or way
|
|
* too big.
|
|
*/
|
|
void
|
|
check_shellsize(void)
|
|
{
|
|
// need room for one window and command line
|
|
if (Rows < min_rows_for_all_tabpages())
|
|
Rows = min_rows_for_all_tabpages();
|
|
limit_screen_size();
|
|
|
|
// make sure these values are not invalid
|
|
if (cmdline_row >= Rows)
|
|
cmdline_row = Rows - 1;
|
|
if (msg_row >= Rows)
|
|
msg_row = Rows - 1;
|
|
}
|
|
|
|
/*
|
|
* Limit Rows and Columns to avoid an overflow in Rows * Columns.
|
|
*/
|
|
void
|
|
limit_screen_size(void)
|
|
{
|
|
if (Columns < MIN_COLUMNS)
|
|
Columns = MIN_COLUMNS;
|
|
else if (Columns > 10000)
|
|
Columns = 10000;
|
|
if (Rows > 1000)
|
|
Rows = 1000;
|
|
}
|
|
|
|
/*
|
|
* Invoked just before the screen structures are going to be (re)allocated.
|
|
*/
|
|
void
|
|
win_new_shellsize(void)
|
|
{
|
|
static int old_Rows = 0;
|
|
static int old_Columns = 0;
|
|
|
|
if (old_Rows != Rows || old_Columns != Columns)
|
|
ui_new_shellsize();
|
|
if (old_Rows != Rows)
|
|
{
|
|
// If 'window' uses the whole screen, keep it using that.
|
|
// Don't change it when set with "-w size" on the command line.
|
|
if (p_window == old_Rows - 1
|
|
|| (old_Rows == 0 && !option_was_set((char_u *)"window")))
|
|
p_window = Rows - 1;
|
|
old_Rows = Rows;
|
|
shell_new_rows(); // update window sizes
|
|
}
|
|
if (old_Columns != Columns)
|
|
{
|
|
old_Columns = Columns;
|
|
shell_new_columns(); // update window sizes
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Call this function when the Vim shell has been resized in any way.
|
|
* Will obtain the current size and redraw (also when size didn't change).
|
|
*/
|
|
void
|
|
shell_resized(void)
|
|
{
|
|
set_shellsize(0, 0, FALSE);
|
|
}
|
|
|
|
/*
|
|
* Check if the shell size changed. Handle a resize.
|
|
* When the size didn't change, nothing happens.
|
|
*/
|
|
void
|
|
shell_resized_check(void)
|
|
{
|
|
int old_Rows = Rows;
|
|
int old_Columns = Columns;
|
|
|
|
if (exiting
|
|
#ifdef FEAT_GUI
|
|
// Do not get the size when executing a shell command during
|
|
// startup.
|
|
|| gui.starting
|
|
#endif
|
|
)
|
|
return;
|
|
|
|
(void)ui_get_shellsize();
|
|
check_shellsize();
|
|
if (old_Rows != Rows || old_Columns != Columns)
|
|
shell_resized();
|
|
}
|
|
|
|
/*
|
|
* Set size of the Vim shell.
|
|
* If 'mustset' is TRUE, we must set Rows and Columns, do not get the real
|
|
* window size (this is used for the :win command).
|
|
* If 'mustset' is FALSE, we may try to get the real window size and if
|
|
* it fails use 'width' and 'height'.
|
|
*/
|
|
static void
|
|
set_shellsize_inner(int width, int height, int mustset)
|
|
{
|
|
if (updating_screen)
|
|
// resizing while in update_screen() may cause a crash
|
|
return;
|
|
|
|
// curwin->w_buffer can be NULL when we are closing a window and the
|
|
// buffer (or window) has already been closed and removing a scrollbar
|
|
// causes a resize event. Don't resize then, it will happen after entering
|
|
// another buffer.
|
|
if (curwin->w_buffer == NULL || curwin->w_lines == NULL)
|
|
return;
|
|
|
|
#ifdef AMIGA
|
|
out_flush(); // must do this before mch_get_shellsize() for
|
|
// some obscure reason
|
|
#endif
|
|
|
|
if (mustset || (ui_get_shellsize() == FAIL && height != 0))
|
|
{
|
|
Rows = height;
|
|
Columns = width;
|
|
check_shellsize();
|
|
ui_set_shellsize(mustset);
|
|
}
|
|
else
|
|
check_shellsize();
|
|
|
|
// The window layout used to be adjusted here, but it now happens in
|
|
// screenalloc() (also invoked from screenclear()). That is because the
|
|
// "busy" check above may skip this, but not screenalloc().
|
|
|
|
if (State != MODE_ASKMORE && State != MODE_EXTERNCMD
|
|
&& State != MODE_CONFIRM)
|
|
screenclear();
|
|
else
|
|
screen_start(); // don't know where cursor is now
|
|
|
|
if (starting != NO_SCREEN)
|
|
{
|
|
maketitle();
|
|
|
|
changed_line_abv_curs();
|
|
invalidate_botline();
|
|
|
|
/*
|
|
* We only redraw when it's needed:
|
|
* - While at the more prompt or executing an external command, don't
|
|
* redraw, but position the cursor.
|
|
* - While editing the command line, only redraw that.
|
|
* - in Ex mode, don't redraw anything.
|
|
* - Otherwise, redraw right now, and position the cursor.
|
|
* Always need to call update_screen() or screenalloc(), to make
|
|
* sure Rows/Columns and the size of ScreenLines[] is correct!
|
|
*/
|
|
if (State == MODE_ASKMORE || State == MODE_EXTERNCMD
|
|
|| State == MODE_CONFIRM || exmode_active)
|
|
{
|
|
screenalloc(FALSE);
|
|
repeat_message();
|
|
}
|
|
else
|
|
{
|
|
if (curwin->w_p_scb)
|
|
do_check_scrollbind(TRUE);
|
|
if (State & MODE_CMDLINE)
|
|
{
|
|
update_screen(UPD_NOT_VALID);
|
|
redrawcmdline();
|
|
}
|
|
else
|
|
{
|
|
update_topline();
|
|
if (pum_visible())
|
|
{
|
|
redraw_later(UPD_NOT_VALID);
|
|
ins_compl_show_pum();
|
|
}
|
|
update_screen(UPD_NOT_VALID);
|
|
if (redrawing())
|
|
setcursor();
|
|
}
|
|
}
|
|
cursor_on(); // redrawing may have switched it off
|
|
}
|
|
out_flush();
|
|
}
|
|
|
|
void
|
|
set_shellsize(int width, int height, int mustset)
|
|
{
|
|
static int busy = FALSE;
|
|
static int do_run = FALSE;
|
|
|
|
if (width < 0 || height < 0) // just checking...
|
|
return;
|
|
|
|
if (State == MODE_HITRETURN || State == MODE_SETWSIZE)
|
|
{
|
|
// postpone the resizing
|
|
State = MODE_SETWSIZE;
|
|
return;
|
|
}
|
|
|
|
// Avoid recursiveness. This can happen when setting the window size
|
|
// causes another window-changed signal or when two SIGWINCH signals come
|
|
// very close together. There needs to be another run then after the
|
|
// current one is done to pick up the latest size.
|
|
do_run = TRUE;
|
|
if (busy)
|
|
return;
|
|
|
|
while (do_run)
|
|
{
|
|
do_run = FALSE;
|
|
busy = TRUE;
|
|
set_shellsize_inner(width, height, mustset);
|
|
busy = FALSE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Output T_CTE, the t_TE termcap entry, and handle expected effects.
|
|
* The code possibly disables modifyOtherKeys and the Kitty keyboard protocol.
|
|
*/
|
|
void
|
|
out_str_t_TE(void)
|
|
{
|
|
out_str(T_CTE);
|
|
|
|
// The seenModifyOtherKeys flag is not reset here. We do expect t_TE to
|
|
// disable modifyOtherKeys, but until Xterm version 377 there is no way to
|
|
// detect it's enabled again after the following t_TI. We assume that when
|
|
// seenModifyOtherKeys was set before it will still be valid.
|
|
|
|
// When the modifyOtherKeys level is detected to be 2 we expect t_TE to
|
|
// disable it. Remembering that it was detected to be enabled is useful in
|
|
// some situations.
|
|
// The following t_TI is expected to request the state and then
|
|
// modify_otherkeys_state will be set again.
|
|
if (modify_otherkeys_state == MOKS_ENABLED
|
|
|| modify_otherkeys_state == MOKS_DISABLED)
|
|
modify_otherkeys_state = MOKS_DISABLED;
|
|
else if (modify_otherkeys_state != MOKS_INITIAL)
|
|
modify_otherkeys_state = MOKS_AFTER_T_TE;
|
|
|
|
// When the kitty keyboard protocol is enabled we expect t_TE to disable
|
|
// it. Remembering that it was detected to be enabled is useful in some
|
|
// situations.
|
|
// The following t_TI is expected to request the state and then
|
|
// kitty_protocol_state will be set again.
|
|
if (kitty_protocol_state == KKPS_ENABLED
|
|
|| kitty_protocol_state == KKPS_DISABLED)
|
|
kitty_protocol_state = KKPS_DISABLED;
|
|
else
|
|
kitty_protocol_state = KKPS_AFTER_T_TE;
|
|
}
|
|
|
|
static int send_t_RK = FALSE;
|
|
|
|
/*
|
|
* Output T_TI and setup for what follows.
|
|
*/
|
|
void
|
|
out_str_t_TI(void)
|
|
{
|
|
out_str(T_CTI);
|
|
|
|
// Send t_RK when there is no more work to do.
|
|
send_t_RK = TRUE;
|
|
}
|
|
|
|
/*
|
|
* Output T_BE, but only when t_PS and t_PE are set.
|
|
*/
|
|
void
|
|
out_str_t_BE(void)
|
|
{
|
|
char_u *p;
|
|
|
|
if (T_BE == NULL || *T_BE == NUL
|
|
|| (p = find_termcode((char_u *)"PS")) == NULL || *p == NUL
|
|
|| (p = find_termcode((char_u *)"PE")) == NULL || *p == NUL)
|
|
return;
|
|
out_str(T_BE);
|
|
}
|
|
|
|
/*
|
|
* If t_TI was recently sent and there is no typeahead or work to do, now send
|
|
* t_RK. This is postponed to avoid the response arriving in a shell command
|
|
* or after Vim exits.
|
|
*/
|
|
void
|
|
may_send_t_RK(void)
|
|
{
|
|
if (send_t_RK
|
|
&& !work_pending()
|
|
&& !ex_normal_busy
|
|
#ifdef FEAT_EVAL
|
|
&& !in_feedkeys
|
|
#endif
|
|
&& !exiting)
|
|
{
|
|
send_t_RK = FALSE;
|
|
out_str(T_CRK);
|
|
out_flush();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set the terminal to TMODE_RAW (for Normal mode) or TMODE_COOK (for external
|
|
* commands and Ex mode).
|
|
*/
|
|
void
|
|
settmode(tmode_T tmode)
|
|
{
|
|
#ifdef FEAT_GUI
|
|
// don't set the term where gvim was started to any mode
|
|
if (gui.in_use)
|
|
return;
|
|
#endif
|
|
|
|
if (!full_screen)
|
|
return;
|
|
|
|
/*
|
|
* When returning after calling a shell cur_tmode is TMODE_UNKNOWN,
|
|
* set the terminal to raw mode, even though we think it already is,
|
|
* because the shell program may have reset the terminal mode.
|
|
* When we think the terminal is normal, don't try to set it to
|
|
* normal again, because that causes problems (logout!) on some
|
|
* machines.
|
|
*/
|
|
if (tmode != cur_tmode)
|
|
{
|
|
#ifdef FEAT_TERMRESPONSE
|
|
# ifdef FEAT_GUI
|
|
if (!gui.in_use && !gui.starting)
|
|
# endif
|
|
{
|
|
// May need to check for T_CRV response and termcodes, it
|
|
// doesn't work in Cooked mode, an external program may get
|
|
// them.
|
|
if (tmode != TMODE_RAW && termrequest_any_pending())
|
|
(void)vpeekc_nomap();
|
|
check_for_codes_from_term();
|
|
}
|
|
#endif
|
|
if (tmode != TMODE_RAW)
|
|
mch_setmouse(FALSE); // switch mouse off
|
|
|
|
// Disable bracketed paste and modifyOtherKeys in cooked mode.
|
|
// Avoid doing this too often, on some terminals the codes are not
|
|
// handled properly.
|
|
if (termcap_active && tmode != TMODE_SLEEP
|
|
&& cur_tmode != TMODE_SLEEP)
|
|
{
|
|
MAY_WANT_TO_LOG_THIS;
|
|
|
|
if (tmode != TMODE_RAW)
|
|
{
|
|
out_str(T_BD); // disable bracketed paste mode
|
|
out_str_t_TE(); // possibly disables modifyOtherKeys
|
|
}
|
|
else
|
|
{
|
|
out_str_t_BE(); // enable bracketed paste mode (should
|
|
// be before mch_settmode().
|
|
out_str_t_TI(); // possibly enables modifyOtherKeys
|
|
}
|
|
}
|
|
out_flush();
|
|
mch_settmode(tmode); // machine specific function
|
|
cur_tmode = tmode;
|
|
if (tmode == TMODE_RAW)
|
|
setmouse(); // may switch mouse on
|
|
out_flush();
|
|
}
|
|
#ifdef FEAT_TERMRESPONSE
|
|
may_req_termresponse();
|
|
#endif
|
|
}
|
|
|
|
void
|
|
starttermcap(void)
|
|
{
|
|
if (!full_screen || termcap_active)
|
|
return;
|
|
|
|
MAY_WANT_TO_LOG_THIS;
|
|
|
|
out_str(T_TI); // start termcap mode
|
|
out_str_t_TI(); // start "raw" mode
|
|
out_str(T_KS); // start "keypad transmit" mode
|
|
out_str_t_BE(); // enable bracketed paste mode
|
|
|
|
#if defined(UNIX) || defined(VMS)
|
|
// Enable xterm's focus reporting mode when 'esckeys' is set.
|
|
if (p_ek && *T_FE != NUL)
|
|
out_str(T_FE);
|
|
#endif
|
|
|
|
out_flush();
|
|
termcap_active = TRUE;
|
|
screen_start(); // don't know where cursor is now
|
|
#ifdef FEAT_TERMRESPONSE
|
|
# ifdef FEAT_GUI
|
|
if (!gui.in_use && !gui.starting)
|
|
# endif
|
|
{
|
|
may_req_termresponse();
|
|
// Immediately check for a response. If t_Co changes, we don't
|
|
// want to redraw with wrong colors first.
|
|
if (crv_status.tr_progress == STATUS_SENT)
|
|
check_for_codes_from_term();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
stoptermcap(void)
|
|
{
|
|
screen_stop_highlight();
|
|
reset_cterm_colors();
|
|
|
|
if (!termcap_active)
|
|
return;
|
|
|
|
#ifdef FEAT_TERMRESPONSE
|
|
# ifdef FEAT_GUI
|
|
if (!gui.in_use && !gui.starting)
|
|
# endif
|
|
{
|
|
// May need to discard T_CRV, T_U7 or T_RBG response.
|
|
if (termrequest_any_pending())
|
|
{
|
|
# ifdef UNIX
|
|
// Give the terminal a chance to respond.
|
|
mch_delay(100L, 0);
|
|
# endif
|
|
# ifdef TCIFLUSH
|
|
// Discard data received but not read.
|
|
if (exiting)
|
|
tcflush(fileno(stdin), TCIFLUSH);
|
|
# endif
|
|
}
|
|
// Check for termcodes first, otherwise an external program may
|
|
// get them.
|
|
check_for_codes_from_term();
|
|
}
|
|
#endif
|
|
MAY_WANT_TO_LOG_THIS;
|
|
|
|
#if defined(UNIX) || defined(VMS)
|
|
// Disable xterm's focus reporting mode if 'esckeys' is set.
|
|
if (p_ek && *T_FD != NUL)
|
|
out_str(T_FD);
|
|
#endif
|
|
|
|
out_str(T_BD); // disable bracketed paste mode
|
|
out_str(T_KE); // stop "keypad transmit" mode
|
|
out_flush();
|
|
termcap_active = FALSE;
|
|
|
|
// Output t_te before t_TE, t_te may switch between main and alternate
|
|
// screen and following codes may work on the active screen only.
|
|
//
|
|
// When using the Kitty keyboard protocol the main and alternate screen
|
|
// use a separate state. If we are (or were) using the Kitty keyboard
|
|
// protocol and t_te is not empty (possibly switching screens) then
|
|
// output t_TE both before and after outputting t_te.
|
|
if (*T_TE != NUL && (kitty_protocol_state == KKPS_ENABLED
|
|
|| kitty_protocol_state == KKPS_DISABLED))
|
|
out_str_t_TE(); // probably disables the kitty keyboard
|
|
// protocol
|
|
|
|
out_str(T_TE); // stop termcap mode
|
|
cursor_on(); // just in case it is still off
|
|
out_str_t_TE(); // stop "raw" mode, modifyOtherKeys and
|
|
// Kitty keyboard protocol
|
|
screen_start(); // don't know where cursor is now
|
|
out_flush();
|
|
}
|
|
|
|
#if defined(FEAT_TERMRESPONSE) || defined(PROTO)
|
|
/*
|
|
* Request version string (for xterm) when needed.
|
|
* Only do this after switching to raw mode, otherwise the result will be
|
|
* echoed.
|
|
* Only do this after startup has finished, to avoid that the response comes
|
|
* while executing "-c !cmd" or even after "-c quit".
|
|
* Only do this after termcap mode has been started, otherwise the codes for
|
|
* the cursor keys may be wrong.
|
|
* Only do this when 'esckeys' is on, otherwise the response causes trouble in
|
|
* Insert mode.
|
|
* On Unix only do it when both output and input are a tty (avoid writing
|
|
* request to terminal while reading from a file).
|
|
* The result is caught in check_termcode().
|
|
*/
|
|
void
|
|
may_req_termresponse(void)
|
|
{
|
|
if (crv_status.tr_progress == STATUS_GET
|
|
&& can_get_termresponse()
|
|
&& starting == 0
|
|
&& *T_CRV != NUL)
|
|
{
|
|
MAY_WANT_TO_LOG_THIS;
|
|
LOG_TR(("Sending CRV request"));
|
|
out_str(T_CRV);
|
|
termrequest_sent(&crv_status);
|
|
// check for the characters now, otherwise they might be eaten by
|
|
// get_keystroke()
|
|
out_flush();
|
|
(void)vpeekc_nomap();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Send sequences to the terminal and check with t_u7 how the cursor moves, to
|
|
* find out properties of the terminal.
|
|
* Note that this goes out before T_CRV, so that the result can be used when
|
|
* the termresponse arrives.
|
|
*/
|
|
void
|
|
check_terminal_behavior(void)
|
|
{
|
|
int did_send = FALSE;
|
|
|
|
if (!can_get_termresponse() || starting != 0 || *T_U7 == NUL)
|
|
return;
|
|
|
|
if (u7_status.tr_progress == STATUS_GET
|
|
&& !option_was_set((char_u *)"ambiwidth"))
|
|
{
|
|
char_u buf[16];
|
|
|
|
// Ambiguous width check.
|
|
// Check how the terminal treats ambiguous character width (UAX #11).
|
|
// First, we move the cursor to (1, 0) and print a test ambiguous
|
|
// character \u25bd (WHITE DOWN-POINTING TRIANGLE) and then query
|
|
// the current cursor position. If the terminal treats \u25bd as
|
|
// single width, the position is (1, 1), or if it is treated as double
|
|
// width, that will be (1, 2). This function has the side effect that
|
|
// changes cursor position, so it must be called immediately after
|
|
// entering termcap mode.
|
|
MAY_WANT_TO_LOG_THIS;
|
|
LOG_TR(("Sending request for ambiwidth check"));
|
|
// Do this in the second row. In the first row the returned sequence
|
|
// may be CSI 1;2R, which is the same as <S-F3>.
|
|
term_windgoto(1, 0);
|
|
buf[mb_char2bytes(0x25bd, buf)] = NUL;
|
|
out_str(buf);
|
|
out_str(T_U7);
|
|
termrequest_sent(&u7_status);
|
|
out_flush();
|
|
did_send = TRUE;
|
|
|
|
// This overwrites a few characters on the screen, a redraw is needed
|
|
// after this. Clear them out for now.
|
|
screen_stop_highlight();
|
|
term_windgoto(1, 0);
|
|
out_str((char_u *)" ");
|
|
line_was_clobbered(1);
|
|
}
|
|
|
|
if (xcc_status.tr_progress == STATUS_GET && Rows > 2)
|
|
{
|
|
// 2. Check compatibility with xterm.
|
|
// We move the cursor to (2, 0), print a test sequence and then query
|
|
// the current cursor position. If the terminal properly handles
|
|
// unknown DCS string and CSI sequence with intermediate byte, the test
|
|
// sequence is ignored and the cursor does not move. If the terminal
|
|
// handles test sequence incorrectly, a garbage string is displayed and
|
|
// the cursor does move.
|
|
MAY_WANT_TO_LOG_THIS;
|
|
LOG_TR(("Sending xterm compatibility test sequence."));
|
|
// Do this in the third row. Second row is used by ambiguous
|
|
// character width check.
|
|
term_windgoto(2, 0);
|
|
// send the test DCS string.
|
|
out_str((char_u *)"\033Pzz\033\\");
|
|
// send the test CSI sequence with intermediate byte.
|
|
out_str((char_u *)"\033[0%m");
|
|
out_str(T_U7);
|
|
termrequest_sent(&xcc_status);
|
|
out_flush();
|
|
did_send = TRUE;
|
|
|
|
// If the terminal handles test sequence incorrectly, garbage text is
|
|
// displayed. Clear them out for now.
|
|
screen_stop_highlight();
|
|
term_windgoto(2, 0);
|
|
out_str((char_u *)" ");
|
|
line_was_clobbered(2);
|
|
}
|
|
|
|
if (did_send)
|
|
{
|
|
term_windgoto(0, 0);
|
|
|
|
// Need to reset the known cursor position.
|
|
screen_start();
|
|
|
|
// check for the characters now, otherwise they might be eaten by
|
|
// get_keystroke()
|
|
out_flush();
|
|
(void)vpeekc_nomap();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Similar to requesting the version string: Request the terminal background
|
|
* color when it is the right moment.
|
|
*/
|
|
void
|
|
may_req_bg_color(void)
|
|
{
|
|
if (can_get_termresponse() && starting == 0)
|
|
{
|
|
int didit = FALSE;
|
|
|
|
# ifdef FEAT_TERMINAL
|
|
// Only request foreground if t_RF is set.
|
|
if (rfg_status.tr_progress == STATUS_GET && *T_RFG != NUL)
|
|
{
|
|
MAY_WANT_TO_LOG_THIS;
|
|
LOG_TR(("Sending FG request"));
|
|
out_str(T_RFG);
|
|
termrequest_sent(&rfg_status);
|
|
didit = TRUE;
|
|
}
|
|
# endif
|
|
|
|
// Only request background if t_RB is set.
|
|
if (rbg_status.tr_progress == STATUS_GET && *T_RBG != NUL)
|
|
{
|
|
MAY_WANT_TO_LOG_THIS;
|
|
LOG_TR(("Sending BG request"));
|
|
out_str(T_RBG);
|
|
termrequest_sent(&rbg_status);
|
|
didit = TRUE;
|
|
}
|
|
|
|
if (didit)
|
|
{
|
|
// check for the characters now, otherwise they might be eaten by
|
|
// get_keystroke()
|
|
out_flush();
|
|
(void)vpeekc_nomap();
|
|
}
|
|
}
|
|
}
|
|
|
|
# ifdef DEBUG_TERMRESPONSE
|
|
static void
|
|
log_tr(const char *fmt, ...)
|
|
{
|
|
static FILE *fd_tr = NULL;
|
|
static proftime_T start;
|
|
proftime_T now;
|
|
va_list ap;
|
|
|
|
if (fd_tr == NULL)
|
|
{
|
|
fd_tr = fopen("termresponse.log", "w");
|
|
profile_start(&start);
|
|
}
|
|
now = start;
|
|
profile_end(&now);
|
|
fprintf(fd_tr, "%s: %s ", profile_msg(&now),
|
|
must_redraw == UPD_NOT_VALID ? "NV"
|
|
: must_redraw == UPD_CLEAR ? "CL" : " ");
|
|
va_start(ap, fmt);
|
|
vfprintf(fd_tr, fmt, ap);
|
|
va_end(ap);
|
|
fputc('\n', fd_tr);
|
|
fflush(fd_tr);
|
|
}
|
|
# endif
|
|
#endif
|
|
|
|
/*
|
|
* Return TRUE when saving and restoring the screen.
|
|
*/
|
|
int
|
|
swapping_screen(void)
|
|
{
|
|
return (full_screen && *T_TI != NUL);
|
|
}
|
|
|
|
/*
|
|
* By outputting the 'cursor very visible' termcap code, for some windowed
|
|
* terminals this makes the screen scrolled to the correct position.
|
|
* Used when starting Vim or returning from a shell.
|
|
*/
|
|
void
|
|
scroll_start(void)
|
|
{
|
|
if (*T_VS == NUL || *T_CVS == NUL)
|
|
return;
|
|
|
|
MAY_WANT_TO_LOG_THIS;
|
|
out_str(T_VS);
|
|
out_str(T_CVS);
|
|
screen_start(); // don't know where cursor is now
|
|
}
|
|
|
|
// True if cursor is not visible
|
|
static int cursor_is_off = FALSE;
|
|
|
|
// True if cursor is not visible due to an ongoing cursor-less sleep
|
|
static int cursor_is_asleep = FALSE;
|
|
|
|
/*
|
|
* Enable the cursor without checking if it's already enabled.
|
|
*/
|
|
void
|
|
cursor_on_force(void)
|
|
{
|
|
out_str(T_VE);
|
|
cursor_is_off = FALSE;
|
|
cursor_is_asleep = FALSE;
|
|
}
|
|
|
|
/*
|
|
* Enable the cursor if it's currently off.
|
|
*/
|
|
void
|
|
cursor_on(void)
|
|
{
|
|
if (cursor_is_off && !cursor_is_asleep)
|
|
cursor_on_force();
|
|
}
|
|
|
|
/*
|
|
* Disable the cursor.
|
|
*/
|
|
void
|
|
cursor_off(void)
|
|
{
|
|
if (full_screen && !cursor_is_off)
|
|
{
|
|
out_str(T_VI); // disable cursor
|
|
cursor_is_off = TRUE;
|
|
}
|
|
}
|
|
|
|
#ifdef FEAT_GUI
|
|
/*
|
|
* Check whether the cursor is invisible due to an ongoing cursor-less sleep
|
|
*/
|
|
int
|
|
cursor_is_sleeping(void)
|
|
{
|
|
return cursor_is_asleep;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Disable the cursor and mark it disabled by cursor-less sleep
|
|
*/
|
|
void
|
|
cursor_sleep(void)
|
|
{
|
|
cursor_is_asleep = TRUE;
|
|
cursor_off();
|
|
}
|
|
|
|
/*
|
|
* Enable the cursor and mark it not disabled by cursor-less sleep
|
|
*/
|
|
void
|
|
cursor_unsleep(void)
|
|
{
|
|
cursor_is_asleep = FALSE;
|
|
cursor_on();
|
|
}
|
|
|
|
#if defined(CURSOR_SHAPE) || defined(PROTO)
|
|
/*
|
|
* Set cursor shape to match Insert or Replace mode.
|
|
*/
|
|
void
|
|
term_cursor_mode(int forced)
|
|
{
|
|
static int showing_mode = -1;
|
|
char_u *p;
|
|
|
|
// Only do something when redrawing the screen and we can restore the
|
|
// mode.
|
|
if (!full_screen || *T_CEI == NUL)
|
|
{
|
|
# ifdef FEAT_TERMRESPONSE
|
|
if (forced && initial_cursor_shape > 0)
|
|
// Restore to initial values.
|
|
term_cursor_shape(initial_cursor_shape, initial_cursor_blink);
|
|
# endif
|
|
return;
|
|
}
|
|
|
|
if ((State & MODE_REPLACE) == MODE_REPLACE)
|
|
{
|
|
if (forced || showing_mode != MODE_REPLACE)
|
|
{
|
|
if (*T_CSR != NUL)
|
|
p = T_CSR; // Replace mode cursor
|
|
else
|
|
p = T_CSI; // fall back to Insert mode cursor
|
|
if (*p != NUL)
|
|
{
|
|
out_str(p);
|
|
showing_mode = MODE_REPLACE;
|
|
}
|
|
}
|
|
}
|
|
else if (State & MODE_INSERT)
|
|
{
|
|
if ((forced || showing_mode != MODE_INSERT) && *T_CSI != NUL)
|
|
{
|
|
out_str(T_CSI); // Insert mode cursor
|
|
showing_mode = MODE_INSERT;
|
|
}
|
|
}
|
|
else if (forced || showing_mode != MODE_NORMAL)
|
|
{
|
|
out_str(T_CEI); // non-Insert mode cursor
|
|
showing_mode = MODE_NORMAL;
|
|
}
|
|
}
|
|
|
|
# if defined(FEAT_TERMINAL) || defined(PROTO)
|
|
void
|
|
term_cursor_color(char_u *color)
|
|
{
|
|
if (*T_CSC == NUL)
|
|
return;
|
|
|
|
out_str(T_CSC); // set cursor color start
|
|
out_str_nf(color);
|
|
out_str(T_CEC); // set cursor color end
|
|
out_flush();
|
|
}
|
|
# endif
|
|
|
|
int
|
|
blink_state_is_inverted(void)
|
|
{
|
|
#ifdef FEAT_TERMRESPONSE
|
|
return rbm_status.tr_progress == STATUS_GOT
|
|
&& rcs_status.tr_progress == STATUS_GOT
|
|
&& initial_cursor_blink != initial_cursor_shape_blink;
|
|
#else
|
|
return FALSE;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* "shape": 1 = block, 2 = underline, 3 = vertical bar
|
|
*/
|
|
void
|
|
term_cursor_shape(int shape, int blink)
|
|
{
|
|
if (*T_CSH != NUL)
|
|
{
|
|
OUT_STR(tgoto((char *)T_CSH, 0, shape * 2 - blink));
|
|
out_flush();
|
|
}
|
|
else
|
|
{
|
|
int do_blink = blink;
|
|
|
|
// t_SH is empty: try setting just the blink state.
|
|
// The blink flags are XORed together, if the initial blinking from
|
|
// style and shape differs, we need to invert the flag here.
|
|
if (blink_state_is_inverted())
|
|
do_blink = !blink;
|
|
|
|
if (do_blink && *T_VS != NUL)
|
|
{
|
|
out_str(T_VS);
|
|
out_flush();
|
|
}
|
|
else if (!do_blink && *T_CVS != NUL)
|
|
{
|
|
out_str(T_CVS);
|
|
out_flush();
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Set scrolling region for window 'wp'.
|
|
* The region starts 'off' lines from the start of the window.
|
|
* Also set the vertical scroll region for a vertically split window. Always
|
|
* the full width of the window, excluding the vertical separator.
|
|
*/
|
|
void
|
|
scroll_region_set(win_T *wp, int off)
|
|
{
|
|
OUT_STR(tgoto((char *)T_CS, W_WINROW(wp) + wp->w_height - 1,
|
|
W_WINROW(wp) + off));
|
|
if (*T_CSV != NUL && wp->w_width != Columns)
|
|
OUT_STR(tgoto((char *)T_CSV, wp->w_wincol + wp->w_width - 1,
|
|
wp->w_wincol));
|
|
screen_start(); // don't know where cursor is now
|
|
}
|
|
|
|
/*
|
|
* Reset scrolling region to the whole screen.
|
|
*/
|
|
void
|
|
scroll_region_reset(void)
|
|
{
|
|
OUT_STR(tgoto((char *)T_CS, (int)Rows - 1, 0));
|
|
if (*T_CSV != NUL)
|
|
OUT_STR(tgoto((char *)T_CSV, (int)Columns - 1, 0));
|
|
screen_start(); // don't know where cursor is now
|
|
}
|
|
|
|
|
|
/*
|
|
* List of terminal codes that are currently recognized.
|
|
*/
|
|
|
|
static struct termcode
|
|
{
|
|
char_u name[2]; // termcap name of entry
|
|
char_u *code; // terminal code (in allocated memory)
|
|
int len; // STRLEN(code)
|
|
int modlen; // length of part before ";*~".
|
|
} *termcodes = NULL;
|
|
|
|
static int tc_max_len = 0; // number of entries that termcodes[] can hold
|
|
static int tc_len = 0; // current number of entries in termcodes[]
|
|
|
|
static int termcode_star(char_u *code, int len);
|
|
|
|
void
|
|
clear_termcodes(void)
|
|
{
|
|
while (tc_len > 0)
|
|
vim_free(termcodes[--tc_len].code);
|
|
VIM_CLEAR(termcodes);
|
|
tc_max_len = 0;
|
|
|
|
#ifdef HAVE_TGETENT
|
|
BC = (char *)empty_option;
|
|
UP = (char *)empty_option;
|
|
PC = NUL; // set pad character to NUL
|
|
ospeed = 0;
|
|
#endif
|
|
|
|
need_gather = TRUE; // need to fill termleader[]
|
|
}
|
|
|
|
#define ATC_FROM_TERM 55
|
|
|
|
/*
|
|
* For xterm we recognize special codes like "ESC[42;*X" and "ESC O*X" that
|
|
* accept modifiers.
|
|
* Set "termcodes[idx].modlen".
|
|
*/
|
|
static void
|
|
adjust_modlen(int idx)
|
|
{
|
|
termcodes[idx].modlen = 0;
|
|
int j = termcode_star(termcodes[idx].code, termcodes[idx].len);
|
|
if (j <= 0)
|
|
return;
|
|
|
|
termcodes[idx].modlen = termcodes[idx].len - 1 - j;
|
|
// For "CSI[@;X" the "@" is not included in "modlen".
|
|
if (termcodes[idx].code[termcodes[idx].modlen - 1] == '@')
|
|
--termcodes[idx].modlen;
|
|
}
|
|
|
|
/*
|
|
* Add a new entry for "name[2]" to the list of terminal codes.
|
|
* Note that "name" may not have a terminating NUL.
|
|
* The list is kept alphabetical for ":set termcap"
|
|
* "flags" is TRUE when replacing 7-bit by 8-bit controls is desired.
|
|
* "flags" can also be ATC_FROM_TERM for got_code_from_term().
|
|
*/
|
|
void
|
|
add_termcode(char_u *name, char_u *string, int flags)
|
|
{
|
|
struct termcode *new_tc;
|
|
int i, j;
|
|
char_u *s;
|
|
int len;
|
|
#ifdef FEAT_EVAL
|
|
char *action = "Setting";
|
|
#endif
|
|
|
|
if (string == NULL || *string == NUL)
|
|
{
|
|
del_termcode(name);
|
|
return;
|
|
}
|
|
|
|
#if defined(MSWIN) && !defined(FEAT_GUI)
|
|
s = vim_strnsave(string, STRLEN(string) + 1);
|
|
#else
|
|
# ifdef VIMDLL
|
|
if (!gui.in_use)
|
|
s = vim_strnsave(string, STRLEN(string) + 1);
|
|
else
|
|
# endif
|
|
s = vim_strsave(string);
|
|
#endif
|
|
if (s == NULL)
|
|
return;
|
|
|
|
// Change leading <Esc>[ to CSI, change <Esc>O to <M-O>.
|
|
if (flags != 0 && flags != ATC_FROM_TERM && term_7to8bit(string) != 0)
|
|
{
|
|
STRMOVE(s, s + 1);
|
|
s[0] = term_7to8bit(string);
|
|
}
|
|
|
|
#if defined(MSWIN) && (!defined(FEAT_GUI) || defined(VIMDLL))
|
|
# ifdef VIMDLL
|
|
if (!gui.in_use)
|
|
# endif
|
|
{
|
|
if (s[0] == K_NUL)
|
|
{
|
|
STRMOVE(s + 1, s);
|
|
s[1] = 3;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
len = (int)STRLEN(s);
|
|
|
|
need_gather = TRUE; // need to fill termleader[]
|
|
|
|
/*
|
|
* need to make space for more entries
|
|
*/
|
|
if (tc_len == tc_max_len)
|
|
{
|
|
tc_max_len += 20;
|
|
new_tc = ALLOC_MULT(struct termcode, tc_max_len);
|
|
if (new_tc == NULL)
|
|
{
|
|
tc_max_len -= 20;
|
|
vim_free(s);
|
|
return;
|
|
}
|
|
for (i = 0; i < tc_len; ++i)
|
|
new_tc[i] = termcodes[i];
|
|
vim_free(termcodes);
|
|
termcodes = new_tc;
|
|
}
|
|
|
|
/*
|
|
* Look for existing entry with the same name, it is replaced.
|
|
* Look for an existing entry that is alphabetical higher, the new entry
|
|
* is inserted in front of it.
|
|
*/
|
|
for (i = 0; i < tc_len; ++i)
|
|
{
|
|
if (termcodes[i].name[0] < name[0])
|
|
continue;
|
|
if (termcodes[i].name[0] == name[0])
|
|
{
|
|
if (termcodes[i].name[1] < name[1])
|
|
continue;
|
|
/*
|
|
* Exact match: May replace old code.
|
|
*/
|
|
if (termcodes[i].name[1] == name[1])
|
|
{
|
|
if (flags == ATC_FROM_TERM && (j = termcode_star(
|
|
termcodes[i].code, termcodes[i].len)) > 0)
|
|
{
|
|
// Don't replace ESC[123;*X or ESC O*X with another when
|
|
// invoked from got_code_from_term().
|
|
if (len == termcodes[i].len - j
|
|
&& STRNCMP(s, termcodes[i].code, len - 1) == 0
|
|
&& s[len - 1]
|
|
== termcodes[i].code[termcodes[i].len - 1])
|
|
{
|
|
// They are equal but for the ";*": don't add it.
|
|
#ifdef FEAT_EVAL
|
|
ch_log(NULL, "Termcap entry %c%c did not change",
|
|
name[0], name[1]);
|
|
#endif
|
|
vim_free(s);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Replace old code.
|
|
#ifdef FEAT_EVAL
|
|
ch_log(NULL, "Termcap entry %c%c was: %s",
|
|
name[0], name[1], termcodes[i].code);
|
|
#endif
|
|
vim_free(termcodes[i].code);
|
|
--tc_len;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* Found alphabetical larger entry, move rest to insert new entry
|
|
*/
|
|
#ifdef FEAT_EVAL
|
|
action = "Adding";
|
|
#endif
|
|
for (j = tc_len; j > i; --j)
|
|
termcodes[j] = termcodes[j - 1];
|
|
break;
|
|
}
|
|
|
|
#ifdef FEAT_EVAL
|
|
ch_log(NULL, "%s termcap entry %c%c to %s", action, name[0], name[1], s);
|
|
#endif
|
|
termcodes[i].name[0] = name[0];
|
|
termcodes[i].name[1] = name[1];
|
|
termcodes[i].code = s;
|
|
termcodes[i].len = len;
|
|
adjust_modlen(i);
|
|
|
|
++tc_len;
|
|
}
|
|
|
|
/*
|
|
* Some function keys may include modifiers, but the terminfo/termcap entries
|
|
* do not indicate that. Insert ";*" where we expect modifiers might appear.
|
|
*/
|
|
static void
|
|
accept_modifiers_for_function_keys(void)
|
|
{
|
|
regmatch_T regmatch;
|
|
CLEAR_FIELD(regmatch);
|
|
regmatch.rm_ic = TRUE;
|
|
regmatch.regprog = vim_regcomp((char_u *)"^\033[\\d\\+\\~$", RE_MAGIC);
|
|
|
|
for (int i = 0; i < tc_len; ++i)
|
|
{
|
|
if (regmatch.regprog == NULL)
|
|
return;
|
|
|
|
// skip PasteStart and PasteEnd
|
|
if (termcodes[i].name[0] == 'P'
|
|
&& (termcodes[i].name[1] == 'S' || termcodes[i].name[1] == 'E'))
|
|
continue;
|
|
|
|
char_u *s = termcodes[i].code;
|
|
if (s != NULL && vim_regexec(®match, s, (colnr_T)0))
|
|
{
|
|
size_t len = STRLEN(s);
|
|
char_u *ns = alloc(len + 3);
|
|
if (ns != NULL)
|
|
{
|
|
mch_memmove(ns, s, len - 1);
|
|
mch_memmove(ns + len - 1, ";*~", 4);
|
|
vim_free(s);
|
|
termcodes[i].code = ns;
|
|
termcodes[i].len += 2;
|
|
adjust_modlen(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
vim_regfree(regmatch.regprog);
|
|
}
|
|
|
|
/*
|
|
* Check termcode "code[len]" for ending in ;*X or *X.
|
|
* The "X" can be any character.
|
|
* Return 0 if not found, 2 for ;*X and 1 for *X.
|
|
*/
|
|
static int
|
|
termcode_star(char_u *code, int len)
|
|
{
|
|
// Shortest is <M-O>*X. With ; shortest is <CSI>@;*X
|
|
if (len >= 3 && code[len - 2] == '*')
|
|
{
|
|
if (len >= 5 && code[len - 3] == ';')
|
|
return 2;
|
|
else
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
char_u *
|
|
find_termcode(char_u *name)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < tc_len; ++i)
|
|
if (termcodes[i].name[0] == name[0] && termcodes[i].name[1] == name[1])
|
|
return termcodes[i].code;
|
|
return NULL;
|
|
}
|
|
|
|
char_u *
|
|
get_termcode(int i)
|
|
{
|
|
if (i >= tc_len)
|
|
return NULL;
|
|
return &termcodes[i].name[0];
|
|
}
|
|
|
|
/*
|
|
* Returns the length of the terminal code at index 'idx'.
|
|
*/
|
|
int
|
|
get_termcode_len(int idx)
|
|
{
|
|
return termcodes[idx].len;
|
|
}
|
|
|
|
void
|
|
del_termcode(char_u *name)
|
|
{
|
|
int i;
|
|
|
|
if (termcodes == NULL) // nothing there yet
|
|
return;
|
|
|
|
need_gather = TRUE; // need to fill termleader[]
|
|
|
|
for (i = 0; i < tc_len; ++i)
|
|
if (termcodes[i].name[0] == name[0] && termcodes[i].name[1] == name[1])
|
|
{
|
|
del_termcode_idx(i);
|
|
return;
|
|
}
|
|
// not found. Give error message?
|
|
}
|
|
|
|
static void
|
|
del_termcode_idx(int idx)
|
|
{
|
|
int i;
|
|
|
|
vim_free(termcodes[idx].code);
|
|
--tc_len;
|
|
for (i = idx; i < tc_len; ++i)
|
|
termcodes[i] = termcodes[i + 1];
|
|
}
|
|
|
|
/*
|
|
* Called when detected that the terminal sends 8-bit codes.
|
|
* Convert all 7-bit codes to their 8-bit equivalent.
|
|
*/
|
|
static void
|
|
switch_to_8bit(void)
|
|
{
|
|
int i;
|
|
int c;
|
|
|
|
// Only need to do something when not already using 8-bit codes.
|
|
if (!term_is_8bit(T_NAME))
|
|
{
|
|
for (i = 0; i < tc_len; ++i)
|
|
{
|
|
c = term_7to8bit(termcodes[i].code);
|
|
if (c != 0)
|
|
{
|
|
STRMOVE(termcodes[i].code + 1, termcodes[i].code + 2);
|
|
termcodes[i].code[0] = c;
|
|
}
|
|
}
|
|
need_gather = TRUE; // need to fill termleader[]
|
|
}
|
|
detected_8bit = TRUE;
|
|
LOG_TR(("Switching to 8 bit"));
|
|
}
|
|
|
|
#ifdef CHECK_DOUBLE_CLICK
|
|
static linenr_T orig_topline = 0;
|
|
# ifdef FEAT_DIFF
|
|
static int orig_topfill = 0;
|
|
# endif
|
|
#endif
|
|
#if defined(CHECK_DOUBLE_CLICK) || defined(PROTO)
|
|
/*
|
|
* Checking for double-clicks ourselves.
|
|
* "orig_topline" is used to avoid detecting a double-click when the window
|
|
* contents scrolled (e.g., when 'scrolloff' is non-zero).
|
|
*/
|
|
/*
|
|
* Set orig_topline. Used when jumping to another window, so that a double
|
|
* click still works.
|
|
*/
|
|
void
|
|
set_mouse_topline(win_T *wp)
|
|
{
|
|
orig_topline = wp->w_topline;
|
|
# ifdef FEAT_DIFF
|
|
orig_topfill = wp->w_topfill;
|
|
# endif
|
|
}
|
|
|
|
/*
|
|
* Returns TRUE if the top line and top fill of window 'wp' matches the saved
|
|
* topline and topfill.
|
|
*/
|
|
int
|
|
is_mouse_topline(win_T *wp)
|
|
{
|
|
return orig_topline == wp->w_topline
|
|
#ifdef FEAT_DIFF
|
|
&& orig_topfill == wp->w_topfill
|
|
#endif
|
|
;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* If "buf" is NULL put "string[new_slen]" in typebuf; "buflen" is not used.
|
|
* If "buf" is not NULL put "string[new_slen]" in "buf[bufsize]" and adjust
|
|
* "buflen".
|
|
* Remove "slen" bytes.
|
|
* Returns FAIL for error.
|
|
*/
|
|
int
|
|
put_string_in_typebuf(
|
|
int offset,
|
|
int slen,
|
|
char_u *string,
|
|
int new_slen,
|
|
char_u *buf,
|
|
int bufsize,
|
|
int *buflen)
|
|
{
|
|
int extra = new_slen - slen;
|
|
|
|
string[new_slen] = NUL;
|
|
if (buf == NULL)
|
|
{
|
|
if (extra < 0)
|
|
// remove matched chars, taking care of noremap
|
|
del_typebuf(-extra, offset);
|
|
else if (extra > 0)
|
|
// insert the extra space we need
|
|
if (ins_typebuf(string + slen, REMAP_YES, offset, FALSE, FALSE)
|
|
== FAIL)
|
|
return FAIL;
|
|
|
|
// Careful: del_typebuf() and ins_typebuf() may have reallocated
|
|
// typebuf.tb_buf[]!
|
|
mch_memmove(typebuf.tb_buf + typebuf.tb_off + offset, string,
|
|
(size_t)new_slen);
|
|
}
|
|
else
|
|
{
|
|
if (extra < 0)
|
|
// remove matched characters
|
|
mch_memmove(buf + offset, buf + offset - extra,
|
|
(size_t)(*buflen + offset + extra));
|
|
else if (extra > 0)
|
|
{
|
|
// Insert the extra space we need. If there is insufficient
|
|
// space return -1.
|
|
if (*buflen + extra + new_slen >= bufsize)
|
|
return FAIL;
|
|
mch_memmove(buf + offset + extra, buf + offset,
|
|
(size_t)(*buflen - offset));
|
|
}
|
|
mch_memmove(buf + offset, string, (size_t)new_slen);
|
|
*buflen = *buflen + extra + new_slen;
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
/*
|
|
* Decode a modifier number as xterm provides it into MOD_MASK bits.
|
|
*/
|
|
int
|
|
decode_modifiers(int n)
|
|
{
|
|
int code = n - 1;
|
|
int modifiers = 0;
|
|
|
|
if (code & 1)
|
|
modifiers |= MOD_MASK_SHIFT;
|
|
if (code & 2)
|
|
modifiers |= MOD_MASK_ALT;
|
|
if (code & 4)
|
|
modifiers |= MOD_MASK_CTRL;
|
|
if (code & 8)
|
|
modifiers |= MOD_MASK_META;
|
|
// Any further modifiers are silently dropped.
|
|
|
|
return modifiers;
|
|
}
|
|
|
|
static int
|
|
modifiers2keycode(int modifiers, int *key, char_u *string)
|
|
{
|
|
int new_slen = 0;
|
|
|
|
if (modifiers == 0)
|
|
return 0;
|
|
|
|
// Some keys have the modifier included. Need to handle that here to
|
|
// make mappings work. This may result in a special key, such as
|
|
// K_S_TAB.
|
|
*key = simplify_key(*key, &modifiers);
|
|
if (modifiers != 0)
|
|
{
|
|
string[new_slen++] = K_SPECIAL;
|
|
string[new_slen++] = (int)KS_MODIFIER;
|
|
string[new_slen++] = modifiers;
|
|
}
|
|
return new_slen;
|
|
}
|
|
|
|
/*
|
|
* Handle a cursor position report.
|
|
*/
|
|
static void
|
|
handle_u7_response(int *arg, char_u *tp UNUSED, int csi_len UNUSED)
|
|
{
|
|
if (arg[0] == 2 && arg[1] >= 2)
|
|
{
|
|
char *aw = NULL;
|
|
|
|
LOG_TR(("Received U7 status: %s", tp));
|
|
u7_status.tr_progress = STATUS_GOT;
|
|
did_cursorhold = TRUE;
|
|
if (arg[1] == 2)
|
|
aw = "single";
|
|
else if (arg[1] == 3)
|
|
aw = "double";
|
|
if (aw != NULL && STRCMP(aw, p_ambw) != 0)
|
|
{
|
|
// Setting the option causes a screen redraw. Do
|
|
// that right away if possible, keeping any
|
|
// messages.
|
|
set_option_value_give_err((char_u *)"ambw", 0L, (char_u *)aw, 0);
|
|
#ifdef DEBUG_TERMRESPONSE
|
|
{
|
|
int r = redraw_asap(UPD_CLEAR);
|
|
|
|
log_tr("set 'ambiwidth', redraw_asap(): %d", r);
|
|
}
|
|
#else
|
|
redraw_asap(UPD_CLEAR);
|
|
#endif
|
|
#ifdef FEAT_EVAL
|
|
set_vim_var_string(VV_TERMU7RESP, tp, csi_len);
|
|
#endif
|
|
apply_autocmds(EVENT_TERMRESPONSEALL,
|
|
(char_u *)"ambiguouswidth", NULL, FALSE, curbuf);
|
|
}
|
|
}
|
|
else if (arg[0] == 3)
|
|
{
|
|
int value;
|
|
|
|
LOG_TR(("Received compatibility test result: %s", tp));
|
|
xcc_status.tr_progress = STATUS_GOT;
|
|
|
|
// Third row: xterm compatibility test.
|
|
// If the cursor is on the first column then the terminal can handle
|
|
// the request for cursor style and blinking.
|
|
value = arg[1] == 1 ? TPR_YES : TPR_NO;
|
|
term_props[TPR_CURSOR_STYLE].tpr_status = value;
|
|
term_props[TPR_CURSOR_BLINK].tpr_status = value;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Handle a response to T_CRV: {lead}{first}{x};{vers};{y}c
|
|
* Xterm and alike use '>' for {first}.
|
|
* Rxvt sends "{lead}?1;2c".
|
|
*/
|
|
static void
|
|
handle_version_response(int first, int *arg, int argc, char_u *tp)
|
|
{
|
|
// The xterm version. It is set to zero when it can't be an actual xterm
|
|
// version.
|
|
int version = arg[1];
|
|
|
|
LOG_TR(("Received CRV response: %s", tp));
|
|
crv_status.tr_progress = STATUS_GOT;
|
|
did_cursorhold = TRUE;
|
|
|
|
// Reset terminal properties that are set based on the termresponse.
|
|
// Mainly useful for tests that send the termresponse multiple times.
|
|
// For testing all props can be reset.
|
|
init_term_props(
|
|
#ifdef FEAT_EVAL
|
|
reset_term_props_on_termresponse
|
|
#else
|
|
FALSE
|
|
#endif
|
|
);
|
|
|
|
// If this code starts with CSI, you can bet that the
|
|
// terminal uses 8-bit codes.
|
|
if (tp[0] == CSI)
|
|
switch_to_8bit();
|
|
|
|
// Screen sends 40500.
|
|
// rxvt sends its version number: "20703" is 2.7.3.
|
|
// Ignore it for when the user has set 'term' to xterm,
|
|
// even though it's an rxvt.
|
|
if (version > 20000)
|
|
version = 0;
|
|
|
|
// Figure out more if the response is CSI > 99 ; 99 ; 99 c
|
|
if (first == '>' && argc == 3)
|
|
{
|
|
// mintty 2.9.5 sends 77;20905;0c.
|
|
// (77 is ASCII 'M' for mintty.)
|
|
if (arg[0] == 77)
|
|
{
|
|
// mintty can do SGR mouse reporting
|
|
term_props[TPR_MOUSE].tpr_status = TPR_MOUSE_SGR;
|
|
}
|
|
|
|
#ifdef FEAT_TERMRESPONSE
|
|
// If xterm version >= 141 try to get termcap codes. For other
|
|
// terminals the request should be ignored.
|
|
if (version >= 141 && p_xtermcodes)
|
|
{
|
|
LOG_TR(("Enable checking for XT codes"));
|
|
check_for_codes = TRUE;
|
|
need_gather = TRUE;
|
|
req_codes_from_term();
|
|
}
|
|
#endif
|
|
|
|
// libvterm sends 0;100;0
|
|
// Konsole sends 0;115;0 and works the same way
|
|
if ((version == 100 || version == 115) && arg[0] == 0 && arg[2] == 0)
|
|
{
|
|
// If run from Vim $COLORS is set to the number of
|
|
// colors the terminal supports. Otherwise assume
|
|
// 256, libvterm supports even more.
|
|
if (mch_getenv((char_u *)"COLORS") == NULL)
|
|
may_adjust_color_count(256);
|
|
// Libvterm can handle SGR mouse reporting.
|
|
term_props[TPR_MOUSE].tpr_status = TPR_MOUSE_SGR;
|
|
}
|
|
|
|
if (version == 95)
|
|
{
|
|
// Mac Terminal.app sends 1;95;0
|
|
if (arg[0] == 1 && arg[2] == 0)
|
|
{
|
|
term_props[TPR_UNDERLINE_RGB].tpr_status = TPR_YES;
|
|
term_props[TPR_MOUSE].tpr_status = TPR_MOUSE_SGR;
|
|
}
|
|
// iTerm2 sends 0;95;0
|
|
else if (arg[0] == 0 && arg[2] == 0)
|
|
{
|
|
// iTerm2 can do SGR mouse reporting
|
|
term_props[TPR_MOUSE].tpr_status = TPR_MOUSE_SGR;
|
|
}
|
|
// old iTerm2 sends 0;95;
|
|
else if (arg[0] == 0 && arg[2] == -1)
|
|
term_props[TPR_UNDERLINE_RGB].tpr_status = TPR_YES;
|
|
}
|
|
|
|
// screen sends 83;40500;0 83 is 'S' in ASCII.
|
|
if (arg[0] == 83)
|
|
{
|
|
// screen supports SGR mouse codes since 4.7.0
|
|
if (arg[1] >= 40700)
|
|
term_props[TPR_MOUSE].tpr_status = TPR_MOUSE_SGR;
|
|
else
|
|
term_props[TPR_MOUSE].tpr_status = TPR_MOUSE_XTERM;
|
|
}
|
|
|
|
// If no recognized terminal has set mouse behavior, assume xterm.
|
|
if (term_props[TPR_MOUSE].tpr_status == TPR_UNKNOWN)
|
|
{
|
|
// Xterm version 277 supports SGR.
|
|
// Xterm version >= 95 supports mouse dragging.
|
|
if (version >= 277)
|
|
term_props[TPR_MOUSE].tpr_status = TPR_MOUSE_SGR;
|
|
else if (version >= 95)
|
|
term_props[TPR_MOUSE].tpr_status = TPR_MOUSE_XTERM2;
|
|
}
|
|
|
|
// Detect terminals that set $TERM to something like
|
|
// "xterm-256color" but are not fully xterm compatible.
|
|
//
|
|
// Gnome terminal sends 1;3801;0, 1;4402;0 or 1;2501;0.
|
|
// Newer Gnome-terminal sends 65;6001;1.
|
|
// xfce4-terminal sends 1;2802;0.
|
|
// screen sends 83;40500;0
|
|
// Assuming any version number over 2500 is not an
|
|
// xterm (without the limit for rxvt and screen).
|
|
if (arg[1] >= 2500)
|
|
term_props[TPR_UNDERLINE_RGB].tpr_status = TPR_YES;
|
|
|
|
else if (version == 136 && arg[2] == 0)
|
|
{
|
|
term_props[TPR_UNDERLINE_RGB].tpr_status = TPR_YES;
|
|
|
|
// PuTTY sends 0;136;0
|
|
if (arg[0] == 0)
|
|
{
|
|
// supports sgr-like mouse reporting.
|
|
term_props[TPR_MOUSE].tpr_status = TPR_MOUSE_SGR;
|
|
}
|
|
// vandyke SecureCRT sends 1;136;0
|
|
}
|
|
|
|
// Konsole sends 0;115;0 - but t_u8 does not actually work, therefore
|
|
// commented out.
|
|
// else if (version == 115 && arg[0] == 0 && arg[2] == 0)
|
|
// term_props[TPR_UNDERLINE_RGB].tpr_status = TPR_YES;
|
|
|
|
// Kitty up to 9.x sends 1;400{version};{secondary-version}
|
|
if (arg[0] == 1 && arg[1] >= 4000 && arg[1] <= 4009)
|
|
{
|
|
term_props[TPR_KITTY].tpr_status = TPR_YES;
|
|
term_props[TPR_KITTY].tpr_set_by_termresponse = TRUE;
|
|
|
|
// Kitty can handle SGR mouse reporting.
|
|
term_props[TPR_MOUSE].tpr_status = TPR_MOUSE_SGR;
|
|
}
|
|
|
|
// GNU screen sends 83;30600;0, 83;40500;0, etc.
|
|
// 30600/40500 is a version number of GNU screen. DA2 support is added
|
|
// on 3.6. DCS string has a special meaning to GNU screen, but xterm
|
|
// compatibility checking does not detect GNU screen.
|
|
if (arg[0] == 83 && arg[1] >= 30600)
|
|
{
|
|
term_props[TPR_CURSOR_STYLE].tpr_status = TPR_NO;
|
|
term_props[TPR_CURSOR_BLINK].tpr_status = TPR_NO;
|
|
}
|
|
|
|
// Xterm first responded to this request at patch level
|
|
// 95, so assume anything below 95 is not xterm and hopefully supports
|
|
// the underline RGB color sequence.
|
|
if (version < 95)
|
|
term_props[TPR_UNDERLINE_RGB].tpr_status = TPR_YES;
|
|
|
|
// Getting the cursor style is only supported properly by xterm since
|
|
// version 279 (otherwise it returns 0x18).
|
|
if (version < 279)
|
|
term_props[TPR_CURSOR_STYLE].tpr_status = TPR_NO;
|
|
|
|
/*
|
|
* Take action on the detected properties.
|
|
*/
|
|
|
|
// Unless the underline RGB color is expected to work, disable "t_8u".
|
|
// It does not work for the real Xterm, it resets the background color.
|
|
// This may cause some flicker. Alternative would be to set "t_8u"
|
|
// here if the terminal is expected to support it, but that might
|
|
// conflict with what was set in the .vimrc.
|
|
if (term_props[TPR_UNDERLINE_RGB].tpr_status != TPR_YES
|
|
&& *T_8U != NUL
|
|
&& !option_was_set((char_u *)"t_8u"))
|
|
{
|
|
set_string_option_direct((char_u *)"t_8u", -1, (char_u *)"",
|
|
OPT_FREE, 0);
|
|
}
|
|
#ifdef FEAT_TERMRESPONSE
|
|
if (*T_8U != NUL && write_t_8u_state == MAYBE)
|
|
// Did skip writing t_8u, a complete redraw is needed.
|
|
redraw_later_clear();
|
|
write_t_8u_state = OK; // can output t_8u now
|
|
#endif
|
|
|
|
// Only set 'ttymouse' automatically if it was not set
|
|
// by the user already.
|
|
if (!option_was_set((char_u *)"ttym")
|
|
&& (term_props[TPR_MOUSE].tpr_status == TPR_MOUSE_XTERM2
|
|
|| term_props[TPR_MOUSE].tpr_status == TPR_MOUSE_SGR))
|
|
{
|
|
set_option_value_give_err((char_u *)"ttym", 0L,
|
|
term_props[TPR_MOUSE].tpr_status == TPR_MOUSE_SGR
|
|
? (char_u *)"sgr" : (char_u *)"xterm2", 0);
|
|
}
|
|
|
|
#ifdef FEAT_TERMRESPONSE
|
|
int need_flush = FALSE;
|
|
|
|
// Only request the cursor style if t_SH and t_RS are
|
|
// set. Only supported properly by xterm since version
|
|
// 279 (otherwise it returns 0x18).
|
|
// Only when getting the cursor style was detected to work.
|
|
// Not for Terminal.app, it can't handle t_RS, it
|
|
// echoes the characters to the screen.
|
|
if (rcs_status.tr_progress == STATUS_GET
|
|
&& term_props[TPR_CURSOR_STYLE].tpr_status == TPR_YES
|
|
&& *T_CSH != NUL
|
|
&& *T_CRS != NUL)
|
|
{
|
|
MAY_WANT_TO_LOG_THIS;
|
|
LOG_TR(("Sending cursor style request"));
|
|
out_str(T_CRS);
|
|
termrequest_sent(&rcs_status);
|
|
need_flush = TRUE;
|
|
}
|
|
|
|
// Only request the cursor blink mode if t_RC set. Not
|
|
// for Gnome terminal, it can't handle t_RC, it
|
|
// echoes the characters to the screen.
|
|
// Only when getting the cursor style was detected to work.
|
|
if (rbm_status.tr_progress == STATUS_GET
|
|
&& term_props[TPR_CURSOR_BLINK].tpr_status == TPR_YES
|
|
&& *T_CRC != NUL)
|
|
{
|
|
MAY_WANT_TO_LOG_THIS;
|
|
LOG_TR(("Sending cursor blink mode request"));
|
|
out_str(T_CRC);
|
|
termrequest_sent(&rbm_status);
|
|
need_flush = TRUE;
|
|
}
|
|
|
|
if (need_flush)
|
|
out_flush();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Add "key" to "buf" and return the number of bytes used.
|
|
* Handles special keys and multi-byte characters.
|
|
*/
|
|
static int
|
|
add_key_to_buf(int key, char_u *buf)
|
|
{
|
|
int idx = 0;
|
|
|
|
if (IS_SPECIAL(key))
|
|
{
|
|
buf[idx++] = K_SPECIAL;
|
|
buf[idx++] = KEY2TERMCAP0(key);
|
|
buf[idx++] = KEY2TERMCAP1(key);
|
|
}
|
|
else if (has_mbyte)
|
|
idx += (*mb_char2bytes)(key, buf + idx);
|
|
else
|
|
buf[idx++] = key;
|
|
return idx;
|
|
}
|
|
|
|
/*
|
|
* Shared between handle_key_with_modifier() and handle_csi_function_key().
|
|
*/
|
|
static int
|
|
put_key_modifiers_in_typebuf(
|
|
int key_arg,
|
|
int modifiers_arg,
|
|
int csi_len,
|
|
int offset,
|
|
char_u *buf,
|
|
int bufsize,
|
|
int *buflen)
|
|
{
|
|
int key = key_arg;
|
|
int modifiers = modifiers_arg;
|
|
|
|
// Some keys need adjustment when the Ctrl modifier is used.
|
|
key = may_adjust_key_for_ctrl(modifiers, key);
|
|
|
|
// May remove the shift modifier if it's already included in the key.
|
|
modifiers = may_remove_shift_modifier(modifiers, key);
|
|
|
|
// Produce modifiers with K_SPECIAL KS_MODIFIER {mod}
|
|
char_u string[MAX_KEY_CODE_LEN + 1];
|
|
int new_slen = modifiers2keycode(modifiers, &key, string);
|
|
|
|
// Add the bytes for the key.
|
|
new_slen += add_key_to_buf(key, string + new_slen);
|
|
|
|
string[new_slen] = NUL;
|
|
if (put_string_in_typebuf(offset, csi_len, string, new_slen,
|
|
buf, bufsize, buflen) == FAIL)
|
|
return -1;
|
|
return new_slen - csi_len + offset;
|
|
}
|
|
|
|
/*
|
|
* Handle a sequence with key and modifier, one of:
|
|
* {lead}27;{modifier};{key}~
|
|
* {lead}{key};{modifier}u
|
|
* Returns the difference in length.
|
|
*/
|
|
static int
|
|
handle_key_with_modifier(
|
|
int *arg,
|
|
int trail,
|
|
int csi_len,
|
|
int offset,
|
|
char_u *buf,
|
|
int bufsize,
|
|
int *buflen)
|
|
{
|
|
// Only set seenModifyOtherKeys for the "{lead}27;" code to avoid setting
|
|
// it for terminals using the kitty keyboard protocol. Xterm sends
|
|
// the form ending in "u" when the formatOtherKeys resource is set. We do
|
|
// not support this.
|
|
//
|
|
// Do not set seenModifyOtherKeys if there was a positive response at any
|
|
// time from requesting the kitty keyboard protocol state, these are not
|
|
// expected to support modifyOtherKeys level 2.
|
|
//
|
|
// Do not set seenModifyOtherKeys for kitty, it does send some sequences
|
|
// like this but does not have the modifyOtherKeys feature.
|
|
if (trail != 'u'
|
|
&& (kitty_protocol_state == KKPS_INITIAL
|
|
|| kitty_protocol_state == KKPS_OFF
|
|
|| kitty_protocol_state == KKPS_AFTER_T_TE)
|
|
&& term_props[TPR_KITTY].tpr_status != TPR_YES)
|
|
{
|
|
#ifdef FEAT_EVAL
|
|
ch_log(NULL, "setting seenModifyOtherKeys to TRUE");
|
|
#endif
|
|
seenModifyOtherKeys = TRUE;
|
|
}
|
|
|
|
int key = trail == 'u' ? arg[0] : arg[2];
|
|
int modifiers = decode_modifiers(arg[1]);
|
|
|
|
// Some terminals do not apply the Shift modifier to the key. To make
|
|
// mappings consistent we do it here. TODO: support more keys.
|
|
if ((modifiers & MOD_MASK_SHIFT) && key >= 'a' && key <= 'z')
|
|
key += 'A' - 'a';
|
|
|
|
// Putting Esc in the buffer creates ambiguity, it can be the start of an
|
|
// escape sequence. Use K_ESC to avoid that.
|
|
if (key == ESC)
|
|
key = K_ESC;
|
|
|
|
return put_key_modifiers_in_typebuf(key, modifiers,
|
|
csi_len, offset, buf, bufsize, buflen);
|
|
}
|
|
|
|
/*
|
|
* Handle a sequence with key without a modifier:
|
|
* {lead}{key}u
|
|
* Returns the difference in length.
|
|
*/
|
|
static int
|
|
handle_key_without_modifier(
|
|
int *arg,
|
|
int csi_len,
|
|
int offset,
|
|
char_u *buf,
|
|
int bufsize,
|
|
int *buflen)
|
|
{
|
|
char_u string[MAX_KEY_CODE_LEN + 1];
|
|
int new_slen;
|
|
|
|
if (arg[0] == ESC)
|
|
{
|
|
// Putting Esc in the buffer creates ambiguity, it can be the start of
|
|
// an escape sequence. Use K_ESC to avoid that.
|
|
string[0] = K_SPECIAL;
|
|
string[1] = KS_EXTRA;
|
|
string[2] = KE_ESC;
|
|
new_slen = 3;
|
|
}
|
|
else
|
|
new_slen = add_key_to_buf(arg[0], string);
|
|
|
|
if (put_string_in_typebuf(offset, csi_len, string, new_slen,
|
|
buf, bufsize, buflen) == FAIL)
|
|
return -1;
|
|
return new_slen - csi_len + offset;
|
|
}
|
|
|
|
/*
|
|
* CSI function key without or with modifiers:
|
|
* {lead}[ABCDEFHPQRS]
|
|
* {lead}1;{modifier}[ABCDEFHPQRS]
|
|
* Returns zero when nog recognized, a positive number when recognized.
|
|
*/
|
|
static int
|
|
handle_csi_function_key(
|
|
int argc,
|
|
int *arg,
|
|
int trail,
|
|
int csi_len,
|
|
char_u *key_name,
|
|
int offset,
|
|
char_u *buf,
|
|
int bufsize,
|
|
int *buflen)
|
|
{
|
|
key_name[0] = 'k';
|
|
switch (trail)
|
|
{
|
|
case 'A': key_name[1] = 'u'; break; // K_UP
|
|
case 'B': key_name[1] = 'd'; break; // K_DOWN
|
|
case 'C': key_name[1] = 'r'; break; // K_RIGHT
|
|
case 'D': key_name[1] = 'l'; break; // K_LEFT
|
|
|
|
// case 'E': keypad BEGIN - not supported
|
|
case 'F': key_name[0] = '@'; key_name[1] = '7'; break; // K_END
|
|
case 'H': key_name[1] = 'h'; break; // K_HOME
|
|
|
|
case 'P': key_name[1] = '1'; break; // K_F1
|
|
case 'Q': key_name[1] = '2'; break; // K_F2
|
|
case 'R': key_name[1] = '3'; break; // K_F3
|
|
case 'S': key_name[1] = '4'; break; // K_F4
|
|
|
|
default: return 0; // not recognized
|
|
}
|
|
|
|
int key = TERMCAP2KEY(key_name[0], key_name[1]);
|
|
int modifiers = argc == 2 ? decode_modifiers(arg[1]) : 0;
|
|
put_key_modifiers_in_typebuf(key, modifiers,
|
|
csi_len, offset, buf, bufsize, buflen);
|
|
return csi_len;
|
|
}
|
|
|
|
/*
|
|
* Handle a CSI escape sequence.
|
|
* - Xterm version string.
|
|
*
|
|
* - Response to XTQMODKEYS: "{lead} > 4 ; Pv m".
|
|
*
|
|
* - Cursor position report: {lead}{row};{col}R
|
|
* The final byte must be 'R'. It is used for checking the
|
|
* ambiguous-width character state.
|
|
*
|
|
* - window position reply: {lead}3;{x};{y}t
|
|
*
|
|
* - key with modifiers when modifyOtherKeys is enabled or the Kitty keyboard
|
|
* protocol is used:
|
|
* {lead}27;{modifier};{key}~
|
|
* {lead}{key};{modifier}u
|
|
*
|
|
* - function key with or without modifiers:
|
|
* {lead}[ABCDEFHPQRS]
|
|
* {lead}1;{modifier}[ABCDEFHPQRS]
|
|
*
|
|
* Return 0 for no match, -1 for partial match, > 0 for full match.
|
|
*/
|
|
static int
|
|
handle_csi(
|
|
char_u *tp,
|
|
int len,
|
|
char_u *argp,
|
|
int offset,
|
|
char_u *buf,
|
|
int bufsize,
|
|
int *buflen,
|
|
char_u *key_name,
|
|
int *slen)
|
|
{
|
|
int first = -1; // optional char right after {lead}
|
|
int trail; // char that ends CSI sequence
|
|
int arg[3] = {-1, -1, -1}; // argument numbers
|
|
int argc = 0; // number of arguments
|
|
char_u *ap = argp;
|
|
int csi_len;
|
|
|
|
// Check for non-digit after CSI.
|
|
if (!VIM_ISDIGIT(*ap))
|
|
first = *ap++;
|
|
|
|
if (first >= 'A' && first <= 'Z')
|
|
{
|
|
// If "first" is in [ABCDEFHPQRS] then it is actually the "trail" and
|
|
// no argument follows.
|
|
trail = first;
|
|
first = -1;
|
|
--ap;
|
|
}
|
|
else
|
|
{
|
|
// Find up to three argument numbers.
|
|
for (argc = 0; argc < 3; )
|
|
{
|
|
if (ap >= tp + len)
|
|
return -1;
|
|
if (*ap == ';')
|
|
arg[argc++] = -1; // omitted number
|
|
else if (VIM_ISDIGIT(*ap))
|
|
{
|
|
arg[argc] = 0;
|
|
for (;;)
|
|
{
|
|
if (ap >= tp + len)
|
|
return -1;
|
|
if (!VIM_ISDIGIT(*ap))
|
|
break;
|
|
arg[argc] = arg[argc] * 10 + (*ap - '0');
|
|
++ap;
|
|
}
|
|
++argc;
|
|
}
|
|
if (*ap == ';')
|
|
++ap;
|
|
else
|
|
break;
|
|
}
|
|
|
|
// mrxvt has been reported to have "+" in the version. Assume
|
|
// the escape sequence ends with a letter or one of "{|}~".
|
|
while (ap < tp + len
|
|
&& !(*ap >= '{' && *ap <= '~')
|
|
&& !ASCII_ISALPHA(*ap))
|
|
++ap;
|
|
if (ap >= tp + len)
|
|
return -1;
|
|
trail = *ap;
|
|
}
|
|
|
|
csi_len = (int)(ap - tp) + 1;
|
|
|
|
// Response to XTQMODKEYS: "CSI > 4 ; Pv m" where Pv indicates the
|
|
// modifyOtherKeys level. Drop similar responses.
|
|
if (first == '>' && (argc == 1 || argc == 2) && trail == 'm')
|
|
{
|
|
if (arg[0] == 4 && argc == 2)
|
|
modify_otherkeys_state = arg[1] == 2 ? MOKS_ENABLED : MOKS_OFF;
|
|
|
|
key_name[0] = (int)KS_EXTRA;
|
|
key_name[1] = (int)KE_IGNORE;
|
|
*slen = csi_len;
|
|
}
|
|
|
|
// Function key starting with CSI:
|
|
// {lead}[ABCDEFHPQRS]
|
|
// {lead}1;{modifier}[ABCDEFHPQRS]
|
|
else if (first == -1 && ASCII_ISUPPER(trail)
|
|
&& (argc == 0 || (argc == 2 && arg[0] == 1)))
|
|
{
|
|
int res = handle_csi_function_key(argc, arg, trail,
|
|
csi_len, key_name, offset, buf, bufsize, buflen);
|
|
return res <= 0 ? res : len + res;
|
|
}
|
|
|
|
// Cursor position report: {lead}{row};{col}R
|
|
// Eat it when there are 2 arguments and it ends in 'R'.
|
|
// Also when u7_status is not "sent", it may be from a previous Vim that
|
|
// just exited. But not for <S-F3>, it sends something similar, check for
|
|
// row and column to make sense.
|
|
else if (first == -1 && argc == 2 && trail == 'R')
|
|
{
|
|
handle_u7_response(arg, tp, csi_len);
|
|
|
|
key_name[0] = (int)KS_EXTRA;
|
|
key_name[1] = (int)KE_IGNORE;
|
|
*slen = csi_len;
|
|
}
|
|
|
|
// Version string: Eat it when there is at least one digit and
|
|
// it ends in 'c'
|
|
else if (*T_CRV != NUL && ap > argp + 1 && trail == 'c')
|
|
{
|
|
handle_version_response(first, arg, argc, tp);
|
|
|
|
*slen = csi_len;
|
|
#ifdef FEAT_EVAL
|
|
set_vim_var_string(VV_TERMRESPONSE, tp, *slen);
|
|
#endif
|
|
apply_autocmds(EVENT_TERMRESPONSE,
|
|
NULL, NULL, FALSE, curbuf);
|
|
apply_autocmds(EVENT_TERMRESPONSEALL,
|
|
(char_u *)"version", NULL, FALSE, curbuf);
|
|
key_name[0] = (int)KS_EXTRA;
|
|
key_name[1] = (int)KE_IGNORE;
|
|
}
|
|
|
|
#ifdef FEAT_TERMRESPONSE
|
|
// Check blinking cursor from xterm:
|
|
// {lead}?12;1$y set
|
|
// {lead}?12;2$y not set
|
|
//
|
|
// {lead} can be <Esc>[ or CSI
|
|
else if (rbm_status.tr_progress == STATUS_SENT
|
|
&& first == '?'
|
|
&& ap == argp + 6
|
|
&& arg[0] == 12
|
|
&& ap[-1] == '$'
|
|
&& trail == 'y')
|
|
{
|
|
initial_cursor_blink = (arg[1] == '1');
|
|
rbm_status.tr_progress = STATUS_GOT;
|
|
LOG_TR(("Received cursor blinking mode response: %s", tp));
|
|
key_name[0] = (int)KS_EXTRA;
|
|
key_name[1] = (int)KE_IGNORE;
|
|
*slen = csi_len;
|
|
# ifdef FEAT_EVAL
|
|
set_vim_var_string(VV_TERMBLINKRESP, tp, *slen);
|
|
# endif
|
|
apply_autocmds(EVENT_TERMRESPONSEALL,
|
|
(char_u *)"cursorblink", NULL, FALSE, curbuf);
|
|
}
|
|
#endif
|
|
|
|
// Kitty keyboard protocol status response: CSI ? flags u
|
|
else if (first == '?' && argc == 1 && trail == 'u')
|
|
{
|
|
// The protocol has various "progressive enhancement flags" values, but
|
|
// we only check for zero and non-zero here.
|
|
if (arg[0] == '0')
|
|
{
|
|
kitty_protocol_state = KKPS_OFF;
|
|
}
|
|
else
|
|
{
|
|
kitty_protocol_state = KKPS_ENABLED;
|
|
|
|
// Reset seenModifyOtherKeys just in case some key combination has
|
|
// been seen that set it before we get the status response.
|
|
#ifdef FEAT_EVAL
|
|
ch_log(NULL, "setting seenModifyOtherKeys to FALSE");
|
|
#endif
|
|
seenModifyOtherKeys = FALSE;
|
|
}
|
|
|
|
key_name[0] = (int)KS_EXTRA;
|
|
key_name[1] = (int)KE_IGNORE;
|
|
*slen = csi_len;
|
|
}
|
|
|
|
#ifdef FEAT_TERMRESPONSE
|
|
// Check for a window position response from the terminal:
|
|
// {lead}3;{x};{y}t
|
|
else if (did_request_winpos && argc == 3 && arg[0] == 3
|
|
&& trail == 't')
|
|
{
|
|
winpos_x = arg[1];
|
|
winpos_y = arg[2];
|
|
// got finished code: consume it
|
|
key_name[0] = (int)KS_EXTRA;
|
|
key_name[1] = (int)KE_IGNORE;
|
|
*slen = csi_len;
|
|
|
|
if (--did_request_winpos <= 0)
|
|
winpos_status.tr_progress = STATUS_GOT;
|
|
}
|
|
#endif
|
|
|
|
// Key with modifier:
|
|
// {lead}27;{modifier};{key}~
|
|
// {lead}{key};{modifier}u
|
|
// Even though we only handle four modifiers and the {modifier} value
|
|
// should be 16 or lower, we accept all modifier values to avoid the raw
|
|
// sequence to be passed through.
|
|
else if ((arg[0] == 27 && argc == 3 && trail == '~')
|
|
|| (argc == 2 && trail == 'u'))
|
|
{
|
|
return len + handle_key_with_modifier(arg, trail,
|
|
csi_len, offset, buf, bufsize, buflen);
|
|
}
|
|
|
|
// Key without modifier (Kitty sends this for Esc):
|
|
// {lead}{key}u
|
|
else if (argc == 1 && trail == 'u')
|
|
{
|
|
return len + handle_key_without_modifier(arg,
|
|
csi_len, offset, buf, bufsize, buflen);
|
|
}
|
|
|
|
// else: Unknown CSI sequence. We could drop it, but then the
|
|
// user can't create a map for it.
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Handle an OSC sequence, fore/background color response from the terminal:
|
|
*
|
|
* {lead}{code};rgb:{rrrr}/{gggg}/{bbbb}{tail}
|
|
* or {lead}{code};rgb:{rr}/{gg}/{bb}{tail}
|
|
*
|
|
* {code} is 10 for foreground, 11 for background
|
|
* {lead} can be <Esc>] or OSC
|
|
* {tail} can be '\007', <Esc>\ or STERM.
|
|
*
|
|
* Consume any code that starts with "{lead}11;", it's also
|
|
* possible that "rgba" is following.
|
|
*/
|
|
static int
|
|
handle_osc(char_u *tp, char_u *argp, int len, char_u *key_name, int *slen)
|
|
{
|
|
int i, j;
|
|
|
|
j = 1 + (tp[0] == ESC);
|
|
if (len >= j + 3 && (argp[0] != '1'
|
|
|| (argp[1] != '1' && argp[1] != '0')
|
|
|| argp[2] != ';'))
|
|
i = 0; // no match
|
|
else
|
|
for (i = j; i < len; ++i)
|
|
if (tp[i] == '\007' || (tp[0] == OSC ? tp[i] == STERM
|
|
: (tp[i] == ESC && i + 1 < len && tp[i + 1] == '\\')))
|
|
{
|
|
int is_bg = argp[1] == '1';
|
|
int is_4digit = i - j >= 21 && tp[j + 11] == '/'
|
|
&& tp[j + 16] == '/';
|
|
|
|
if (i - j >= 15 && STRNCMP(tp + j + 3, "rgb:", 4) == 0
|
|
&& (is_4digit
|
|
|| (tp[j + 9] == '/' && tp[j + 12] == '/')))
|
|
{
|
|
char_u *tp_r = tp + j + 7;
|
|
char_u *tp_g = tp + j + (is_4digit ? 12 : 10);
|
|
char_u *tp_b = tp + j + (is_4digit ? 17 : 13);
|
|
#if defined(FEAT_TERMRESPONSE) && defined(FEAT_TERMINAL)
|
|
int rval, gval, bval;
|
|
|
|
rval = hexhex2nr(tp_r);
|
|
gval = hexhex2nr(tp_g);
|
|
bval = hexhex2nr(tp_b);
|
|
#endif
|
|
if (is_bg)
|
|
{
|
|
char *new_bg_val = (3 * '6' < *tp_r + *tp_g +
|
|
*tp_b) ? "light" : "dark";
|
|
|
|
LOG_TR(("Received RBG response: %s", tp));
|
|
#ifdef FEAT_TERMRESPONSE
|
|
rbg_status.tr_progress = STATUS_GOT;
|
|
# ifdef FEAT_TERMINAL
|
|
bg_r = rval;
|
|
bg_g = gval;
|
|
bg_b = bval;
|
|
# endif
|
|
#endif
|
|
if (!option_was_set((char_u *)"bg")
|
|
&& STRCMP(p_bg, new_bg_val) != 0)
|
|
{
|
|
// value differs, apply it
|
|
set_option_value_give_err((char_u *)"bg",
|
|
0L, (char_u *)new_bg_val, 0);
|
|
reset_option_was_set((char_u *)"bg");
|
|
redraw_asap(UPD_CLEAR);
|
|
}
|
|
}
|
|
#if defined(FEAT_TERMRESPONSE) && defined(FEAT_TERMINAL)
|
|
else
|
|
{
|
|
LOG_TR(("Received RFG response: %s", tp));
|
|
rfg_status.tr_progress = STATUS_GOT;
|
|
fg_r = rval;
|
|
fg_g = gval;
|
|
fg_b = bval;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// got finished code: consume it
|
|
key_name[0] = (int)KS_EXTRA;
|
|
key_name[1] = (int)KE_IGNORE;
|
|
*slen = i + 1 + (tp[i] == ESC);
|
|
#ifdef FEAT_EVAL
|
|
set_vim_var_string(is_bg ? VV_TERMRBGRESP
|
|
: VV_TERMRFGRESP, tp, *slen);
|
|
#endif
|
|
apply_autocmds(EVENT_TERMRESPONSEALL,
|
|
is_bg ? (char_u *)"background" : (char_u *)"foreground", NULL, FALSE, curbuf);
|
|
break;
|
|
}
|
|
if (i == len)
|
|
{
|
|
LOG_TR(("not enough characters for RB"));
|
|
return FAIL;
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
/*
|
|
* Check for key code response from xterm:
|
|
* {lead}{flag}+r<hex bytes><{tail}
|
|
*
|
|
* {lead} can be <Esc>P or DCS
|
|
* {flag} can be '0' or '1'
|
|
* {tail} can be Esc>\ or STERM
|
|
*
|
|
* Check for resource response from xterm (and drop it):
|
|
* {lead}{flag}+R<hex bytes>=<value>{tail}
|
|
*
|
|
* Check for cursor shape response from xterm:
|
|
* {lead}1$r<digit> q{tail}
|
|
*
|
|
* {lead} can be <Esc>P or DCS
|
|
* {tail} can be <Esc>\ or STERM
|
|
*
|
|
* Consume any code that starts with "{lead}.+r" or "{lead}.$r".
|
|
*/
|
|
static int
|
|
handle_dcs(char_u *tp, char_u *argp, int len, char_u *key_name, int *slen)
|
|
{
|
|
int i, j;
|
|
|
|
LOG_TR(("Received DCS response: %s", (char*)tp));
|
|
j = 1 + (tp[0] == ESC);
|
|
if (len < j + 3)
|
|
i = len; // need more chars
|
|
else if ((argp[1] != '+' && argp[1] != '$')
|
|
|| (argp[2] != 'r' && argp[2] != 'R'))
|
|
i = 0; // no match
|
|
else if (argp[1] == '+')
|
|
// key code response
|
|
for (i = j; i < len; ++i)
|
|
{
|
|
if ((tp[i] == ESC && i + 1 < len && tp[i + 1] == '\\')
|
|
|| tp[i] == STERM)
|
|
{
|
|
#ifdef FEAT_TERMRESPONSE
|
|
// handle a key code response, drop a resource response
|
|
if (i - j >= 3 && argp[2] == 'r')
|
|
got_code_from_term(tp + j, i);
|
|
#endif
|
|
key_name[0] = (int)KS_EXTRA;
|
|
key_name[1] = (int)KE_IGNORE;
|
|
*slen = i + 1 + (tp[i] == ESC);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Probably the cursor shape response. Make sure that "i"
|
|
// is equal to "len" when there are not sufficient
|
|
// characters.
|
|
for (i = j + 3; i < len; ++i)
|
|
{
|
|
if (i - j == 3 && !SAFE_isdigit(tp[i]))
|
|
break;
|
|
if (i - j == 4 && tp[i] != ' ')
|
|
break;
|
|
if (i - j == 5 && tp[i] != 'q')
|
|
break;
|
|
if (i - j == 6 && tp[i] != ESC && tp[i] != STERM)
|
|
break;
|
|
if ((i - j == 6 && tp[i] == STERM)
|
|
|| (i - j == 7 && tp[i] == '\\'))
|
|
{
|
|
#ifdef FEAT_TERMRESPONSE
|
|
int number = argp[3] - '0';
|
|
|
|
// 0, 1 = block blink, 2 = block
|
|
// 3 = underline blink, 4 = underline
|
|
// 5 = vertical bar blink, 6 = vertical bar
|
|
number = number == 0 ? 1 : number;
|
|
initial_cursor_shape = (number + 1) / 2;
|
|
// The blink flag is actually inverted, compared to
|
|
// the value set with T_SH.
|
|
initial_cursor_shape_blink =
|
|
(number & 1) ? FALSE : TRUE;
|
|
rcs_status.tr_progress = STATUS_GOT;
|
|
#endif
|
|
LOG_TR(("Received cursor shape response: %s", tp));
|
|
|
|
key_name[0] = (int)KS_EXTRA;
|
|
key_name[1] = (int)KE_IGNORE;
|
|
*slen = i + 1;
|
|
#ifdef FEAT_EVAL
|
|
set_vim_var_string(VV_TERMSTYLERESP, tp, *slen);
|
|
#endif
|
|
apply_autocmds(EVENT_TERMRESPONSEALL,
|
|
(char_u *)"cursorshape", NULL, FALSE, curbuf);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (i == len)
|
|
{
|
|
// These codes arrive many together, each code can be
|
|
// truncated at any point.
|
|
LOG_TR(("not enough characters for XT"));
|
|
return FAIL;
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
/*
|
|
* Check if typebuf.tb_buf[] contains a terminal key code.
|
|
* Check from typebuf.tb_buf[typebuf.tb_off] to typebuf.tb_buf[typebuf.tb_off
|
|
* + "max_offset"].
|
|
* Return 0 for no match, -1 for partial match, > 0 for full match.
|
|
* Return KEYLEN_REMOVED when a key code was deleted.
|
|
* With a match, the match is removed, the replacement code is inserted in
|
|
* typebuf.tb_buf[] and the number of characters in typebuf.tb_buf[] is
|
|
* returned.
|
|
* When "buf" is not NULL, buf[bufsize] is used instead of typebuf.tb_buf[].
|
|
* "buflen" is then the length of the string in buf[] and is updated for
|
|
* inserts and deletes.
|
|
*/
|
|
int
|
|
check_termcode(
|
|
int max_offset,
|
|
char_u *buf,
|
|
int bufsize,
|
|
int *buflen)
|
|
{
|
|
char_u *tp;
|
|
char_u *p;
|
|
int slen = 0; // init for GCC
|
|
int modslen;
|
|
int len;
|
|
int retval = 0;
|
|
int offset;
|
|
char_u key_name[2];
|
|
int modifiers;
|
|
char_u *modifiers_start = NULL;
|
|
int key;
|
|
int new_slen; // Length of what will replace the termcode
|
|
char_u string[MAX_KEY_CODE_LEN + 1];
|
|
int i, j;
|
|
int idx = 0;
|
|
int cpo_koffset;
|
|
|
|
cpo_koffset = (vim_strchr(p_cpo, CPO_KOFFSET) != NULL);
|
|
|
|
/*
|
|
* Speed up the checks for terminal codes by gathering all first bytes
|
|
* used in termleader[]. Often this is just a single <Esc>.
|
|
*/
|
|
if (need_gather)
|
|
gather_termleader();
|
|
|
|
/*
|
|
* Check at several positions in typebuf.tb_buf[], to catch something like
|
|
* "x<Up>" that can be mapped. Stop at max_offset, because characters
|
|
* after that cannot be used for mapping, and with @r commands
|
|
* typebuf.tb_buf[] can become very long.
|
|
* This is used often, KEEP IT FAST!
|
|
*/
|
|
for (offset = 0; offset < max_offset; ++offset)
|
|
{
|
|
if (buf == NULL)
|
|
{
|
|
if (offset >= typebuf.tb_len)
|
|
break;
|
|
tp = typebuf.tb_buf + typebuf.tb_off + offset;
|
|
len = typebuf.tb_len - offset; // length of the input
|
|
}
|
|
else
|
|
{
|
|
if (offset >= *buflen)
|
|
break;
|
|
tp = buf + offset;
|
|
len = *buflen - offset;
|
|
}
|
|
|
|
/*
|
|
* Don't check characters after K_SPECIAL, those are already
|
|
* translated terminal chars (avoid translating ~@^Hx).
|
|
*/
|
|
if (*tp == K_SPECIAL)
|
|
{
|
|
offset += 2; // there are always 2 extra characters
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Skip this position if the character does not appear as the first
|
|
* character in term_strings. This speeds up a lot, since most
|
|
* termcodes start with the same character (ESC or CSI).
|
|
*/
|
|
i = *tp;
|
|
for (p = termleader; *p && *p != i; ++p)
|
|
;
|
|
if (*p == NUL)
|
|
continue;
|
|
|
|
/*
|
|
* Skip this position if p_ek is not set and tp[0] is an ESC and we
|
|
* are in Insert mode.
|
|
*/
|
|
if (*tp == ESC && !p_ek && (State & MODE_INSERT))
|
|
continue;
|
|
|
|
tp[len] = NUL;
|
|
key_name[0] = NUL; // no key name found yet
|
|
key_name[1] = NUL; // no key name found yet
|
|
modifiers = 0; // no modifiers yet
|
|
|
|
#ifdef FEAT_GUI
|
|
if (gui.in_use)
|
|
{
|
|
/*
|
|
* GUI special key codes are all of the form [CSI xx].
|
|
*/
|
|
if (*tp == CSI) // Special key from GUI
|
|
{
|
|
if (len < 3)
|
|
return -1; // Shouldn't happen
|
|
slen = 3;
|
|
key_name[0] = tp[1];
|
|
key_name[1] = tp[2];
|
|
}
|
|
}
|
|
else
|
|
#endif // FEAT_GUI
|
|
#ifdef MSWIN
|
|
if (len >= 3 && tp[0] == CSI && tp[1] == KS_EXTRA
|
|
&& (tp[2] == KE_MOUSEUP
|
|
|| tp[2] == KE_MOUSEDOWN
|
|
|| tp[2] == KE_MOUSELEFT
|
|
|| tp[2] == KE_MOUSERIGHT))
|
|
{
|
|
// MS-Windows console sends mouse scroll events encoded:
|
|
// - CSI
|
|
// - KS_EXTRA
|
|
// - {KE_MOUSE[UP|DOWN|LEFT|RIGHT]}
|
|
slen = 3;
|
|
key_name[0] = tp[1];
|
|
key_name[1] = tp[2];
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
int mouse_index_found = -1;
|
|
|
|
for (idx = 0; idx < tc_len; ++idx)
|
|
{
|
|
/*
|
|
* Ignore the entry if we are not at the start of
|
|
* typebuf.tb_buf[]
|
|
* and there are not enough characters to make a match.
|
|
* But only when the 'K' flag is in 'cpoptions'.
|
|
*/
|
|
slen = termcodes[idx].len;
|
|
modifiers_start = NULL;
|
|
if (cpo_koffset && offset && len < slen)
|
|
continue;
|
|
if (STRNCMP(termcodes[idx].code, tp,
|
|
(size_t)(slen > len ? len : slen)) == 0)
|
|
{
|
|
int looks_like_mouse_start = FALSE;
|
|
|
|
if (len < slen) // got a partial sequence
|
|
return -1; // need to get more chars
|
|
|
|
/*
|
|
* When found a keypad key, check if there is another key
|
|
* that matches and use that one. This makes <Home> to be
|
|
* found instead of <kHome> when they produce the same
|
|
* key code.
|
|
*/
|
|
if (termcodes[idx].name[0] == 'K'
|
|
&& VIM_ISDIGIT(termcodes[idx].name[1]))
|
|
{
|
|
for (j = idx + 1; j < tc_len; ++j)
|
|
if (termcodes[j].len == slen &&
|
|
STRNCMP(termcodes[idx].code,
|
|
termcodes[j].code, slen) == 0)
|
|
{
|
|
idx = j;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (slen == 2 && len > 2
|
|
&& termcodes[idx].code[0] == ESC
|
|
&& termcodes[idx].code[1] == '[')
|
|
{
|
|
// The mouse termcode "ESC [" is also the prefix of
|
|
// "ESC [ I" (focus gained) and other keys. Check some
|
|
// more bytes to find out.
|
|
if (!SAFE_isdigit(tp[2]))
|
|
{
|
|
// ESC [ without number following: Only use it when
|
|
// there is no other match.
|
|
looks_like_mouse_start = TRUE;
|
|
}
|
|
else if (termcodes[idx].name[0] == KS_DEC_MOUSE)
|
|
{
|
|
char_u *nr = tp + 2;
|
|
int count = 0;
|
|
|
|
// If a digit is following it could be a key with
|
|
// modifier, e.g., ESC [ 1;2P. Can be confused
|
|
// with DEC_MOUSE, which requires four numbers
|
|
// following. If not then it can't be a DEC_MOUSE
|
|
// code.
|
|
for (;;)
|
|
{
|
|
++count;
|
|
(void)getdigits(&nr);
|
|
if (nr >= tp + len)
|
|
return -1; // partial sequence
|
|
if (*nr != ';')
|
|
break;
|
|
++nr;
|
|
if (nr >= tp + len)
|
|
return -1; // partial sequence
|
|
}
|
|
if (count < 4)
|
|
continue; // no match
|
|
}
|
|
}
|
|
if (looks_like_mouse_start)
|
|
{
|
|
// Only use it when there is no other match.
|
|
if (mouse_index_found < 0)
|
|
mouse_index_found = idx;
|
|
}
|
|
else
|
|
{
|
|
key_name[0] = termcodes[idx].name[0];
|
|
key_name[1] = termcodes[idx].name[1];
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check for code with modifier, like xterm uses:
|
|
* <Esc>[123;*X (modslen == slen - 3)
|
|
* <Esc>[@;*X (matches <Esc>[X and <Esc>[1;9X )
|
|
* Also <Esc>O*X and <M-O>*X (modslen == slen - 2).
|
|
* When there is a modifier the * matches a number.
|
|
* When there is no modifier the ;* or * is omitted.
|
|
*/
|
|
if (termcodes[idx].modlen > 0 && mouse_index_found < 0)
|
|
{
|
|
modslen = termcodes[idx].modlen;
|
|
if (cpo_koffset && offset && len < modslen)
|
|
continue;
|
|
if (STRNCMP(termcodes[idx].code, tp,
|
|
(size_t)(modslen > len ? len : modslen)) == 0)
|
|
{
|
|
int n;
|
|
|
|
if (len <= modslen) // got a partial sequence
|
|
return -1; // need to get more chars
|
|
|
|
if (tp[modslen] == termcodes[idx].code[slen - 1])
|
|
// no modifiers
|
|
slen = modslen + 1;
|
|
else if (tp[modslen] != ';' && modslen == slen - 3)
|
|
// no match for "code;*X" with "code;"
|
|
continue;
|
|
else if (termcodes[idx].code[modslen] == '@'
|
|
&& (tp[modslen] != '1'
|
|
|| tp[modslen + 1] != ';'))
|
|
// no match for "<Esc>[@" with "<Esc>[1;"
|
|
continue;
|
|
else
|
|
{
|
|
// Skip over the digits, the final char must
|
|
// follow. URXVT can use a negative value, thus
|
|
// also accept '-'.
|
|
for (j = slen - 2; j < len && (SAFE_isdigit(tp[j])
|
|
|| tp[j] == '-' || tp[j] == ';'); ++j)
|
|
;
|
|
++j;
|
|
if (len < j) // got a partial sequence
|
|
return -1; // need to get more chars
|
|
if (tp[j - 1] != termcodes[idx].code[slen - 1])
|
|
continue; // no match
|
|
|
|
modifiers_start = tp + slen - 2;
|
|
|
|
// Match! Convert modifier bits.
|
|
n = atoi((char *)modifiers_start);
|
|
modifiers |= decode_modifiers(n);
|
|
|
|
slen = j;
|
|
}
|
|
key_name[0] = termcodes[idx].name[0];
|
|
key_name[1] = termcodes[idx].name[1];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (idx == tc_len && mouse_index_found >= 0)
|
|
{
|
|
key_name[0] = termcodes[mouse_index_found].name[0];
|
|
key_name[1] = termcodes[mouse_index_found].name[1];
|
|
}
|
|
}
|
|
|
|
if (key_name[0] == NUL
|
|
// Mouse codes of DEC and pterm start with <ESC>[. When
|
|
// detecting the start of these mouse codes they might as well be
|
|
// another key code or terminal response.
|
|
#ifdef FEAT_MOUSE_DEC
|
|
|| key_name[0] == KS_DEC_MOUSE
|
|
#endif
|
|
#ifdef FEAT_MOUSE_PTERM
|
|
|| key_name[0] == KS_PTERM_MOUSE
|
|
#endif
|
|
)
|
|
{
|
|
char_u *argp = tp[0] == ESC ? tp + 2 : tp + 1;
|
|
|
|
/*
|
|
* Check for responses from the terminal starting with {lead}:
|
|
* "<Esc>[" or CSI followed by [0-9>?].
|
|
* Also for function keys without a modifier:
|
|
* "<Esc>[" or CSI followed by [ABCDEFHPQRS].
|
|
*
|
|
* - Xterm version string: {lead}>{x};{vers};{y}c
|
|
* Also eat other possible responses to t_RV, rxvt returns
|
|
* "{lead}?1;2c".
|
|
*
|
|
* - Response to XTQMODKEYS: "{lead} > 4 ; Pv m".
|
|
*
|
|
* - Cursor position report: {lead}{row};{col}R
|
|
* The final byte must be 'R'. It is used for checking the
|
|
* ambiguous-width character state.
|
|
*
|
|
* - window position reply: {lead}3;{x};{y}t
|
|
*
|
|
* - key with modifiers when modifyOtherKeys is enabled:
|
|
* {lead}27;{modifier};{key}~
|
|
* {lead}{key};{modifier}u
|
|
*/
|
|
if (((tp[0] == ESC && len >= 3 && tp[1] == '[')
|
|
|| (tp[0] == CSI && len >= 2))
|
|
&& vim_strchr((char_u *)"0123456789>?ABCDEFHPQRS",
|
|
*argp) != NULL)
|
|
{
|
|
int resp = handle_csi(tp, len, argp, offset, buf,
|
|
bufsize, buflen, key_name, &slen);
|
|
if (resp != 0)
|
|
{
|
|
#ifdef DEBUG_TERMRESPONSE
|
|
if (resp == -1)
|
|
LOG_TR(("Not enough characters for CSI sequence"));
|
|
#endif
|
|
return resp;
|
|
}
|
|
}
|
|
|
|
// Check for fore/background color response from the terminal,
|
|
// starting} with <Esc>] or OSC
|
|
else if ((*T_RBG != NUL || *T_RFG != NUL)
|
|
&& ((tp[0] == ESC && len >= 2 && tp[1] == ']')
|
|
|| tp[0] == OSC))
|
|
{
|
|
if (handle_osc(tp, argp, len, key_name, &slen) == FAIL)
|
|
return -1;
|
|
}
|
|
|
|
// Check for key code response from xterm,
|
|
// starting with <Esc>P or DCS
|
|
// It would only be needed with this condition:
|
|
// (check_for_codes || rcs_status.tr_progress == STATUS_SENT)
|
|
// Now this is always done so that DCS codes don't mess up things.
|
|
else if ((tp[0] == ESC && len >= 2 && tp[1] == 'P') || tp[0] == DCS)
|
|
{
|
|
if (handle_dcs(tp, argp, len, key_name, &slen) == FAIL)
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (key_name[0] == NUL)
|
|
continue; // No match at this position, try next one
|
|
|
|
// We only get here when we have a complete termcode match
|
|
|
|
#if defined(FEAT_GUI) || defined(MSWIN)
|
|
/*
|
|
* For scroll events from the GUI or MS-Windows console, fetch the
|
|
* pointer coordinates so that we know which window to scroll later.
|
|
*/
|
|
if (TRUE
|
|
# if defined(FEAT_GUI) && !defined(MSWIN)
|
|
&& gui.in_use
|
|
# endif
|
|
&& key_name[0] == (int)KS_EXTRA
|
|
&& (key_name[1] == (int)KE_X1MOUSE
|
|
|| key_name[1] == (int)KE_X2MOUSE
|
|
|| key_name[1] == (int)KE_MOUSEMOVE_XY
|
|
|| key_name[1] == (int)KE_MOUSELEFT
|
|
|| key_name[1] == (int)KE_MOUSERIGHT
|
|
|| key_name[1] == (int)KE_MOUSEDOWN
|
|
|| key_name[1] == (int)KE_MOUSEUP))
|
|
{
|
|
char_u bytes[6];
|
|
int num_bytes = get_bytes_from_buf(tp + slen, bytes, 4);
|
|
|
|
if (num_bytes == -1) // not enough coordinates
|
|
return -1;
|
|
mouse_col = 128 * (bytes[0] - ' ' - 1) + bytes[1] - ' ' - 1;
|
|
mouse_row = 128 * (bytes[2] - ' ' - 1) + bytes[3] - ' ' - 1;
|
|
slen += num_bytes;
|
|
// equal to K_MOUSEMOVE
|
|
if (key_name[1] == (int)KE_MOUSEMOVE_XY)
|
|
key_name[1] = (int)KE_MOUSEMOVE;
|
|
}
|
|
else
|
|
#endif
|
|
/*
|
|
* If it is a mouse click, get the coordinates.
|
|
*/
|
|
if (key_name[0] == KS_MOUSE
|
|
#ifdef FEAT_MOUSE_GPM
|
|
|| key_name[0] == KS_GPM_MOUSE
|
|
#endif
|
|
#ifdef FEAT_MOUSE_JSB
|
|
|| key_name[0] == KS_JSBTERM_MOUSE
|
|
#endif
|
|
#ifdef FEAT_MOUSE_NET
|
|
|| key_name[0] == KS_NETTERM_MOUSE
|
|
#endif
|
|
#ifdef FEAT_MOUSE_DEC
|
|
|| key_name[0] == KS_DEC_MOUSE
|
|
#endif
|
|
#ifdef FEAT_MOUSE_PTERM
|
|
|| key_name[0] == KS_PTERM_MOUSE
|
|
#endif
|
|
#ifdef FEAT_MOUSE_URXVT
|
|
|| key_name[0] == KS_URXVT_MOUSE
|
|
#endif
|
|
|| key_name[0] == KS_SGR_MOUSE
|
|
|| key_name[0] == KS_SGR_MOUSE_RELEASE)
|
|
{
|
|
if (check_termcode_mouse(tp, &slen, key_name, modifiers_start, idx,
|
|
&modifiers) == -1)
|
|
return -1;
|
|
}
|
|
|
|
#ifdef FEAT_GUI
|
|
/*
|
|
* If using the GUI, then we get menu and scrollbar events.
|
|
*
|
|
* A menu event is encoded as K_SPECIAL, KS_MENU, KE_FILLER followed by
|
|
* four bytes which are to be taken as a pointer to the vimmenu_T
|
|
* structure.
|
|
*
|
|
* A tab line event is encoded as K_SPECIAL KS_TABLINE nr, where "nr"
|
|
* is one byte with the tab index.
|
|
*
|
|
* A scrollbar event is K_SPECIAL, KS_VER_SCROLLBAR, KE_FILLER followed
|
|
* by one byte representing the scrollbar number, and then four bytes
|
|
* representing a long_u which is the new value of the scrollbar.
|
|
*
|
|
* A horizontal scrollbar event is K_SPECIAL, KS_HOR_SCROLLBAR,
|
|
* KE_FILLER followed by four bytes representing a long_u which is the
|
|
* new value of the scrollbar.
|
|
*/
|
|
# ifdef FEAT_MENU
|
|
else if (key_name[0] == (int)KS_MENU)
|
|
{
|
|
long_u val;
|
|
int num_bytes = get_long_from_buf(tp + slen, &val);
|
|
|
|
if (num_bytes == -1)
|
|
return -1;
|
|
current_menu = (vimmenu_T *)val;
|
|
slen += num_bytes;
|
|
|
|
// The menu may have been deleted right after it was used, check
|
|
// for that.
|
|
if (check_menu_pointer(root_menu, current_menu) == FAIL)
|
|
{
|
|
key_name[0] = KS_EXTRA;
|
|
key_name[1] = (int)KE_IGNORE;
|
|
}
|
|
}
|
|
# endif
|
|
# ifdef FEAT_GUI_TABLINE
|
|
else if (key_name[0] == (int)KS_TABLINE)
|
|
{
|
|
// Selecting tabline tab or using its menu.
|
|
char_u bytes[6];
|
|
int num_bytes = get_bytes_from_buf(tp + slen, bytes, 1);
|
|
|
|
if (num_bytes == -1)
|
|
return -1;
|
|
current_tab = (int)bytes[0];
|
|
if (current_tab == 255) // -1 in a byte gives 255
|
|
current_tab = -1;
|
|
slen += num_bytes;
|
|
}
|
|
else if (key_name[0] == (int)KS_TABMENU)
|
|
{
|
|
// Selecting tabline tab or using its menu.
|
|
char_u bytes[6];
|
|
int num_bytes = get_bytes_from_buf(tp + slen, bytes, 2);
|
|
|
|
if (num_bytes == -1)
|
|
return -1;
|
|
current_tab = (int)bytes[0];
|
|
current_tabmenu = (int)bytes[1];
|
|
slen += num_bytes;
|
|
}
|
|
# endif
|
|
# ifndef USE_ON_FLY_SCROLL
|
|
else if (key_name[0] == (int)KS_VER_SCROLLBAR)
|
|
{
|
|
long_u val;
|
|
char_u bytes[6];
|
|
int num_bytes;
|
|
|
|
// Get the last scrollbar event in the queue of the same type
|
|
j = 0;
|
|
for (i = 0; tp[j] == CSI && tp[j + 1] == KS_VER_SCROLLBAR
|
|
&& tp[j + 2] != NUL; ++i)
|
|
{
|
|
j += 3;
|
|
num_bytes = get_bytes_from_buf(tp + j, bytes, 1);
|
|
if (num_bytes == -1)
|
|
break;
|
|
if (i == 0)
|
|
current_scrollbar = (int)bytes[0];
|
|
else if (current_scrollbar != (int)bytes[0])
|
|
break;
|
|
j += num_bytes;
|
|
num_bytes = get_long_from_buf(tp + j, &val);
|
|
if (num_bytes == -1)
|
|
break;
|
|
scrollbar_value = val;
|
|
j += num_bytes;
|
|
slen = j;
|
|
}
|
|
if (i == 0) // not enough characters to make one
|
|
return -1;
|
|
}
|
|
else if (key_name[0] == (int)KS_HOR_SCROLLBAR)
|
|
{
|
|
long_u val;
|
|
int num_bytes;
|
|
|
|
// Get the last horiz. scrollbar event in the queue
|
|
j = 0;
|
|
for (i = 0; tp[j] == CSI && tp[j + 1] == KS_HOR_SCROLLBAR
|
|
&& tp[j + 2] != NUL; ++i)
|
|
{
|
|
j += 3;
|
|
num_bytes = get_long_from_buf(tp + j, &val);
|
|
if (num_bytes == -1)
|
|
break;
|
|
scrollbar_value = val;
|
|
j += num_bytes;
|
|
slen = j;
|
|
}
|
|
if (i == 0) // not enough characters to make one
|
|
return -1;
|
|
}
|
|
# endif // !USE_ON_FLY_SCROLL
|
|
#endif // FEAT_GUI
|
|
|
|
#if defined(UNIX) || defined(VMS)
|
|
/*
|
|
* Handle FocusIn/FocusOut event sequences reported by XTerm.
|
|
* (CSI I/CSI O)
|
|
*/
|
|
if (key_name[0] == KS_EXTRA
|
|
# ifdef FEAT_GUI
|
|
&& !gui.in_use
|
|
# endif
|
|
)
|
|
{
|
|
if (key_name[1] == KE_FOCUSGAINED)
|
|
{
|
|
if (!focus_state)
|
|
{
|
|
ui_focus_change(TRUE);
|
|
did_cursorhold = TRUE;
|
|
focus_state = TRUE;
|
|
}
|
|
key_name[1] = (int)KE_IGNORE;
|
|
}
|
|
else if (key_name[1] == KE_FOCUSLOST)
|
|
{
|
|
if (focus_state)
|
|
{
|
|
ui_focus_change(FALSE);
|
|
did_cursorhold = TRUE;
|
|
focus_state = FALSE;
|
|
}
|
|
key_name[1] = (int)KE_IGNORE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Change <xHome> to <Home>, <xUp> to <Up>, etc.
|
|
*/
|
|
key = handle_x_keys(TERMCAP2KEY(key_name[0], key_name[1]));
|
|
|
|
/*
|
|
* Add any modifier codes to our string.
|
|
*/
|
|
new_slen = modifiers2keycode(modifiers, &key, string);
|
|
|
|
// Finally, add the special key code to our string
|
|
key_name[0] = KEY2TERMCAP0(key);
|
|
key_name[1] = KEY2TERMCAP1(key);
|
|
if (key_name[0] == KS_KEY)
|
|
{
|
|
// from ":set <M-b>=xx"
|
|
if (has_mbyte)
|
|
new_slen += (*mb_char2bytes)(key_name[1], string + new_slen);
|
|
else
|
|
string[new_slen++] = key_name[1];
|
|
}
|
|
else if (new_slen == 0 && key_name[0] == KS_EXTRA
|
|
&& key_name[1] == KE_IGNORE)
|
|
{
|
|
// Do not put K_IGNORE into the buffer, do return KEYLEN_REMOVED
|
|
// to indicate what happened.
|
|
retval = KEYLEN_REMOVED;
|
|
}
|
|
else
|
|
{
|
|
string[new_slen++] = K_SPECIAL;
|
|
string[new_slen++] = key_name[0];
|
|
string[new_slen++] = key_name[1];
|
|
}
|
|
if (put_string_in_typebuf(offset, slen, string, new_slen,
|
|
buf, bufsize, buflen) == FAIL)
|
|
return -1;
|
|
return retval == 0 ? (len + new_slen - slen + offset) : retval;
|
|
}
|
|
|
|
#ifdef FEAT_TERMRESPONSE
|
|
LOG_TR(("normal character"));
|
|
#endif
|
|
|
|
return 0; // no match found
|
|
}
|
|
|
|
#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMRESPONSE)) || defined(PROTO)
|
|
/*
|
|
* Get the text foreground color, if known.
|
|
*/
|
|
void
|
|
term_get_fg_color(char_u *r, char_u *g, char_u *b)
|
|
{
|
|
if (rfg_status.tr_progress != STATUS_GOT)
|
|
return;
|
|
|
|
*r = fg_r;
|
|
*g = fg_g;
|
|
*b = fg_b;
|
|
}
|
|
|
|
/*
|
|
* Get the text background color, if known.
|
|
*/
|
|
void
|
|
term_get_bg_color(char_u *r, char_u *g, char_u *b)
|
|
{
|
|
if (rbg_status.tr_progress != STATUS_GOT)
|
|
return;
|
|
|
|
*r = bg_r;
|
|
*g = bg_g;
|
|
*b = bg_b;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Replace any terminal code strings in from[] with the equivalent internal
|
|
* vim representation. This is used for the "from" and "to" part of a
|
|
* mapping, and the "to" part of a menu command.
|
|
* Any strings like "<C-UP>" are also replaced, unless 'cpoptions' contains
|
|
* '<'.
|
|
* K_SPECIAL by itself is replaced by K_SPECIAL KS_SPECIAL KE_FILLER.
|
|
*
|
|
* The replacement is done in result[] and finally copied into allocated
|
|
* memory. If this all works well *bufp is set to the allocated memory and a
|
|
* pointer to it is returned. If something fails *bufp is set to NULL and from
|
|
* is returned.
|
|
*
|
|
* CTRL-V characters are removed. When "flags" has REPTERM_FROM_PART, a
|
|
* trailing CTRL-V is included, otherwise it is removed (for ":map xx ^V", maps
|
|
* xx to nothing). When 'cpoptions' does not contain 'B', a backslash can be
|
|
* used instead of a CTRL-V.
|
|
*
|
|
* Flags:
|
|
* REPTERM_FROM_PART see above
|
|
* REPTERM_DO_LT also translate <lt>
|
|
* REPTERM_SPECIAL always accept <key> notation
|
|
* REPTERM_NO_SIMPLIFY do not simplify <C-H> to 0x08 and set 8th bit for <A-x>
|
|
*
|
|
* "did_simplify" is set when some <C-H> or <A-x> code was simplified, unless
|
|
* it is NULL.
|
|
*/
|
|
char_u *
|
|
replace_termcodes(
|
|
char_u *from,
|
|
char_u **bufp,
|
|
scid_T sid_arg UNUSED, // script ID to use for <SID>,
|
|
// or 0 to use current_sctx
|
|
int flags,
|
|
int *did_simplify)
|
|
{
|
|
int i;
|
|
int slen;
|
|
int key;
|
|
size_t dlen = 0;
|
|
char_u *src;
|
|
int do_backslash; // backslash is a special character
|
|
int do_special; // recognize <> key codes
|
|
int do_key_code; // recognize raw key codes
|
|
char_u *result; // buffer for resulting string
|
|
garray_T ga;
|
|
|
|
do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL);
|
|
do_special = (vim_strchr(p_cpo, CPO_SPECI) == NULL)
|
|
|| (flags & REPTERM_SPECIAL);
|
|
do_key_code = (vim_strchr(p_cpo, CPO_KEYCODE) == NULL);
|
|
src = from;
|
|
|
|
/*
|
|
* Allocate space for the translation. Worst case a single character is
|
|
* replaced by 6 bytes (shifted special key), plus a NUL at the end.
|
|
* In the rare case more might be needed ga_grow() must be called again.
|
|
*/
|
|
ga_init2(&ga, 1L, 100);
|
|
if (ga_grow(&ga, (int)(STRLEN(src) * 6 + 1)) == FAIL) // out of memory
|
|
{
|
|
*bufp = NULL;
|
|
return from;
|
|
}
|
|
result = ga.ga_data;
|
|
|
|
/*
|
|
* Check for #n at start only: function key n
|
|
*/
|
|
if ((flags & REPTERM_FROM_PART) && src[0] == '#' && VIM_ISDIGIT(src[1]))
|
|
{
|
|
result[dlen++] = K_SPECIAL;
|
|
result[dlen++] = 'k';
|
|
if (src[1] == '0')
|
|
result[dlen++] = ';'; // #0 is F10 is "k;"
|
|
else
|
|
result[dlen++] = src[1]; // #3 is F3 is "k3"
|
|
src += 2;
|
|
}
|
|
|
|
/*
|
|
* Copy each byte from *from to result[dlen]
|
|
*/
|
|
while (*src != NUL)
|
|
{
|
|
/*
|
|
* If 'cpoptions' does not contain '<', check for special key codes,
|
|
* like "<C-S-LeftMouse>"
|
|
*/
|
|
if (do_special && ((flags & REPTERM_DO_LT)
|
|
|| STRNCMP(src, "<lt>", 4) != 0))
|
|
{
|
|
#ifdef FEAT_EVAL
|
|
/*
|
|
* Change <SID>Func to K_SNR <script-nr> _Func. This name is used
|
|
* for script-local user functions.
|
|
* (room: 5 * 6 = 30 bytes; needed: 3 + <nr> + 1 <= 14)
|
|
* Also change <SID>name.Func to K_SNR <import-script-nr> _Func.
|
|
* Only if "name" is recognized as an import.
|
|
*/
|
|
if (STRNICMP(src, "<SID>", 5) == 0)
|
|
{
|
|
if (sid_arg < 0 || (sid_arg == 0 && current_sctx.sc_sid <= 0))
|
|
emsg(_(e_using_sid_not_in_script_context));
|
|
else
|
|
{
|
|
char_u *dot;
|
|
long sid = sid_arg != 0 ? sid_arg : current_sctx.sc_sid;
|
|
|
|
src += 5;
|
|
if (in_vim9script()
|
|
&& (dot = vim_strchr(src, '.')) != NULL)
|
|
{
|
|
imported_T *imp = find_imported(src, dot - src, FALSE);
|
|
|
|
if (imp != NULL)
|
|
{
|
|
scriptitem_T *si = SCRIPT_ITEM(imp->imp_sid);
|
|
size_t len;
|
|
|
|
src = dot + 1;
|
|
if (si->sn_autoload_prefix != NULL)
|
|
{
|
|
// Turn "<SID>name.Func"
|
|
// into "scriptname#Func".
|
|
len = STRLEN(si->sn_autoload_prefix);
|
|
if (ga_grow(&ga,
|
|
(int)(STRLEN(src) * 6 + len + 1)) == FAIL)
|
|
{
|
|
ga_clear(&ga);
|
|
*bufp = NULL;
|
|
return from;
|
|
}
|
|
result = ga.ga_data;
|
|
STRCPY(result + dlen, si->sn_autoload_prefix);
|
|
dlen += len;
|
|
continue;
|
|
}
|
|
sid = imp->imp_sid;
|
|
}
|
|
}
|
|
|
|
result[dlen++] = K_SPECIAL;
|
|
result[dlen++] = (int)KS_EXTRA;
|
|
result[dlen++] = (int)KE_SNR;
|
|
sprintf((char *)result + dlen, "%ld", sid);
|
|
dlen += STRLEN(result + dlen);
|
|
result[dlen++] = '_';
|
|
continue;
|
|
}
|
|
}
|
|
#endif
|
|
int fsk_flags = FSK_KEYCODE
|
|
| ((flags & REPTERM_NO_SIMPLIFY) ? 0 : FSK_SIMPLIFY)
|
|
| ((flags & REPTERM_FROM_PART) ? FSK_FROM_PART : 0);
|
|
slen = trans_special(&src, result + dlen, fsk_flags,
|
|
TRUE, did_simplify);
|
|
if (slen > 0)
|
|
{
|
|
dlen += slen;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If 'cpoptions' does not contain 'k', see if it's an actual key-code.
|
|
* Note that this is also checked after replacing the <> form.
|
|
* Single character codes are NOT replaced (e.g. ^H or DEL), because
|
|
* it could be a character in the file.
|
|
*/
|
|
if (do_key_code)
|
|
{
|
|
i = find_term_bykeys(src);
|
|
if (i >= 0)
|
|
{
|
|
result[dlen++] = K_SPECIAL;
|
|
result[dlen++] = termcodes[i].name[0];
|
|
result[dlen++] = termcodes[i].name[1];
|
|
src += termcodes[i].len;
|
|
// If terminal code matched, continue after it.
|
|
continue;
|
|
}
|
|
}
|
|
|
|
#ifdef FEAT_EVAL
|
|
if (do_special)
|
|
{
|
|
char_u *p, *s, len;
|
|
|
|
/*
|
|
* Replace <Leader> by the value of "mapleader".
|
|
* Replace <LocalLeader> by the value of "maplocalleader".
|
|
* If "mapleader" or "maplocalleader" isn't set use a backslash.
|
|
*/
|
|
if (STRNICMP(src, "<Leader>", 8) == 0)
|
|
{
|
|
len = 8;
|
|
p = get_var_value((char_u *)"g:mapleader");
|
|
}
|
|
else if (STRNICMP(src, "<LocalLeader>", 13) == 0)
|
|
{
|
|
len = 13;
|
|
p = get_var_value((char_u *)"g:maplocalleader");
|
|
}
|
|
else
|
|
{
|
|
len = 0;
|
|
p = NULL;
|
|
}
|
|
if (len != 0)
|
|
{
|
|
// Allow up to 8 * 6 characters for "mapleader".
|
|
if (p == NULL || *p == NUL || STRLEN(p) > 8 * 6)
|
|
s = (char_u *)"\\";
|
|
else
|
|
s = p;
|
|
while (*s != NUL)
|
|
result[dlen++] = *s++;
|
|
src += len;
|
|
continue;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Remove CTRL-V and ignore the next character.
|
|
* For "from" side the CTRL-V at the end is included, for the "to"
|
|
* part it is removed.
|
|
* If 'cpoptions' does not contain 'B', also accept a backslash.
|
|
*/
|
|
key = *src;
|
|
if (key == Ctrl_V || (do_backslash && key == '\\'))
|
|
{
|
|
++src; // skip CTRL-V or backslash
|
|
if (*src == NUL)
|
|
{
|
|
if (flags & REPTERM_FROM_PART)
|
|
result[dlen++] = key;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// skip multibyte char correctly
|
|
for (i = (*mb_ptr2len)(src); i > 0; --i)
|
|
{
|
|
/*
|
|
* If the character is K_SPECIAL, replace it with K_SPECIAL
|
|
* KS_SPECIAL KE_FILLER.
|
|
* If compiled with the GUI replace CSI with K_CSI.
|
|
*/
|
|
if (*src == K_SPECIAL)
|
|
{
|
|
result[dlen++] = K_SPECIAL;
|
|
result[dlen++] = KS_SPECIAL;
|
|
result[dlen++] = KE_FILLER;
|
|
}
|
|
# ifdef FEAT_GUI
|
|
else if (*src == CSI)
|
|
{
|
|
result[dlen++] = K_SPECIAL;
|
|
result[dlen++] = KS_EXTRA;
|
|
result[dlen++] = (int)KE_CSI;
|
|
}
|
|
# endif
|
|
else
|
|
result[dlen++] = *src;
|
|
++src;
|
|
}
|
|
}
|
|
result[dlen] = NUL;
|
|
|
|
/*
|
|
* Copy the new string to allocated memory.
|
|
* If this fails, just return from.
|
|
*/
|
|
if ((*bufp = vim_strsave(result)) != NULL)
|
|
from = *bufp;
|
|
vim_free(result);
|
|
return from;
|
|
}
|
|
|
|
/*
|
|
* Find a termcode with keys 'src' (must be NUL terminated).
|
|
* Return the index in termcodes[], or -1 if not found.
|
|
*/
|
|
static int
|
|
find_term_bykeys(char_u *src)
|
|
{
|
|
int i;
|
|
int slen = (int)STRLEN(src);
|
|
|
|
for (i = 0; i < tc_len; ++i)
|
|
{
|
|
if (slen == termcodes[i].len
|
|
&& STRNCMP(termcodes[i].code, src, (size_t)slen) == 0)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Gather the first characters in the terminal key codes into a string.
|
|
* Used to speed up check_termcode().
|
|
*/
|
|
static void
|
|
gather_termleader(void)
|
|
{
|
|
int i;
|
|
int len = 0;
|
|
|
|
#ifdef FEAT_GUI
|
|
if (gui.in_use)
|
|
termleader[len++] = CSI; // the GUI codes are not in termcodes[]
|
|
#endif
|
|
#ifdef FEAT_TERMRESPONSE
|
|
if (check_for_codes || *T_CRS != NUL)
|
|
termleader[len++] = DCS; // the termcode response starts with DCS
|
|
// in 8-bit mode
|
|
#endif
|
|
termleader[len] = NUL;
|
|
|
|
for (i = 0; i < tc_len; ++i)
|
|
if (vim_strchr(termleader, termcodes[i].code[0]) == NULL)
|
|
{
|
|
termleader[len++] = termcodes[i].code[0];
|
|
termleader[len] = NUL;
|
|
}
|
|
|
|
need_gather = FALSE;
|
|
}
|
|
|
|
/*
|
|
* Show all termcodes (for ":set termcap")
|
|
* This code looks a lot like showoptions(), but is different.
|
|
* "flags" can have OPT_ONECOLUMN.
|
|
*/
|
|
void
|
|
show_termcodes(int flags)
|
|
{
|
|
int col;
|
|
int *items;
|
|
int item_count;
|
|
int run;
|
|
int row, rows;
|
|
int cols;
|
|
int i;
|
|
int len;
|
|
|
|
#define INC3 27 // try to make three columns
|
|
#define INC2 40 // try to make two columns
|
|
#define GAP 2 // spaces between columns
|
|
|
|
if (tc_len == 0) // no terminal codes (must be GUI)
|
|
return;
|
|
items = ALLOC_MULT(int, tc_len);
|
|
if (items == NULL)
|
|
return;
|
|
|
|
// Highlight title
|
|
msg_puts_title(_("\n--- Terminal keys ---"));
|
|
|
|
/*
|
|
* Do the loop three times:
|
|
* 1. display the short items (non-strings and short strings)
|
|
* 2. display the medium items (medium length strings)
|
|
* 3. display the long items (remaining strings)
|
|
* When "flags" has OPT_ONECOLUMN do everything in 3.
|
|
*/
|
|
for (run = (flags & OPT_ONECOLUMN) ? 3 : 1; run <= 3 && !got_int; ++run)
|
|
{
|
|
/*
|
|
* collect the items in items[]
|
|
*/
|
|
item_count = 0;
|
|
for (i = 0; i < tc_len; i++)
|
|
{
|
|
len = show_one_termcode(termcodes[i].name,
|
|
termcodes[i].code, FALSE);
|
|
if ((flags & OPT_ONECOLUMN) ||
|
|
(len <= INC3 - GAP ? run == 1
|
|
: len <= INC2 - GAP ? run == 2
|
|
: run == 3))
|
|
items[item_count++] = i;
|
|
}
|
|
|
|
/*
|
|
* display the items
|
|
*/
|
|
if (run <= 2)
|
|
{
|
|
cols = (Columns + GAP) / (run == 1 ? INC3 : INC2);
|
|
if (cols == 0)
|
|
cols = 1;
|
|
rows = (item_count + cols - 1) / cols;
|
|
}
|
|
else // run == 3
|
|
rows = item_count;
|
|
for (row = 0; row < rows && !got_int; ++row)
|
|
{
|
|
msg_putchar('\n'); // go to next line
|
|
if (got_int) // 'q' typed in more
|
|
break;
|
|
col = 0;
|
|
for (i = row; i < item_count; i += rows)
|
|
{
|
|
msg_col = col; // make columns
|
|
show_one_termcode(termcodes[items[i]].name,
|
|
termcodes[items[i]].code, TRUE);
|
|
if (run == 2)
|
|
col += INC2;
|
|
else
|
|
col += INC3;
|
|
}
|
|
out_flush();
|
|
ui_breakcheck();
|
|
}
|
|
}
|
|
vim_free(items);
|
|
}
|
|
|
|
/*
|
|
* Show one termcode entry.
|
|
* Output goes into IObuff[]
|
|
*/
|
|
int
|
|
show_one_termcode(char_u *name, char_u *code, int printit)
|
|
{
|
|
char_u *p;
|
|
int len;
|
|
|
|
if (name[0] > '~')
|
|
{
|
|
IObuff[0] = ' ';
|
|
IObuff[1] = ' ';
|
|
IObuff[2] = ' ';
|
|
IObuff[3] = ' ';
|
|
}
|
|
else
|
|
{
|
|
IObuff[0] = 't';
|
|
IObuff[1] = '_';
|
|
IObuff[2] = name[0];
|
|
IObuff[3] = name[1];
|
|
}
|
|
IObuff[4] = ' ';
|
|
|
|
p = get_special_key_name(TERMCAP2KEY(name[0], name[1]), 0);
|
|
if (p[1] != 't')
|
|
STRCPY(IObuff + 5, p);
|
|
else
|
|
IObuff[5] = NUL;
|
|
len = (int)STRLEN(IObuff);
|
|
do
|
|
IObuff[len++] = ' ';
|
|
while (len < 17);
|
|
IObuff[len] = NUL;
|
|
if (code == NULL)
|
|
len += 4;
|
|
else
|
|
len += vim_strsize(code);
|
|
|
|
if (printit)
|
|
{
|
|
msg_puts((char *)IObuff);
|
|
if (code == NULL)
|
|
msg_puts("NULL");
|
|
else
|
|
msg_outtrans(code);
|
|
}
|
|
return len;
|
|
}
|
|
|
|
#if defined(FEAT_TERMRESPONSE) || defined(PROTO)
|
|
/*
|
|
* For Xterm >= 140 compiled with OPT_TCAP_QUERY: Obtain the actually used
|
|
* termcap codes from the terminal itself.
|
|
* We get them one by one to avoid a very long response string.
|
|
*/
|
|
static int xt_index_in = 0;
|
|
static int xt_index_out = 0;
|
|
|
|
static void
|
|
req_codes_from_term(void)
|
|
{
|
|
xt_index_out = 0;
|
|
xt_index_in = 0;
|
|
req_more_codes_from_term();
|
|
}
|
|
|
|
static void
|
|
req_more_codes_from_term(void)
|
|
{
|
|
char buf[32]; // extra size to shut up LGTM
|
|
int old_idx = xt_index_out;
|
|
|
|
// Don't do anything when going to exit.
|
|
if (exiting)
|
|
return;
|
|
|
|
// Send up to 10 more requests out than we received. Avoid sending too
|
|
// many, there can be a buffer overflow somewhere.
|
|
while (xt_index_out < xt_index_in + 10 && key_names[xt_index_out] != NULL)
|
|
{
|
|
char *key_name = key_names[xt_index_out];
|
|
|
|
MAY_WANT_TO_LOG_THIS;
|
|
LOG_TR(("Requesting XT %d: %s", xt_index_out, key_name));
|
|
if (key_name[2] != NUL)
|
|
sprintf(buf, "\033P+q%02x%02x%02x\033\\", key_name[0], key_name[1], key_name[2]);
|
|
else
|
|
sprintf(buf, "\033P+q%02x%02x\033\\", key_name[0], key_name[1]);
|
|
out_str_nf((char_u *)buf);
|
|
++xt_index_out;
|
|
}
|
|
|
|
// Send the codes out right away.
|
|
if (xt_index_out != old_idx)
|
|
out_flush();
|
|
}
|
|
|
|
/*
|
|
* Decode key code response from xterm:
|
|
* '<Esc>P1+r<name>=<string><Esc>\' if it is enabled/supported
|
|
* '<Esc>P0+r<Esc>\' if it not enabled
|
|
* A "0" instead of the "1" indicates a code that isn't supported.
|
|
* Both <name> and <string> are encoded in hex.
|
|
* "code" points to the "0" or "1".
|
|
*/
|
|
static void
|
|
got_code_from_term(char_u *code, int len)
|
|
{
|
|
#define XT_LEN 100
|
|
char_u name[4];
|
|
char_u str[XT_LEN];
|
|
int i;
|
|
int j = 0;
|
|
int c;
|
|
|
|
// A '1' means the code is supported, a '0' means it isn't.
|
|
// If it is supported, there must be a '=' following
|
|
// When half the length is > XT_LEN we can't use it.
|
|
if (code[0] == '1' && (code[7] == '=' || code[9] == '=') && len / 2 < XT_LEN)
|
|
{
|
|
// Get the name from the response and find it in the table.
|
|
name[0] = hexhex2nr(code + 3);
|
|
name[1] = hexhex2nr(code + 5);
|
|
if (code[9] == '=')
|
|
name[2] = hexhex2nr(code + 7);
|
|
else
|
|
name[2] = NUL;
|
|
name[3] = NUL;
|
|
for (i = 0; key_names[i] != NULL; ++i)
|
|
{
|
|
if (STRCMP(key_names[i], name) == 0)
|
|
{
|
|
xt_index_in = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
LOG_TR(("Received XT %d: %s", xt_index_in, (char *)name));
|
|
|
|
if (key_names[i] != NULL)
|
|
{
|
|
i = (code[7] == '=') ? 8 : 10;
|
|
for (; (c = hexhex2nr(code + i)) >= 0; i += 2)
|
|
str[j++] = c;
|
|
str[j] = NUL;
|
|
if (name[0] == 'C' && name[1] == 'o')
|
|
{
|
|
// Color count is not a key code.
|
|
int val = atoi((char *)str);
|
|
#if defined(FEAT_EVAL)
|
|
if (val == t_colors)
|
|
ch_log(NULL, "got_code_from_term(Co): no change (%d)", val);
|
|
else
|
|
ch_log(NULL,
|
|
"got_code_from_term(Co): changed from %d to %d",
|
|
t_colors, val);
|
|
#endif
|
|
may_adjust_color_count(val);
|
|
}
|
|
#ifdef FEAT_TERMGUICOLORS
|
|
// when RGB result comes back, it is supported when the result contains an '='
|
|
else if (name[0] == 'R' && name[1] == 'G' && name[2] == 'B' && code[9] == '=')
|
|
{
|
|
int val = atoi((char *)str);
|
|
// only enable it, if termguicolors hasn't been set yet and
|
|
// there are 8 bits per color channel
|
|
if (val == 8 && !p_tgc_set)
|
|
{
|
|
#ifdef FEAT_EVAL
|
|
ch_log(NULL, "got_code_from_term(RGB): xterm-direct colors detected");
|
|
#endif
|
|
// RGB capability set, enable termguicolors
|
|
set_option_value((char_u *)"termguicolors", 1L, NULL, 0);
|
|
}
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
i = find_term_bykeys(str);
|
|
if (i >= 0 && name[0] == termcodes[i].name[0]
|
|
&& name[1] == termcodes[i].name[1])
|
|
{
|
|
// Existing entry with the same name and code - skip.
|
|
#ifdef FEAT_EVAL
|
|
ch_log(NULL, "got_code_from_term(): Entry %c%c did not change",
|
|
name[0], name[1]);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
if (i >= 0)
|
|
{
|
|
// Delete an existing entry using the same code.
|
|
#ifdef FEAT_EVAL
|
|
ch_log(NULL, "got_code_from_term(): Deleting entry %c%c with matching keys %s",
|
|
termcodes[i].name[0], termcodes[i].name[1], str);
|
|
#endif
|
|
del_termcode_idx(i);
|
|
}
|
|
#ifdef FEAT_EVAL
|
|
else
|
|
ch_log(NULL, "got_code_from_term(): Adding entry %c%c with keys %s",
|
|
name[0], name[1], str);
|
|
#endif
|
|
add_termcode(name, str, ATC_FROM_TERM);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// May request more codes now that we received one.
|
|
++xt_index_in;
|
|
req_more_codes_from_term();
|
|
}
|
|
|
|
/*
|
|
* Check if there are any unanswered requests and deal with them.
|
|
* This is called before starting an external program or getting direct
|
|
* keyboard input. We don't want responses to be send to that program or
|
|
* handled as typed text.
|
|
*/
|
|
static void
|
|
check_for_codes_from_term(void)
|
|
{
|
|
int c;
|
|
|
|
// If no codes requested or all are answered, no need to wait.
|
|
if (xt_index_out == 0 || xt_index_out == xt_index_in)
|
|
return;
|
|
|
|
// Vgetc() will check for and handle any response.
|
|
// Keep calling vpeekc() until we don't get any responses.
|
|
++no_mapping;
|
|
++allow_keys;
|
|
for (;;)
|
|
{
|
|
c = vpeekc();
|
|
if (c == NUL) // nothing available
|
|
break;
|
|
|
|
// If a response is recognized it's replaced with K_IGNORE, must read
|
|
// it from the input stream. If there is no K_IGNORE we can't do
|
|
// anything, break here (there might be some responses further on, but
|
|
// we don't want to throw away any typed chars).
|
|
if (c != K_SPECIAL && c != K_IGNORE)
|
|
break;
|
|
c = vgetc();
|
|
if (c != K_IGNORE)
|
|
{
|
|
vungetc(c);
|
|
break;
|
|
}
|
|
}
|
|
--no_mapping;
|
|
--allow_keys;
|
|
}
|
|
#endif
|
|
|
|
#if (defined(MSWIN) && (!defined(FEAT_GUI) || defined(VIMDLL))) || defined(PROTO)
|
|
static char ksme_str[20];
|
|
static char ksmr_str[20];
|
|
static char ksmd_str[20];
|
|
|
|
/*
|
|
* For Win32 console: update termcap codes for existing console attributes.
|
|
*/
|
|
void
|
|
update_tcap(int attr)
|
|
{
|
|
sprintf(ksme_str, "\033|%dm", attr);
|
|
sprintf(ksmd_str, "\033|%dm", attr | 0x08); // FOREGROUND_INTENSITY
|
|
sprintf(ksmr_str, "\033|%dm", ((attr & 0x0F) << 4) | ((attr & 0xF0) >> 4));
|
|
|
|
tcap_entry_T *p = find_builtin_term(DEFAULT_TERM);
|
|
if (p == NULL) // did not find it
|
|
return;
|
|
while (p->bt_string != NULL)
|
|
{
|
|
if (p->bt_entry == (int)KS_ME)
|
|
p->bt_string = &ksme_str[0];
|
|
else if (p->bt_entry == (int)KS_MR)
|
|
p->bt_string = &ksmr_str[0];
|
|
else if (p->bt_entry == (int)KS_MD)
|
|
p->bt_string = &ksmd_str[0];
|
|
++p;
|
|
}
|
|
}
|
|
|
|
# ifdef FEAT_TERMGUICOLORS
|
|
# define KSSIZE 20
|
|
|
|
typedef enum
|
|
{
|
|
CMODE_INDEXED = 0, // Use cmd.exe 4bit palette.
|
|
CMODE_RGB, // Use 24bit RGB colors using VTP.
|
|
CMODE_256COL, // Emulate xterm's 256-color palette using VTP.
|
|
CMODE_LAST,
|
|
} cmode_T;
|
|
|
|
struct ks_tbl_S
|
|
{
|
|
int code; // value of KS_
|
|
char *vtp; // code in RGB mode
|
|
char *vtp2; // code in 256color mode
|
|
char buf[CMODE_LAST][KSSIZE]; // real buffer
|
|
};
|
|
|
|
static struct ks_tbl_S ks_tbl[] =
|
|
{
|
|
{(int)KS_ME, "\033|0m", "\033|0m", {""}}, // normal
|
|
{(int)KS_MR, "\033|7m", "\033|7m", {""}}, // reverse
|
|
{(int)KS_MD, "\033|1m", "\033|1m", {""}}, // bold
|
|
{(int)KS_SO, "\033|91m", "\033|91m", {""}}, // standout: bright red text
|
|
{(int)KS_SE, "\033|39m", "\033|39m", {""}}, // standout end: default color
|
|
{(int)KS_CZH, "\033|3m", "\033|3m", {""}}, // italic
|
|
{(int)KS_CZR, "\033|0m", "\033|0m", {""}}, // italic end
|
|
{(int)KS_US, "\033|4m", "\033|4m", {""}}, // underscore
|
|
{(int)KS_UE, "\033|24m", "\033|24m", {""}}, // underscore end
|
|
# ifdef TERMINFO
|
|
{(int)KS_CAB, "\033|%p1%db", "\033|%p14%dm", {""}}, // set background color
|
|
{(int)KS_CAF, "\033|%p1%df", "\033|%p13%dm", {""}}, // set foreground color
|
|
{(int)KS_CS, "\033|%p1%d;%p2%dR", "\033|%p1%d;%p2%dR", {""}},
|
|
{(int)KS_CSV, "\033|%p1%d;%p2%dV", "\033|%p1%d;%p2%dV", {""}},
|
|
# else
|
|
{(int)KS_CAB, "\033|%db", "\033|4%dm", {""}}, // set background color
|
|
{(int)KS_CAF, "\033|%df", "\033|3%dm", {""}}, // set foreground color
|
|
{(int)KS_CS, "\033|%d;%dR", "\033|%d;%dR", {""}},
|
|
{(int)KS_CSV, "\033|%d;%dV", "\033|%d;%dV", {""}},
|
|
# endif
|
|
{(int)KS_CCO, "256", "256", {""}}, // colors
|
|
{(int)KS_NAME, NULL, NULL, {""}} // terminator
|
|
};
|
|
|
|
/*
|
|
* Find the first entry for "code" in the builtin termcap for "name".
|
|
* Returns NULL when not found.
|
|
*/
|
|
static tcap_entry_T *
|
|
find_first_tcap(
|
|
char_u *name,
|
|
int code)
|
|
{
|
|
tcap_entry_T *p = find_builtin_term(name);
|
|
if (p == NULL)
|
|
return NULL;
|
|
while (p->bt_string != NULL)
|
|
{
|
|
if (p->bt_entry == code)
|
|
return p;
|
|
++p;
|
|
}
|
|
return NULL;
|
|
}
|
|
# endif
|
|
|
|
/*
|
|
* For Win32 console: replace the sequence immediately after termguicolors.
|
|
*/
|
|
void
|
|
swap_tcap(void)
|
|
{
|
|
# ifdef FEAT_TERMGUICOLORS
|
|
static int init_done = FALSE;
|
|
static cmode_T curr_mode;
|
|
struct ks_tbl_S *ks;
|
|
cmode_T mode;
|
|
|
|
if (!init_done)
|
|
{
|
|
for (ks = ks_tbl; ks->code != (int)KS_NAME; ks++)
|
|
{
|
|
tcap_entry_T *bt = find_first_tcap(DEFAULT_TERM, ks->code);
|
|
if (bt != NULL)
|
|
{
|
|
// Preserve the original value.
|
|
STRNCPY(ks->buf[CMODE_INDEXED], bt->bt_string, KSSIZE);
|
|
STRNCPY(ks->buf[CMODE_RGB], ks->vtp, KSSIZE);
|
|
STRNCPY(ks->buf[CMODE_256COL], ks->vtp2, KSSIZE);
|
|
|
|
bt->bt_string = ks->buf[CMODE_INDEXED];
|
|
}
|
|
}
|
|
init_done = TRUE;
|
|
curr_mode = CMODE_INDEXED;
|
|
}
|
|
|
|
if (p_tgc)
|
|
mode = CMODE_RGB;
|
|
else if (t_colors >= 256)
|
|
mode = CMODE_256COL;
|
|
else
|
|
mode = CMODE_INDEXED;
|
|
|
|
if (mode == curr_mode)
|
|
return;
|
|
|
|
for (ks = ks_tbl; ks->code != (int)KS_NAME; ks++)
|
|
{
|
|
tcap_entry_T *bt = find_first_tcap(DEFAULT_TERM, ks->code);
|
|
if (bt != NULL)
|
|
bt->bt_string = ks->buf[mode];
|
|
}
|
|
|
|
curr_mode = mode;
|
|
# endif
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
#if (defined(MSWIN) && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL))) || defined(FEAT_TERMINAL) \
|
|
|| defined(PROTO)
|
|
static int cube_value[] = {
|
|
0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF
|
|
};
|
|
|
|
static int grey_ramp[] = {
|
|
0x08, 0x12, 0x1C, 0x26, 0x30, 0x3A, 0x44, 0x4E, 0x58, 0x62, 0x6C, 0x76,
|
|
0x80, 0x8A, 0x94, 0x9E, 0xA8, 0xB2, 0xBC, 0xC6, 0xD0, 0xDA, 0xE4, 0xEE
|
|
};
|
|
|
|
static const char_u ansi_table[16][3] = {
|
|
// R G B
|
|
{ 0, 0, 0}, // black
|
|
{224, 0, 0}, // dark red
|
|
{ 0, 224, 0}, // dark green
|
|
{224, 224, 0}, // dark yellow / brown
|
|
{ 0, 0, 224}, // dark blue
|
|
{224, 0, 224}, // dark magenta
|
|
{ 0, 224, 224}, // dark cyan
|
|
{224, 224, 224}, // light grey
|
|
|
|
{128, 128, 128}, // dark grey
|
|
{255, 64, 64}, // light red
|
|
{ 64, 255, 64}, // light green
|
|
{255, 255, 64}, // yellow
|
|
{ 64, 64, 255}, // light blue
|
|
{255, 64, 255}, // light magenta
|
|
{ 64, 255, 255}, // light cyan
|
|
{255, 255, 255}, // white
|
|
};
|
|
|
|
#if defined(MSWIN)
|
|
// Mapping between cterm indices < 16 and their counterpart in the ANSI palette.
|
|
static const char_u cterm_ansi_idx[] = {
|
|
0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15
|
|
};
|
|
#endif
|
|
|
|
#define ANSI_INDEX_NONE 0
|
|
|
|
void
|
|
ansi_color2rgb(int nr, char_u *r, char_u *g, char_u *b, char_u *ansi_idx)
|
|
{
|
|
if (nr < 16)
|
|
{
|
|
*r = ansi_table[nr][0];
|
|
*g = ansi_table[nr][1];
|
|
*b = ansi_table[nr][2];
|
|
*ansi_idx = nr + 1;
|
|
}
|
|
else
|
|
{
|
|
*r = 0;
|
|
*g = 0;
|
|
*b = 0;
|
|
*ansi_idx = ANSI_INDEX_NONE;
|
|
}
|
|
}
|
|
|
|
void
|
|
cterm_color2rgb(int nr, char_u *r, char_u *g, char_u *b, char_u *ansi_idx)
|
|
{
|
|
int idx;
|
|
|
|
if (nr < 16)
|
|
{
|
|
#if defined(MSWIN)
|
|
idx = cterm_ansi_idx[nr];
|
|
#else
|
|
idx = nr;
|
|
#endif
|
|
*r = ansi_table[idx][0];
|
|
*g = ansi_table[idx][1];
|
|
*b = ansi_table[idx][2];
|
|
*ansi_idx = idx + 1;
|
|
}
|
|
else if (nr < 232)
|
|
{
|
|
// 216 color cube
|
|
idx = nr - 16;
|
|
*r = cube_value[idx / 36 % 6];
|
|
*g = cube_value[idx / 6 % 6];
|
|
*b = cube_value[idx % 6];
|
|
*ansi_idx = ANSI_INDEX_NONE;
|
|
}
|
|
else if (nr < 256)
|
|
{
|
|
// 24 grey scale ramp
|
|
idx = nr - 232;
|
|
*r = grey_ramp[idx];
|
|
*g = grey_ramp[idx];
|
|
*b = grey_ramp[idx];
|
|
*ansi_idx = ANSI_INDEX_NONE;
|
|
}
|
|
else
|
|
{
|
|
*r = 0;
|
|
*g = 0;
|
|
*b = 0;
|
|
*ansi_idx = ANSI_INDEX_NONE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Replace K_BS by <BS> and K_DEL by <DEL>.
|
|
* Include any modifiers into the key and drop them.
|
|
* Returns "len" adjusted for replaced codes.
|
|
*/
|
|
int
|
|
term_replace_keycodes(char_u *ta_buf, int ta_len, int len_arg)
|
|
{
|
|
int len = len_arg;
|
|
int i;
|
|
int c;
|
|
|
|
for (i = ta_len; i < ta_len + len; ++i)
|
|
{
|
|
if (ta_buf[i] == CSI && len - i > 3 && ta_buf[i + 1] == KS_MODIFIER)
|
|
{
|
|
int modifiers = ta_buf[i + 2];
|
|
int key = ta_buf[i + 3];
|
|
|
|
// Try to use the modifier to modify the key. In any case drop the
|
|
// modifier.
|
|
mch_memmove(ta_buf + i + 1, ta_buf + i + 4, (size_t)(len - i - 3));
|
|
len -= 3;
|
|
if (key < 0x80)
|
|
key = merge_modifyOtherKeys(key, &modifiers);
|
|
ta_buf[i] = key;
|
|
}
|
|
else if (ta_buf[i] == CSI && len - i > 2)
|
|
{
|
|
c = TERMCAP2KEY(ta_buf[i + 1], ta_buf[i + 2]);
|
|
if (c == K_DEL || c == K_KDEL || c == K_BS)
|
|
{
|
|
mch_memmove(ta_buf + i + 1, ta_buf + i + 3,
|
|
(size_t)(len - i - 2));
|
|
if (c == K_DEL || c == K_KDEL)
|
|
ta_buf[i] = DEL;
|
|
else
|
|
ta_buf[i] = Ctrl_H;
|
|
len -= 2;
|
|
}
|
|
}
|
|
else if (ta_buf[i] == '\r')
|
|
ta_buf[i] = '\n';
|
|
if (has_mbyte)
|
|
i += (*mb_ptr2len_len)(ta_buf + i, ta_len + len - i) - 1;
|
|
}
|
|
return len;
|
|
}
|