mirror of
https://github.com/vim/vim
synced 2025-03-16 06:47:52 +01:00
Problem: enabling termguicolors automatically confuses users. Since querying the terminal for the RGB flag happens asynchronously, enabling termguicolors is noticeable by users as the highlighting changes and is therefore unexpected. (after v9.1.1054) Solution: comment out that part for now. We may need another way to enable this in the future. fixes: #16539 fixes: #16568 fixes: #16649 Signed-off-by: Christian Brabandt <cb@256bit.org>
7609 lines
190 KiB
C
7609 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;
|
|
#if 0
|
|
#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
|
|
#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",
|
|
// disabled, because it switches termguicolors, but that
|
|
// is noticable and confuses users
|
|
// "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);
|
|
}
|
|
#if 0
|
|
#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
|
|
#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;
|
|
}
|