mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
feat(terminal): support theme update notifications (DEC mode 2031) (#31999)
This commit is contained in:
parent
7eabc8899a
commit
f1c45fc7a4
@ -337,6 +337,8 @@ TERMINAL
|
|||||||
unfocused terminal window will have no cursor at all (so there is nothing to
|
unfocused terminal window will have no cursor at all (so there is nothing to
|
||||||
highlight).
|
highlight).
|
||||||
• |jobstart()| gained the "term" flag.
|
• |jobstart()| gained the "term" flag.
|
||||||
|
• The |terminal| will send theme update notifications when 'background' is
|
||||||
|
changed and DEC mode 2031 is enabled.
|
||||||
|
|
||||||
TREESITTER
|
TREESITTER
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@
|
|||||||
#include "nvim/spellfile.h"
|
#include "nvim/spellfile.h"
|
||||||
#include "nvim/spellsuggest.h"
|
#include "nvim/spellsuggest.h"
|
||||||
#include "nvim/strings.h"
|
#include "nvim/strings.h"
|
||||||
|
#include "nvim/terminal.h"
|
||||||
#include "nvim/types_defs.h"
|
#include "nvim/types_defs.h"
|
||||||
#include "nvim/vim_defs.h"
|
#include "nvim/vim_defs.h"
|
||||||
#include "nvim/window.h"
|
#include "nvim/window.h"
|
||||||
@ -532,6 +533,15 @@ const char *did_set_background(optset_T *args)
|
|||||||
check_string_option(&p_bg);
|
check_string_option(&p_bg);
|
||||||
init_highlight(false, false);
|
init_highlight(false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Notify all terminal buffers that the background color changed so they can
|
||||||
|
// send a theme update notification
|
||||||
|
FOR_ALL_BUFFERS(buf) {
|
||||||
|
if (buf->terminal) {
|
||||||
|
terminal_notify_theme(buf->terminal, dark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,6 +179,8 @@ struct terminal {
|
|||||||
StringBuilder *send; ///< When there is a pending TermRequest autocommand, block and store input.
|
StringBuilder *send; ///< When there is a pending TermRequest autocommand, block and store input.
|
||||||
} pending;
|
} pending;
|
||||||
|
|
||||||
|
bool theme_updates; ///< Send a theme update notification when 'bg' changes
|
||||||
|
|
||||||
bool color_set[16];
|
bool color_set[16];
|
||||||
|
|
||||||
char *selection_buffer; /// libvterm selection buffer
|
char *selection_buffer; /// libvterm selection buffer
|
||||||
@ -193,6 +195,7 @@ static VTermScreenCallbacks vterm_screen_callbacks = {
|
|||||||
.movecursor = term_movecursor,
|
.movecursor = term_movecursor,
|
||||||
.settermprop = term_settermprop,
|
.settermprop = term_settermprop,
|
||||||
.bell = term_bell,
|
.bell = term_bell,
|
||||||
|
.theme = term_theme,
|
||||||
.sb_pushline = term_sb_push, // Called before a line goes offscreen.
|
.sb_pushline = term_sb_push, // Called before a line goes offscreen.
|
||||||
.sb_popline = term_sb_pop,
|
.sb_popline = term_sb_pop,
|
||||||
};
|
};
|
||||||
@ -1141,6 +1144,20 @@ bool terminal_running(const Terminal *term)
|
|||||||
return !term->closed;
|
return !term->closed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void terminal_notify_theme(Terminal *term, bool dark)
|
||||||
|
FUNC_ATTR_NONNULL_ALL
|
||||||
|
{
|
||||||
|
if (!term->theme_updates) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char buf[10];
|
||||||
|
ssize_t ret = snprintf(buf, sizeof(buf), "\x1b[997;%cn", dark ? '1' : '2');
|
||||||
|
assert(ret > 0);
|
||||||
|
assert((size_t)ret <= sizeof(buf));
|
||||||
|
terminal_send(term, buf, (size_t)ret);
|
||||||
|
}
|
||||||
|
|
||||||
static void terminal_focus(const Terminal *term, bool focus)
|
static void terminal_focus(const Terminal *term, bool focus)
|
||||||
FUNC_ATTR_NONNULL_ALL
|
FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
@ -1259,6 +1276,10 @@ static int term_settermprop(VTermProp prop, VTermValue *val, void *data)
|
|||||||
invalidate_terminal(term, -1, -1);
|
invalidate_terminal(term, -1, -1);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case VTERM_PROP_THEMEUPDATES:
|
||||||
|
term->theme_updates = val->boolean;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1273,6 +1294,14 @@ static int term_bell(void *data)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Called when the terminal wants to query the system theme.
|
||||||
|
static int term_theme(bool *dark, void *data)
|
||||||
|
FUNC_ATTR_NONNULL_ALL
|
||||||
|
{
|
||||||
|
*dark = (*p_bg == 'd');
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/// Scrollback push handler: called just before a line goes offscreen (and libvterm will forget it),
|
/// Scrollback push handler: called just before a line goes offscreen (and libvterm will forget it),
|
||||||
/// giving us a chance to store it.
|
/// giving us a chance to store it.
|
||||||
///
|
///
|
||||||
|
@ -784,6 +784,17 @@ static int resize(int new_rows, int new_cols, VTermStateFields *fields, void *us
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int theme(bool *dark, void *user)
|
||||||
|
{
|
||||||
|
VTermScreen *screen = user;
|
||||||
|
|
||||||
|
if (screen->callbacks && screen->callbacks->theme) {
|
||||||
|
return (*screen->callbacks->theme)(dark, screen->cbdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static int setlineinfo(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo,
|
static int setlineinfo(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo,
|
||||||
void *user)
|
void *user)
|
||||||
{
|
{
|
||||||
@ -838,6 +849,7 @@ static VTermStateCallbacks state_cbs = {
|
|||||||
.settermprop = &settermprop,
|
.settermprop = &settermprop,
|
||||||
.bell = &bell,
|
.bell = &bell,
|
||||||
.resize = &resize,
|
.resize = &resize,
|
||||||
|
.theme = &theme,
|
||||||
.setlineinfo = &setlineinfo,
|
.setlineinfo = &setlineinfo,
|
||||||
.sb_clear = &sb_clear,
|
.sb_clear = &sb_clear,
|
||||||
};
|
};
|
||||||
|
@ -819,6 +819,10 @@ static void set_dec_mode(VTermState *state, int num, int val)
|
|||||||
state->mode.bracketpaste = (unsigned)val;
|
state->mode.bracketpaste = (unsigned)val;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 2031:
|
||||||
|
settermprop_bool(state, VTERM_PROP_THEMEUPDATES, val);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
DEBUG_LOG("libvterm: Unknown DEC mode %d\n", num);
|
DEBUG_LOG("libvterm: Unknown DEC mode %d\n", num);
|
||||||
return;
|
return;
|
||||||
@ -894,6 +898,10 @@ static void request_dec_mode(VTermState *state, int num)
|
|||||||
reply = state->mode.bracketpaste;
|
reply = state->mode.bracketpaste;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 2031:
|
||||||
|
reply = state->mode.theme_updates;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?%d;%d$y", num, 0);
|
vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?%d;%d$y", num, 0);
|
||||||
return;
|
return;
|
||||||
@ -1387,6 +1395,7 @@ static int on_csi(const char *leader, const long args[], int argcount, const cha
|
|||||||
|
|
||||||
{
|
{
|
||||||
char *qmark = (leader_byte == '?') ? "?" : "";
|
char *qmark = (leader_byte == '?') ? "?" : "";
|
||||||
|
bool dark = false;
|
||||||
|
|
||||||
switch (val) {
|
switch (val) {
|
||||||
case 0:
|
case 0:
|
||||||
@ -1403,6 +1412,13 @@ static int on_csi(const char *leader, const long args[], int argcount, const cha
|
|||||||
vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%s%d;%dR", qmark, state->pos.row + 1,
|
vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%s%d;%dR", qmark, state->pos.row + 1,
|
||||||
state->pos.col + 1);
|
state->pos.col + 1);
|
||||||
break;
|
break;
|
||||||
|
case 996:
|
||||||
|
if (state->callbacks && state->callbacks->theme) {
|
||||||
|
if (state->callbacks->theme(&dark, state->cbdata)) {
|
||||||
|
vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?997;%cn", dark ? '1' : '2');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -2268,6 +2284,9 @@ int vterm_state_set_termprop(VTermState *state, VTermProp prop, VTermValue *val)
|
|||||||
case VTERM_PROP_FOCUSREPORT:
|
case VTERM_PROP_FOCUSREPORT:
|
||||||
state->mode.report_focus = (unsigned)val->boolean;
|
state->mode.report_focus = (unsigned)val->boolean;
|
||||||
return 1;
|
return 1;
|
||||||
|
case VTERM_PROP_THEMEUPDATES:
|
||||||
|
state->mode.theme_updates = (unsigned)val->boolean;
|
||||||
|
return 1;
|
||||||
|
|
||||||
case VTERM_N_PROPS:
|
case VTERM_N_PROPS:
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -86,6 +86,7 @@ typedef enum {
|
|||||||
VTERM_PROP_CURSORSHAPE, // number
|
VTERM_PROP_CURSORSHAPE, // number
|
||||||
VTERM_PROP_MOUSE, // number
|
VTERM_PROP_MOUSE, // number
|
||||||
VTERM_PROP_FOCUSREPORT, // bool
|
VTERM_PROP_FOCUSREPORT, // bool
|
||||||
|
VTERM_PROP_THEMEUPDATES, // bool
|
||||||
|
|
||||||
VTERM_N_PROPS,
|
VTERM_N_PROPS,
|
||||||
} VTermProp;
|
} VTermProp;
|
||||||
@ -111,6 +112,7 @@ typedef struct {
|
|||||||
int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
|
int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
|
||||||
int (*bell)(void *user);
|
int (*bell)(void *user);
|
||||||
int (*resize)(int rows, int cols, void *user);
|
int (*resize)(int rows, int cols, void *user);
|
||||||
|
int (*theme)(bool *dark, void *user);
|
||||||
int (*sb_pushline)(int cols, const VTermScreenCell *cells, void *user);
|
int (*sb_pushline)(int cols, const VTermScreenCell *cells, void *user);
|
||||||
int (*sb_popline)(int cols, VTermScreenCell *cells, void *user);
|
int (*sb_popline)(int cols, VTermScreenCell *cells, void *user);
|
||||||
int (*sb_clear)(void *user);
|
int (*sb_clear)(void *user);
|
||||||
@ -263,6 +265,7 @@ typedef struct {
|
|||||||
int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
|
int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
|
||||||
int (*bell)(void *user);
|
int (*bell)(void *user);
|
||||||
int (*resize)(int rows, int cols, VTermStateFields *fields, void *user);
|
int (*resize)(int rows, int cols, VTermStateFields *fields, void *user);
|
||||||
|
int (*theme)(bool *dark, void *user);
|
||||||
int (*setlineinfo)(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo,
|
int (*setlineinfo)(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo,
|
||||||
void *user);
|
void *user);
|
||||||
int (*sb_clear)(void *user);
|
int (*sb_clear)(void *user);
|
||||||
|
@ -119,6 +119,7 @@ struct VTermState {
|
|||||||
unsigned leftrightmargin:1;
|
unsigned leftrightmargin:1;
|
||||||
unsigned bracketpaste:1;
|
unsigned bracketpaste:1;
|
||||||
unsigned report_focus:1;
|
unsigned report_focus:1;
|
||||||
|
unsigned theme_updates:1;
|
||||||
} mode;
|
} mode;
|
||||||
|
|
||||||
VTermEncodingInstance encoding[4], encoding_utf8;
|
VTermEncodingInstance encoding[4], encoding_utf8;
|
||||||
|
@ -3309,6 +3309,32 @@ describe('TUI bg color', function()
|
|||||||
{3:-- TERMINAL --} |
|
{3:-- TERMINAL --} |
|
||||||
]])
|
]])
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('sends theme update notifications when background changes #31652', function()
|
||||||
|
command('set background=dark') -- set outer Nvim background
|
||||||
|
local child_server = new_pipename()
|
||||||
|
local screen = tt.setup_child_nvim({
|
||||||
|
'--listen',
|
||||||
|
child_server,
|
||||||
|
'-u',
|
||||||
|
'NONE',
|
||||||
|
'-i',
|
||||||
|
'NONE',
|
||||||
|
'--cmd',
|
||||||
|
'colorscheme vim',
|
||||||
|
'--cmd',
|
||||||
|
'set noswapfile',
|
||||||
|
})
|
||||||
|
screen:expect({ any = '%[No Name%]' })
|
||||||
|
local child_session = n.connect(child_server)
|
||||||
|
retry(nil, nil, function()
|
||||||
|
eq({ true, 'dark' }, { child_session:request('nvim_eval', '&background') })
|
||||||
|
end)
|
||||||
|
command('set background=light') -- set outer Nvim background
|
||||||
|
retry(nil, nil, function()
|
||||||
|
eq({ true, 'light' }, { child_session:request('nvim_eval', '&background') })
|
||||||
|
end)
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- These tests require `tt` because --headless/--embed
|
-- These tests require `tt` because --headless/--embed
|
||||||
|
@ -345,6 +345,8 @@ static VTermValueType vterm_get_prop_type(VTermProp prop)
|
|||||||
return VTERM_VALUETYPE_INT;
|
return VTERM_VALUETYPE_INT;
|
||||||
case VTERM_PROP_FOCUSREPORT:
|
case VTERM_PROP_FOCUSREPORT:
|
||||||
return VTERM_VALUETYPE_BOOL;
|
return VTERM_VALUETYPE_BOOL;
|
||||||
|
case VTERM_PROP_THEMEUPDATES:
|
||||||
|
return VTERM_VALUETYPE_BOOL;
|
||||||
|
|
||||||
case VTERM_N_PROPS:
|
case VTERM_N_PROPS:
|
||||||
return 0;
|
return 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user