aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/news.txt2
-rw-r--r--runtime/lua/vim/_defaults.lua49
-rw-r--r--src/nvim/tui/input.c39
-rw-r--r--src/nvim/tui/input.h1
-rw-r--r--src/nvim/tui/tui.c35
-rw-r--r--src/nvim/tui/tui_defs.h1
6 files changed, 100 insertions, 27 deletions
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index 9c69970400..98782bfd15 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -284,6 +284,8 @@ TUI
:lua =vim.api.nvim_get_chan_info(vim.api.nvim_list_uis()[1].chan)
• |log| messages written by the builtin UI client (TUI, |--remote-ui|) are
now prefixed with "ui" instead of "?".
+• The TUI will re-query the terminal's background color when a theme update
+ notification is received and Nvim will update 'background' accordingly.
UI
diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua
index 2687f34302..6583cf48b3 100644
--- a/runtime/lua/vim/_defaults.lua
+++ b/runtime/lua/vim/_defaults.lua
@@ -546,8 +546,9 @@ do
---
--- @param option string Option name
--- @param value any Option value
- local function setoption(option, value)
- if vim.api.nvim_get_option_info2(option, {}).was_set then
+ --- @param force boolean? Always set the value, even if already set
+ local function setoption(option, value, force)
+ if not force and vim.api.nvim_get_option_info2(option, {}).was_set then
-- Don't do anything if option is already set
return
end
@@ -563,7 +564,7 @@ do
once = true,
nested = true,
callback = function()
- setoption(option, value)
+ setoption(option, value, force)
end,
})
end
@@ -645,11 +646,15 @@ do
return nil, nil, nil
end
- local timer = assert(vim.uv.new_timer())
-
+ -- This autocommand updates the value of 'background' anytime we receive
+ -- an OSC 11 response from the terminal emulator. If the user has set
+ -- 'background' explictly then we will delete this autocommand,
+ -- effectively disabling automatic background setting.
+ local force = false
local id = vim.api.nvim_create_autocmd('TermResponse', {
group = group,
nested = true,
+ desc = "Update the value of 'background' automatically based on the terminal emulator's background color",
callback = function(args)
local resp = args.data ---@type string
local r, g, b = parseosc11(resp)
@@ -661,27 +666,33 @@ do
if rr and gg and bb then
local luminance = (0.299 * rr) + (0.587 * gg) + (0.114 * bb)
local bg = luminance < 0.5 and 'dark' or 'light'
- setoption('background', bg)
+ setoption('background', bg, force)
+
+ -- On the first query response, don't force setting the option in
+ -- case the user has already set it manually. If they have, then
+ -- this autocommand will be deleted. If they haven't, then we do
+ -- want to force setting the option to override the value set by
+ -- this autocommand.
+ if not force then
+ force = true
+ end
end
+ end
+ end,
+ })
- return true
+ vim.api.nvim_create_autocmd('VimEnter', {
+ group = group,
+ nested = true,
+ once = true,
+ callback = function()
+ if vim.api.nvim_get_option_info2('background', {}).was_set then
+ vim.api.nvim_del_autocmd(id)
end
end,
})
io.stdout:write('\027]11;?\007')
-
- timer:start(1000, 0, function()
- -- Delete the autocommand if no response was received
- vim.schedule(function()
- -- Suppress error if autocommand has already been deleted
- pcall(vim.api.nvim_del_autocmd, id)
- end)
-
- if not timer:is_closing() then
- timer:close()
- end
- end)
end
--- If the TUI (term_has_truecolor) was able to determine that the host
diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c
index 98dd7b4b45..744d306c06 100644
--- a/src/nvim/tui/input.c
+++ b/src/nvim/tui/input.c
@@ -160,12 +160,16 @@ void tinput_init(TermInput *input, Loop *loop)
// initialize a timer handle for handling ESC with libtermkey
uv_timer_init(&loop->uv, &input->timer_handle);
input->timer_handle.data = input;
+
+ uv_timer_init(&loop->uv, &input->bg_query_timer);
+ input->bg_query_timer.data = input;
}
void tinput_destroy(TermInput *input)
{
map_destroy(int, &kitty_key_map);
uv_close((uv_handle_t *)&input->timer_handle, NULL);
+ uv_close((uv_handle_t *)&input->bg_query_timer, NULL);
rstream_may_close(&input->read_stream);
termkey_destroy(input->tk);
}
@@ -179,6 +183,7 @@ void tinput_stop(TermInput *input)
{
rstream_stop(&input->read_stream);
uv_timer_stop(&input->timer_handle);
+ uv_timer_stop(&input->bg_query_timer);
}
static void tinput_done_event(void **argv)
@@ -474,6 +479,13 @@ static void tinput_timer_cb(uv_timer_t *handle)
tinput_flush(input);
}
+static void bg_query_timer_cb(uv_timer_t *handle)
+ FUNC_ATTR_NONNULL_ALL
+{
+ TermInput *input = handle->data;
+ tui_query_bg_color(input->tui_data);
+}
+
/// Handle focus events.
///
/// If the upcoming sequence of bytes in the input stream matches the termcode
@@ -660,6 +672,33 @@ static void handle_unknown_csi(TermInput *input, const TermKeyKey *key)
}
}
break;
+ case 'n':
+ // Device Status Report (DSR)
+ if (nparams == 2) {
+ int args[2];
+ for (size_t i = 0; i < ARRAY_SIZE(args); i++) {
+ if (termkey_interpret_csi_param(params[i], &args[i], NULL, NULL) != TERMKEY_RES_KEY) {
+ return;
+ }
+ }
+
+ if (args[0] == 997) {
+ // Theme update notification
+ // https://github.com/contour-terminal/contour/blob/master/docs/vt-extensions/color-palette-update-notifications.md
+ // The second argument tells us whether the OS theme is set to light
+ // mode or dark mode, but all we care about is the background color of
+ // the terminal emulator. We query for that with OSC 11 and the response
+ // is handled by the autocommand created in _defaults.lua. The terminal
+ // may send us multiple notifications all at once so we use a timer to
+ // coalesce the queries.
+ if (uv_timer_get_due_in(&input->bg_query_timer) > 0) {
+ return;
+ }
+
+ uv_timer_start(&input->bg_query_timer, bg_query_timer_cb, 100, 0);
+ }
+ }
+ break;
default:
break;
}
diff --git a/src/nvim/tui/input.h b/src/nvim/tui/input.h
index 4c2baf908e..f6aaff30de 100644
--- a/src/nvim/tui/input.h
+++ b/src/nvim/tui/input.h
@@ -32,6 +32,7 @@ typedef struct {
TermKey *tk;
TermKey_Terminfo_Getstr_Hook *tk_ti_hook_fn; ///< libtermkey terminfo hook
uv_timer_t timer_handle;
+ uv_timer_t bg_query_timer; ///< timer used to batch background color queries
Loop *loop;
RStream read_stream;
TUIData *tui_data;
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index 6e0012096b..603db5b891 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -241,16 +241,19 @@ void tui_handle_term_mode(TUIData *tui, TermMode mode, TermModeState state)
tui->unibi_ext.sync = (int)unibi_add_ext_str(tui->ut, "Sync",
"\x1b[?2026%?%p1%{1}%-%tl%eh%;");
break;
- case kTermModeResizeEvents:
- signal_watcher_stop(&tui->winch_handle);
- tui_set_term_mode(tui, mode, true);
- break;
case kTermModeGraphemeClusters:
if (!is_set) {
tui_set_term_mode(tui, mode, true);
tui->did_set_grapheme_cluster_mode = true;
}
break;
+ case kTermModeThemeUpdates:
+ tui_set_term_mode(tui, mode, true);
+ break;
+ case kTermModeResizeEvents:
+ signal_watcher_stop(&tui->winch_handle);
+ tui_set_term_mode(tui, mode, true);
+ break;
}
}
}
@@ -320,6 +323,18 @@ static void tui_reset_key_encoding(TUIData *tui)
}
}
+/// Write the OSC 11 sequence to the terminal emulator to query the current
+/// background color.
+///
+/// The response will be handled by the TermResponse autocommand created in
+/// _defaults.lua.
+void tui_query_bg_color(TUIData *tui)
+ FUNC_ATTR_NONNULL_ALL
+{
+ out(tui, S_LEN("\x1b]11;?\x07"));
+ flush_buf(tui);
+}
+
/// Enable the alternate screen and emit other control sequences to start the TUI.
///
/// This is also called when the TUI is resumed after being suspended. We reinitialize all state
@@ -438,14 +453,13 @@ static void terminfo_start(TUIData *tui)
// Enable bracketed paste
unibi_out_ext(tui, tui->unibi_ext.enable_bracketed_paste);
- // Query support for mode 2026 (Synchronized Output). Some terminals also
- // support an older DCS sequence for synchronized output, but we will only use
- // mode 2026.
+ // Query support for private DEC modes that Nvim can take advantage of.
// Some terminals (such as Terminal.app) do not support DECRQM, so skip the query.
if (!nsterm) {
tui_request_term_mode(tui, kTermModeSynchronizedOutput);
- tui_request_term_mode(tui, kTermModeResizeEvents);
tui_request_term_mode(tui, kTermModeGraphemeClusters);
+ tui_request_term_mode(tui, kTermModeThemeUpdates);
+ tui_request_term_mode(tui, kTermModeResizeEvents);
}
// Don't use DECRQSS in screen or tmux, as they behave strangely when receiving it.
@@ -493,6 +507,10 @@ static void terminfo_start(TUIData *tui)
/// Disable the alternate screen and prepare for the TUI to close.
static void terminfo_stop(TUIData *tui)
{
+ // Disable theme update notifications. We do this first to avoid getting any
+ // more notifications after we reset the cursor and any color palette changes.
+ tui_set_term_mode(tui, kTermModeThemeUpdates, false);
+
// Destroy output stuff
tui_mode_change(tui, NULL_STRING, SHAPE_IDX_N);
tui_mouse_off(tui);
@@ -509,6 +527,7 @@ static void terminfo_stop(TUIData *tui)
if (tui->did_set_grapheme_cluster_mode) {
tui_set_term_mode(tui, kTermModeGraphemeClusters, false);
}
+
// May restore old title before exiting alternate screen.
tui_set_title(tui, NULL_STRING);
if (ui_client_exit_status == 0) {
diff --git a/src/nvim/tui/tui_defs.h b/src/nvim/tui/tui_defs.h
index bd99d6b0ad..5d6f027bf7 100644
--- a/src/nvim/tui/tui_defs.h
+++ b/src/nvim/tui/tui_defs.h
@@ -5,6 +5,7 @@ typedef struct TUIData TUIData;
typedef enum {
kTermModeSynchronizedOutput = 2026,
kTermModeGraphemeClusters = 2027,
+ kTermModeThemeUpdates = 2031,
kTermModeResizeEvents = 2048,
} TermMode;