patch 9.1.1703: Cannot react to terminal OSC responses
Problem: Cannot react to terminal OSC responses
Solution: Allow TermResponseAll to be triggered by Terminal OSC
responses (Foxe Chen)
fixes: #14995
closes: #17975
Signed-off-by: Foxe Chen <chen.foxe@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
committed by
Christian Brabandt
parent
7f380259cf
commit
1f51bbc3b9
@ -3793,3 +3793,5 @@ EXTERN char e_socket_server_failed_connecting[]
|
||||
EXTERN char e_socket_server_unavailable[]
|
||||
INIT(= N_("E1567: Cannot start socket server, socket path is unavailable"));
|
||||
#endif
|
||||
EXTERN char e_osc_response_timed_out[]
|
||||
INIT(= N_("E1568: OSC command response timed out: %.*s"));
|
||||
|
||||
@ -141,8 +141,8 @@ static struct vimvar
|
||||
{VV_NAME("t_blob", VAR_NUMBER), NULL, VV_RO},
|
||||
{VV_NAME("t_class", VAR_NUMBER), NULL, VV_RO},
|
||||
{VV_NAME("t_object", VAR_NUMBER), NULL, VV_RO},
|
||||
{VV_NAME("termrfgresp", VAR_STRING), NULL, VV_RO},
|
||||
{VV_NAME("termrbgresp", VAR_STRING), NULL, VV_RO},
|
||||
{VV_NAME("termrfgresp", VAR_STRING), NULL, 0},
|
||||
{VV_NAME("termrbgresp", VAR_STRING), NULL, 0},
|
||||
{VV_NAME("termu7resp", VAR_STRING), NULL, VV_RO},
|
||||
{VV_NAME("termstyleresp", VAR_STRING), NULL, VV_RO},
|
||||
{VV_NAME("termblinkresp", VAR_STRING), NULL, VV_RO},
|
||||
@ -166,6 +166,7 @@ static struct vimvar
|
||||
{VV_NAME("wayland_display", VAR_STRING), NULL, VV_RO},
|
||||
{VV_NAME("clipmethod", VAR_STRING), NULL, VV_RO},
|
||||
{VV_NAME("termda1", VAR_STRING), NULL, VV_RO},
|
||||
{VV_NAME("termosc", VAR_STRING), NULL, VV_RO},
|
||||
};
|
||||
|
||||
// shorthand
|
||||
@ -2910,6 +2911,17 @@ set_vim_var_string(
|
||||
vimvars[idx].vv_str = vim_strnsave(val, len);
|
||||
}
|
||||
|
||||
void
|
||||
set_vim_var_string_direct(
|
||||
int idx,
|
||||
char_u *val)
|
||||
{
|
||||
clear_tv(&vimvars[idx].vv_di.di_tv);
|
||||
vimvars[idx].vv_tv_type = VAR_STRING;
|
||||
|
||||
vimvars[idx].vv_str = val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set List v: variable to "val".
|
||||
*/
|
||||
|
||||
@ -82,7 +82,13 @@ static int KeyNoremap = 0; // remapping flags
|
||||
// typebuf.tb_buf has three parts: room in front (for result of mappings), the
|
||||
// middle for typeahead and room for new characters (which needs to be 3 *
|
||||
// MAXMAPLEN for the Amiga).
|
||||
#ifdef FEAT_NORMAL
|
||||
// Add extra space to handle large OSC responses in bigger chunks (improve
|
||||
// performance)
|
||||
#define TYPELEN_INIT (5 * (MAXMAPLEN + 3) + 2048)
|
||||
#else // Tiny version
|
||||
#define TYPELEN_INIT (5 * (MAXMAPLEN + 3))
|
||||
#endif
|
||||
static char_u typebuf_init[TYPELEN_INIT]; // initial typebuf.tb_buf
|
||||
static char_u noremapbuf_init[TYPELEN_INIT]; // initial typebuf.tb_noremap
|
||||
|
||||
|
||||
@ -883,8 +883,6 @@ vim_main2(void)
|
||||
// Requesting the termresponse is postponed until here, so that a "-c q"
|
||||
// argument doesn't make it appear in the shell Vim was started from.
|
||||
may_req_termresponse();
|
||||
|
||||
may_req_bg_color();
|
||||
#endif
|
||||
|
||||
// start in insert mode
|
||||
|
||||
15
src/option.c
15
src/option.c
@ -3905,6 +3905,21 @@ did_set_numberwidth(optset_T *args UNUSED)
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Process the updated 'osctimeoutlen' option value.
|
||||
*/
|
||||
char *
|
||||
did_set_osctimeoutlen(optset_T *args)
|
||||
{
|
||||
if (p_ost < 0)
|
||||
{
|
||||
p_ost = args->os_oldval.number;
|
||||
return e_argument_must_be_positive;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process the updated 'paste' option value. Called after p_paste was set or
|
||||
* reset. When 'paste' is set or reset also change other options.
|
||||
|
||||
@ -828,6 +828,7 @@ EXTERN char_u *p_nf; // 'nrformats'
|
||||
#if defined(MSWIN)
|
||||
EXTERN int p_odev; // 'opendevice'
|
||||
#endif
|
||||
EXTERN long p_ost; // 'osctimeoutlen'
|
||||
EXTERN char_u *p_opfunc; // 'operatorfunc'
|
||||
EXTERN char_u *p_para; // 'paragraphs'
|
||||
EXTERN int p_paste; // 'paste'
|
||||
|
||||
@ -1926,6 +1926,9 @@ static struct vimoption options[] =
|
||||
{"optimize", "opt", P_BOOL|P_VI_DEF,
|
||||
(char_u *)NULL, PV_NONE, NULL, NULL,
|
||||
{(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
|
||||
{"osctimeoutlen", "ost", P_NUM|P_VI_DEF,
|
||||
(char_u *)&p_ost, PV_NONE, did_set_osctimeoutlen, NULL,
|
||||
{(char_u *)1000, (char_u *)0L} SCTX_INIT},
|
||||
{"osfiletype", "oft", P_STRING|P_ALLOCED|P_VI_DEF,
|
||||
(char_u *)NULL, PV_NONE, NULL, NULL,
|
||||
{(char_u *)0L, (char_u *)0L} SCTX_INIT},
|
||||
|
||||
9
src/po/vim.pot
generated
9
src/po/vim.pot
generated
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Vim\n"
|
||||
"Report-Msgid-Bugs-To: vim-dev@vim.org\n"
|
||||
"POT-Creation-Date: 2025-08-23 16:16+0200\n"
|
||||
"POT-Creation-Date: 2025-08-27 19:10+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -8832,6 +8832,10 @@ msgstr ""
|
||||
msgid "E1567: Cannot start socket server, socket path is unavailable"
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "E1568: OSC command response timed out: %.*s"
|
||||
msgstr ""
|
||||
|
||||
#. type of cmdline window or 0
|
||||
#. result of cmdline window or 0
|
||||
#. buffer of cmdline window or NULL
|
||||
@ -9691,6 +9695,9 @@ msgstr ""
|
||||
msgid "restore the screen contents when exiting Vim"
|
||||
msgstr ""
|
||||
|
||||
msgid "timeout used for terminal OSC responses"
|
||||
msgstr ""
|
||||
|
||||
msgid "using the mouse"
|
||||
msgstr ""
|
||||
|
||||
|
||||
@ -51,6 +51,7 @@ void set_vcount(long count, long count1, int set_prevcount);
|
||||
void save_vimvars(vimvars_save_T *vvsave);
|
||||
void restore_vimvars(vimvars_save_T *vvsave);
|
||||
void set_vim_var_string(int idx, char_u *val, int len);
|
||||
void set_vim_var_string_direct(int idx, char_u *val);
|
||||
void set_vim_var_list(int idx, list_T *val);
|
||||
void set_vim_var_dict(int idx, dict_T *val);
|
||||
void set_argv_var(char **argv, int argc);
|
||||
|
||||
@ -58,6 +58,7 @@ char *did_set_modified(optset_T *args);
|
||||
char *did_set_mousehide(optset_T *args);
|
||||
char *did_set_number_relativenumber(optset_T *args);
|
||||
char *did_set_numberwidth(optset_T *args);
|
||||
char *did_set_osctimeoutlen(optset_T *args);
|
||||
char *did_set_paste(optset_T *args);
|
||||
char *did_set_previewwindow(optset_T *args);
|
||||
char *did_set_pyxversion(optset_T *args);
|
||||
|
||||
@ -59,7 +59,6 @@ void starttermcap(void);
|
||||
void stoptermcap(void);
|
||||
void may_req_termresponse(void);
|
||||
void check_terminal_behavior(void);
|
||||
void may_req_bg_color(void);
|
||||
int swapping_screen(void);
|
||||
void scroll_start(void);
|
||||
void cursor_on_force(void);
|
||||
|
||||
@ -20,6 +20,9 @@ void f_timer_pause(typval_T *argvars, typval_T *rettv);
|
||||
void f_timer_start(typval_T *argvars, typval_T *rettv);
|
||||
void f_timer_stop(typval_T *argvars, typval_T *rettv);
|
||||
void f_timer_stopall(typval_T *argvars, typval_T *rettv);
|
||||
#if defined(MSWIN) || defined(__MINGW32__)
|
||||
int gettimeofday(struct timeval *tv, char *dummy);
|
||||
#endif
|
||||
void time_push(void *tv_rel, void *tv_start);
|
||||
void time_pop(void *tp);
|
||||
void time_msg(char *mesg, void *tv_start);
|
||||
|
||||
@ -1290,6 +1290,19 @@ typedef struct
|
||||
#endif
|
||||
} tasave_T;
|
||||
|
||||
// Holds state for current OSC response.
|
||||
typedef struct
|
||||
{
|
||||
int processing; // If we are in the middle of an OSC response
|
||||
char_u start_char; // First char in the OSC response
|
||||
garray_T buf; // Buffer holding the OSC response, to be
|
||||
// placed in the "v:termosc" vim var.
|
||||
|
||||
struct timeval start; // Set at the beginning of an OSC response.
|
||||
// Used to timeout after a set amount of
|
||||
// time.
|
||||
} oscstate_T;
|
||||
|
||||
/*
|
||||
* Used for conversion of terminal I/O and script files.
|
||||
*/
|
||||
|
||||
280
src/term.c
280
src/term.c
@ -116,19 +116,6 @@ static termrequest_T u7_status = TERMREQUEST_INIT;
|
||||
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;
|
||||
@ -143,10 +130,6 @@ 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,
|
||||
@ -4192,49 +4175,6 @@ check_terminal_behavior(void)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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_TR1("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_TR1("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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -5740,105 +5680,89 @@ handle_csi(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static oscstate_T osc_state;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* Handles any OSC sequence and places the result in "v:termosc". Note that the
|
||||
* OSC identifier and terminator character(s) will not be placed in the final
|
||||
* result. Returns OK on success and FAIL on failure.
|
||||
*/
|
||||
static int
|
||||
handle_osc(char_u *tp, char_u *argp, int len, char_u *key_name, int *slen)
|
||||
handle_osc(char_u *tp, int len, char_u *key_name, int *slen)
|
||||
{
|
||||
int i, j;
|
||||
struct timeval now;
|
||||
char_u last_char;
|
||||
|
||||
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_TRN("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_TRN("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)
|
||||
if (!osc_state.processing)
|
||||
{
|
||||
LOG_TR1("not enough characters for RB");
|
||||
int cur;
|
||||
|
||||
LOG_TRN("Received OSC response: %s", (char*)tp);
|
||||
// Check if it is a valid OSC sequence, and consume it. OSC format
|
||||
// consists of:
|
||||
// <idenfitifer><data><terminator>
|
||||
// <identifier> is either <Esc>] or an OSC character
|
||||
// <terminator> can be '\007', <Esc>\ or STERM.
|
||||
cur = 1 + (tp[0] == ESC);
|
||||
|
||||
if (len < cur + 1 + (tp[0] != OSC)) // Include terminator as well
|
||||
return FAIL;
|
||||
|
||||
// The whole OSC response may be larger than the typeahead buffer.
|
||||
// To handle this, keep reading data in and out of the typeahead
|
||||
// buffer until we read an OSC terminator or timeout.
|
||||
ga_init2(&osc_state.buf, 1, 1024);
|
||||
gettimeofday(&osc_state.start, NULL);
|
||||
|
||||
osc_state.processing = TRUE;
|
||||
osc_state.start_char = tp[0];
|
||||
last_char = 0;
|
||||
}
|
||||
else
|
||||
last_char = ((char_u *)osc_state.buf.ga_data)[osc_state.buf.ga_len - 1];
|
||||
|
||||
key_name[0] = (int)KS_EXTRA;
|
||||
key_name[1] = (int)KE_IGNORE;
|
||||
|
||||
// Read data and append to buffer. If we reach a terminator, then
|
||||
// finally set the vim var.
|
||||
for (int i = 0; i < len; i++)
|
||||
if (tp[i] == '\007' || (osc_state.start_char == OSC ? tp[i] == STERM
|
||||
: ((tp[i] == ESC && i + 1 < len && tp[i + 1] == '\\')
|
||||
|| (i == 0 && tp[i] == '\\' && last_char == ESC)
|
||||
)))
|
||||
{
|
||||
osc_state.processing = FALSE;
|
||||
|
||||
ga_concat_len(&osc_state.buf, tp, i + 1 + (tp[i] == ESC));
|
||||
ga_append(&osc_state.buf, NUL);
|
||||
*slen = i + 1 + (tp[i] == ESC);
|
||||
#ifdef FEAT_EVAL
|
||||
set_vim_var_string_direct(VV_TERMOSC, osc_state.buf.ga_data);
|
||||
#endif
|
||||
apply_autocmds(EVENT_TERMRESPONSEALL, (char_u *)"osc",
|
||||
NULL, FALSE, curbuf);
|
||||
return OK;
|
||||
}
|
||||
|
||||
// Check if timeout has been reached
|
||||
gettimeofday(&now, NULL);
|
||||
|
||||
if ((now.tv_sec * 1000000 + now.tv_usec) -
|
||||
(osc_state.start.tv_sec * 1000000 + osc_state.start.tv_usec)
|
||||
>= p_ost * 1000)
|
||||
{
|
||||
semsg(_(e_osc_response_timed_out), osc_state.buf.ga_len,
|
||||
osc_state.buf.ga_data);
|
||||
|
||||
ga_clear(&osc_state.buf);
|
||||
osc_state.processing = FALSE;
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
ga_concat(&osc_state.buf, tp);
|
||||
*slen = len; // Consume everything
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
@ -6028,6 +5952,11 @@ check_termcode(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (osc_state.processing)
|
||||
// Still processing OSC response data, go straight to handler
|
||||
// function.
|
||||
goto handle_osc;
|
||||
|
||||
/*
|
||||
* Skip this position if the character does not appear as the first
|
||||
* character in term_strings. This speeds up a lot, since most
|
||||
@ -6300,13 +6229,11 @@ check_termcode(
|
||||
}
|
||||
}
|
||||
|
||||
// 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))
|
||||
// Check for OSC responses from terminal
|
||||
else if ((tp[0] == ESC && len >= 2 && tp[1] == ']') || tp[0] == OSC)
|
||||
{
|
||||
if (handle_osc(tp, argp, len, key_name, &slen) == FAIL)
|
||||
handle_osc:
|
||||
if (handle_osc(tp, len, key_name, &slen) == FAIL)
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -6549,10 +6476,12 @@ check_termcode(
|
||||
*/
|
||||
key = handle_x_keys(TERMCAP2KEY(key_name[0], key_name[1]));
|
||||
|
||||
/*
|
||||
* Add any modifier codes to our string.
|
||||
*/
|
||||
new_slen = modifiers2keycode(modifiers, &key, string);
|
||||
if (osc_state.processing)
|
||||
// We don't want to add anything to the typeahead buffer.
|
||||
new_slen = 0;
|
||||
else
|
||||
// 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);
|
||||
@ -6592,18 +6521,28 @@ check_termcode(
|
||||
}
|
||||
|
||||
#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMRESPONSE)) || defined(PROTO)
|
||||
|
||||
static void
|
||||
term_get_color(char_u *str, char_u *r, char_u *g, char_u *b)
|
||||
{
|
||||
char_u rn[3], gn[3], bn[3];
|
||||
|
||||
if (sscanf((char *)str, "%*[^:]:%2s%*[^/]/%2s%*[^/]/%2s",
|
||||
(char *)&rn, (char *)&gn, (char *)&bn) != 3)
|
||||
return;
|
||||
|
||||
*r = hexhex2nr(rn);
|
||||
*g = hexhex2nr(gn);
|
||||
*b = hexhex2nr(bn);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
term_get_color(get_vim_var_str(VV_TERMRFGRESP), r, g, b);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -6612,12 +6551,7 @@ term_get_fg_color(char_u *r, char_u *g, char_u *b)
|
||||
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;
|
||||
term_get_color(get_vim_var_str(VV_TERMRBGRESP), r, g, b);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@ -252,6 +252,7 @@ NEW_TESTS = \
|
||||
test_plugin_tohtml \
|
||||
test_plugin_tutor \
|
||||
test_plugin_zip \
|
||||
test_plugin_colorresp \
|
||||
test_plus_arg_edit \
|
||||
test_popup \
|
||||
test_popupwin \
|
||||
@ -525,6 +526,7 @@ NEW_TESTS_RES = \
|
||||
test_plugin_tohtml.res \
|
||||
test_plugin_tutor.res \
|
||||
test_plugin_zip.res \
|
||||
test_plugin_colorresp.res \
|
||||
test_plus_arg_edit.res \
|
||||
test_popup.res \
|
||||
test_popupwin.res \
|
||||
|
||||
74
src/testdir/test_plugin_colorresp.vim
Normal file
74
src/testdir/test_plugin_colorresp.vim
Normal file
@ -0,0 +1,74 @@
|
||||
" Test for the colorresp plugin
|
||||
|
||||
CheckNotGui
|
||||
CheckUnix
|
||||
|
||||
runtime plugin/colorresp.vim
|
||||
|
||||
func Test_colorresp()
|
||||
set t_RF=x
|
||||
set t_RB=y
|
||||
|
||||
" response to t_RF, 4 digits
|
||||
let red = 0x12
|
||||
let green = 0x34
|
||||
let blue = 0x56
|
||||
let seq = printf("\<Esc>]10;rgb:%02x00/%02x00/%02x00\x07", red, green, blue)
|
||||
call feedkeys(seq, 'Lx!')
|
||||
call assert_equal(seq, v:termrfgresp)
|
||||
" call WaitForAssert({-> assert_equal(seq, v:termrfgresp)})
|
||||
|
||||
" response to t_RF, 2 digits
|
||||
let red = 0x78
|
||||
let green = 0x9a
|
||||
let blue = 0xbc
|
||||
let seq = printf("\<Esc>]10;rgb:%02x/%02x/%02x\x07", red, green, blue)
|
||||
call feedkeys(seq, 'Lx!')
|
||||
call assert_equal(seq, v:termrfgresp)
|
||||
|
||||
" response to t_RB, 4 digits, dark
|
||||
set background=light
|
||||
call test_option_not_set('background')
|
||||
let red = 0x29
|
||||
let green = 0x4a
|
||||
let blue = 0x6b
|
||||
let seq = printf("\<Esc>]11;rgb:%02x00/%02x00/%02x00\x07", red, green, blue)
|
||||
call feedkeys(seq, 'Lx!')
|
||||
call assert_equal(seq, v:termrbgresp)
|
||||
call assert_equal('dark', &background)
|
||||
|
||||
" response to t_RB, 4 digits, light
|
||||
set background=dark
|
||||
call test_option_not_set('background')
|
||||
let red = 0x81
|
||||
let green = 0x63
|
||||
let blue = 0x65
|
||||
let seq = printf("\<Esc>]11;rgb:%02x00/%02x00/%02x00\x07", red, green, blue)
|
||||
call feedkeys(seq, 'Lx!')
|
||||
call assert_equal(seq, v:termrbgresp)
|
||||
call assert_equal('light', &background)
|
||||
|
||||
" response to t_RB, 2 digits, dark
|
||||
set background=light
|
||||
call test_option_not_set('background')
|
||||
let red = 0x47
|
||||
let green = 0x59
|
||||
let blue = 0x5b
|
||||
let seq = printf("\<Esc>]11;rgb:%02x/%02x/%02x\x07", red, green, blue)
|
||||
call feedkeys(seq, 'Lx!')
|
||||
call assert_equal(seq, v:termrbgresp)
|
||||
call assert_equal('dark', &background)
|
||||
|
||||
" response to t_RB, 2 digits, light
|
||||
set background=dark
|
||||
call test_option_not_set('background')
|
||||
let red = 0x83
|
||||
let green = 0xa4
|
||||
let blue = 0xc2
|
||||
let seq = printf("\<Esc>]11;rgb:%02x/%02x/%02x\x07", red, green, blue)
|
||||
call feedkeys(seq, 'Lx!')
|
||||
call assert_equal(seq, v:termrbgresp)
|
||||
call assert_equal('light', &background)
|
||||
|
||||
set t_RF= t_RB=
|
||||
endfunc
|
||||
@ -1632,74 +1632,6 @@ func Test_mouse_termcodes()
|
||||
%bw!
|
||||
endfunc
|
||||
|
||||
" This only checks if the sequence is recognized.
|
||||
func Test_term_rgb_response()
|
||||
set t_RF=x
|
||||
set t_RB=y
|
||||
|
||||
" response to t_RF, 4 digits
|
||||
let red = 0x12
|
||||
let green = 0x34
|
||||
let blue = 0x56
|
||||
let seq = printf("\<Esc>]10;rgb:%02x00/%02x00/%02x00\x07", red, green, blue)
|
||||
call feedkeys(seq, 'Lx!')
|
||||
call assert_equal(seq, v:termrfgresp)
|
||||
|
||||
" response to t_RF, 2 digits
|
||||
let red = 0x78
|
||||
let green = 0x9a
|
||||
let blue = 0xbc
|
||||
let seq = printf("\<Esc>]10;rgb:%02x/%02x/%02x\x07", red, green, blue)
|
||||
call feedkeys(seq, 'Lx!')
|
||||
call assert_equal(seq, v:termrfgresp)
|
||||
|
||||
" response to t_RB, 4 digits, dark
|
||||
set background=light
|
||||
eval 'background'->test_option_not_set()
|
||||
let red = 0x29
|
||||
let green = 0x4a
|
||||
let blue = 0x6b
|
||||
let seq = printf("\<Esc>]11;rgb:%02x00/%02x00/%02x00\x07", red, green, blue)
|
||||
call feedkeys(seq, 'Lx!')
|
||||
call assert_equal(seq, v:termrbgresp)
|
||||
call assert_equal('dark', &background)
|
||||
|
||||
" response to t_RB, 4 digits, light
|
||||
set background=dark
|
||||
call test_option_not_set('background')
|
||||
let red = 0x81
|
||||
let green = 0x63
|
||||
let blue = 0x65
|
||||
let seq = printf("\<Esc>]11;rgb:%02x00/%02x00/%02x00\x07", red, green, blue)
|
||||
call feedkeys(seq, 'Lx!')
|
||||
call assert_equal(seq, v:termrbgresp)
|
||||
call assert_equal('light', &background)
|
||||
|
||||
" response to t_RB, 2 digits, dark
|
||||
set background=light
|
||||
call test_option_not_set('background')
|
||||
let red = 0x47
|
||||
let green = 0x59
|
||||
let blue = 0x5b
|
||||
let seq = printf("\<Esc>]11;rgb:%02x/%02x/%02x\x07", red, green, blue)
|
||||
call feedkeys(seq, 'Lx!')
|
||||
call assert_equal(seq, v:termrbgresp)
|
||||
call assert_equal('dark', &background)
|
||||
|
||||
" response to t_RB, 2 digits, light
|
||||
set background=dark
|
||||
call test_option_not_set('background')
|
||||
let red = 0x83
|
||||
let green = 0xa4
|
||||
let blue = 0xc2
|
||||
let seq = printf("\<Esc>]11;rgb:%02x/%02x/%02x\x07", red, green, blue)
|
||||
call feedkeys(seq, 'Lx!')
|
||||
call assert_equal(seq, v:termrbgresp)
|
||||
call assert_equal('light', &background)
|
||||
|
||||
set t_RF= t_RB=
|
||||
endfunc
|
||||
|
||||
" This only checks if the sequence is recognized.
|
||||
" This must be after other tests, because it has side effects to xterm
|
||||
" properties.
|
||||
@ -2808,4 +2740,17 @@ func Test_da1_handling()
|
||||
call assert_equal("\<Esc>[?62,52;c", v:termda1)
|
||||
endfunc
|
||||
|
||||
" Test if OSC terminal responses are captured correctly
|
||||
func Test_term_response_osc()
|
||||
" Test if large OSC responses (that must be processed in chunks) are handled
|
||||
let data = repeat('a', 3000)
|
||||
|
||||
call feedkeys("\<Esc>]12;" .. data .. "\x07", 'Lx!')
|
||||
call assert_equal("\<Esc>]12;" .. data .. "\x07", v:termosc)
|
||||
|
||||
" Test small OSC responses
|
||||
call feedkeys("\<Esc>]15;hello world!\07", 'Lx!')
|
||||
call assert_equal("\<Esc>]15;hello world!\x07", v:termosc)
|
||||
endfunc
|
||||
|
||||
" vim: shiftwidth=2 sts=2 expandtab
|
||||
|
||||
@ -116,6 +116,7 @@ let test_values = {
|
||||
\ 'winminwidth': [[0, 1, 10], [-1]],
|
||||
\ 'winwidth': [[1, 10, 999], [-1, 0]],
|
||||
\ 'wltimeoutlen': [[1, 10, 999],[-1]],
|
||||
\ 'osctimeoutlen': [[0, 1, 8, 9999], [-1]],
|
||||
\
|
||||
"\ string options
|
||||
\ 'ambiwidth': [['', 'single', 'double'], ['xxx']],
|
||||
|
||||
28
src/time.c
28
src/time.c
@ -127,6 +127,20 @@ get_ctime(time_t thetime, int add_newline)
|
||||
return buf;
|
||||
}
|
||||
|
||||
#if defined(MSWIN) || defined(__MINGW32__)
|
||||
/*
|
||||
* Windows doesn't have gettimeofday(), although it does have struct timeval.
|
||||
*/
|
||||
int
|
||||
gettimeofday(struct timeval *tv, char *dummy UNUSED)
|
||||
{
|
||||
long t = clock();
|
||||
tv->tv_sec = t / CLOCKS_PER_SEC;
|
||||
tv->tv_usec = (t - tv->tv_sec * CLOCKS_PER_SEC) * 1000000 / CLOCKS_PER_SEC;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(FEAT_EVAL) || defined(PROTO)
|
||||
|
||||
#if defined(MACOS_X)
|
||||
@ -947,20 +961,6 @@ f_timer_stopall(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
|
||||
# if defined(STARTUPTIME) || defined(PROTO)
|
||||
static struct timeval prev_timeval;
|
||||
|
||||
# ifdef MSWIN
|
||||
/*
|
||||
* Windows doesn't have gettimeofday(), although it does have struct timeval.
|
||||
*/
|
||||
static int
|
||||
gettimeofday(struct timeval *tv, char *dummy UNUSED)
|
||||
{
|
||||
long t = clock();
|
||||
tv->tv_sec = t / CLOCKS_PER_SEC;
|
||||
tv->tv_usec = (t - tv->tv_sec * CLOCKS_PER_SEC) * 1000000 / CLOCKS_PER_SEC;
|
||||
return 0;
|
||||
}
|
||||
# endif
|
||||
|
||||
/*
|
||||
* Save the previous time before doing something that could nest.
|
||||
* set "*tv_rel" to the time elapsed so far.
|
||||
|
||||
@ -724,6 +724,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
1703,
|
||||
/**/
|
||||
1702,
|
||||
/**/
|
||||
|
||||
@ -2240,7 +2240,8 @@ typedef int sock_T;
|
||||
#define VV_WAYLAND_DISPLAY 112
|
||||
#define VV_CLIPMETHOD 113
|
||||
#define VV_TERMDA1 114
|
||||
#define VV_LEN 115 // number of v: vars
|
||||
#define VV_TERMOSC 115
|
||||
#define VV_LEN 116 // number of v: vars
|
||||
|
||||
// used for v_number in VAR_BOOL and VAR_SPECIAL
|
||||
#define VVAL_FALSE 0L // VAR_BOOL
|
||||
|
||||
Reference in New Issue
Block a user