From 298608f88c463705cfd6ee47035c049dbb1d9fa0 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 12 Jul 2016 22:52:10 -0700 Subject: Automatically detect terminal background and set bg=dark or bg=light xterm-compatible terminals support reporting their configured colors back to the application. Use this to obtain the current background color, compute its luminance to classify it as light or dark, and set 'bg' accordingly. Also set the default for 'bg', so that `:set bg&` will revert to that detected default. --- src/nvim/option.c | 2 +- src/nvim/tui/input.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++- src/nvim/tui/tui.c | 6 +++++ 3 files changed, 71 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/option.c b/src/nvim/option.c index 81f57522b3..5333c0291d 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -874,7 +874,7 @@ set_options_default ( /// @param name The name of the option /// @param val The value of the option /// @param allocated If true, do not copy default as it was already allocated. -static void set_string_default(const char *name, char *val, bool allocated) +void set_string_default(const char *name, char *val, bool allocated) FUNC_ATTR_NONNULL_ALL { int opt_idx = findoption((char_u *)name); diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index be256f3ebc..c41107d4d3 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -4,8 +4,10 @@ #include "nvim/api/vim.h" #include "nvim/api/private/helpers.h" #include "nvim/ascii.h" +#include "nvim/charset.h" #include "nvim/main.h" #include "nvim/misc2.h" +#include "nvim/option.h" #include "nvim/os/os.h" #include "nvim/os/input.h" #include "nvim/event/rstream.h" @@ -320,6 +322,66 @@ static bool handle_forced_escape(TermInput *input) return false; } +static void set_bg_deferred(void **argv) +{ + char *bgvalue = argv[0]; + set_string_default("bg", bgvalue, false); + if (!option_was_set((char_u *)"bg")) { + set_option_value((char_u *)"bg", 0, (char_u *)bgvalue, 0); + } +} + +static bool handle_background_color(TermInput *input) +{ + size_t count = 0; + size_t component = 0; + uint16_t rgb[] = { 0, 0, 0 }; + uint16_t rgb_max[] = { 0, 0, 0 }; + bool eat_backslash = false; + bool done = false; + bool bad = false; + if (rbuffer_size(input->read_stream.buffer) >= 9 + && !rbuffer_cmp(input->read_stream.buffer, "\x1b]11;rgb:", 9)) { + rbuffer_consumed(input->read_stream.buffer, 9); + RBUFFER_EACH(input->read_stream.buffer, c, i) { + count = i + 1; + if (eat_backslash) { + done = true; + break; + } else if (c == '\x07') { + done = true; + break; + } else if (c == '\x1b') { + eat_backslash = true; + } else if (bad) { + // ignore + } else if (c == '/') { + if (component < 3) { + component++; + } + } else if (ascii_isxdigit(c)) { + if (component < 3 && rgb_max[component] != 0xffff) { + rgb_max[component] = (uint16_t)((rgb_max[component] << 4) | 0xf); + rgb[component] = (uint16_t)((rgb[component] << 4) | hex2nr(c)); + } + } else { + bad = true; + } + } + rbuffer_consumed(input->read_stream.buffer, count); + if (done && !bad && rgb_max[0] && rgb_max[1] && rgb_max[2]) { + double r = (double)rgb[0] / (double)rgb_max[0]; + double g = (double)rgb[1] / (double)rgb_max[1]; + double b = (double)rgb[2] / (double)rgb_max[2]; + double luminance = 0.299*r + 0.587*g + 0.114*b; // CCIR 601 + char *bgvalue = luminance < 0.5 ? "dark" : "light"; + loop_schedule(&main_loop, event_create(1, set_bg_deferred, 1, bgvalue)); + } + return true; + } + return false; +} + static void restart_reading(void **argv); static void read_cb(Stream *stream, RBuffer *buf, size_t c, void *data, @@ -351,7 +413,8 @@ static void read_cb(Stream *stream, RBuffer *buf, size_t c, void *data, do { if (handle_focus_event(input) || handle_bracketed_paste(input) - || handle_forced_escape(input)) { + || handle_forced_escape(input) + || handle_background_color(input)) { continue; } diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index d220df508a..c4efa69c4c 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -68,6 +68,7 @@ typedef struct { int enter_insert_mode, enter_replace_mode, exit_insert_mode; int set_rgb_foreground, set_rgb_background; int enable_focus_reporting, disable_focus_reporting; + int get_bg; } unibi_ext; } TUIData; @@ -125,6 +126,7 @@ static void terminfo_start(UI *ui) data->unibi_ext.exit_insert_mode = -1; data->unibi_ext.enable_focus_reporting = -1; data->unibi_ext.disable_focus_reporting = -1; + data->unibi_ext.get_bg = -1; data->out_fd = 1; data->out_isatty = os_isatty(data->out_fd); // setup unibilium @@ -140,6 +142,8 @@ static void terminfo_start(UI *ui) // Enter alternate screen and clear unibi_out(ui, unibi_enter_ca_mode); unibi_out(ui, unibi_clear_screen); + // Ask the terminal to send us the background color + unibi_out(ui, data->unibi_ext.get_bg); // Enable bracketed paste unibi_out(ui, data->unibi_ext.enable_bracketed_paste); // Enable focus reporting @@ -829,6 +833,8 @@ static void fix_terminfo(TUIData *data) data->unibi_ext.disable_focus_reporting = (int)unibi_add_ext_str(ut, NULL, "\x1b[?1004l"); + data->unibi_ext.get_bg = (int)unibi_add_ext_str(ut, NULL, "\x1b]11;?\x07"); + #define XTERM_SETAF \ "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m" #define XTERM_SETAB \ -- cgit From d3dc94615523a71606edcbe6bf528f8b49a78c7d Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 17 Feb 2019 11:32:18 +0100 Subject: TUI: rework background-color detection - Like Vim, use set_option_value() followed by reset_option_was_set(). - Do not use set_string_default(), so the default is predictable. This affects `:set bg&`. - Wait until end-of-startup (VimEnter) to handle the response. The response is racey anyways, so timing is irrelevant. This allows OptionSet to be triggered, unlike during startup. --- src/nvim/globals.h | 8 ++++---- src/nvim/option.c | 2 +- src/nvim/tui/input.c | 25 ++++++++++++++++++++++--- 3 files changed, 27 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/nvim/globals.h b/src/nvim/globals.h index f47697b190..a988af79b2 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -83,10 +83,10 @@ EXTERN struct nvim_stats_s { int64_t redraw; } g_stats INIT(= { 0, 0 }); -/* Values for "starting" */ -#define NO_SCREEN 2 /* no screen updating yet */ -#define NO_BUFFERS 1 /* not all buffers loaded yet */ -/* 0 not starting anymore */ +// Values for "starting". +#define NO_SCREEN 2 // no screen updating yet +#define NO_BUFFERS 1 // not all buffers loaded yet +// 0 not starting anymore /* * Number of Rows and Columns in the screen. diff --git a/src/nvim/option.c b/src/nvim/option.c index b85ec6dbcc..fc1fab834e 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -883,7 +883,7 @@ set_options_default ( /// @param name The name of the option /// @param val The value of the option /// @param allocated If true, do not copy default as it was already allocated. -void set_string_default(const char *name, char *val, bool allocated) +static void set_string_default(const char *name, char *val, bool allocated) FUNC_ATTR_NONNULL_ALL { int opt_idx = findoption(name); diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index e0dd4022ed..3eb88366d6 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -357,12 +357,28 @@ static bool handle_forced_escape(TermInput *input) static void set_bg_deferred(void **argv) { char *bgvalue = argv[0]; - set_string_default("bg", bgvalue, false); - if (!option_was_set("bg")) { - set_option_value("bg", 0, bgvalue, 0); + if (starting) { + // Wait until after startup, so OptionSet is triggered. + loop_schedule(&main_loop, event_create(set_bg_deferred, 1, bgvalue)); + return; + } + if (!option_was_set("bg") && !strequal((char *)p_bg, bgvalue)) { + // Value differs, apply it. + set_option_value("bg", 0L, bgvalue, 0); + reset_option_was_set("bg"); } } +// During startup, tui.c requests the background color (see `ext.get_bg`). +// +// Here in input.c, we watch for the terminal response `\e]11;COLOR\a`. If +// COLOR matches `rgb:RRRR/GGGG/BBBB` where R, G, and B are hex digits, then +// compute the luminance[1] of the RGB color and classify it as light/dark +// accordingly. Note that the color components may have anywhere from one to +// four hex digits, and require scaling accordingly as values out of 4, 8, 12, +// or 16 bits. +// +// [1] https://en.wikipedia.org/wiki/Luma_%28video%29 static bool handle_background_color(TermInput *input) { size_t count = 0; @@ -407,7 +423,10 @@ static bool handle_background_color(TermInput *input) double b = (double)rgb[2] / (double)rgb_max[2]; double luminance = (0.299 * r) + (0.587 * g) + (0.114 * b); // CCIR 601 char *bgvalue = luminance < 0.5 ? "dark" : "light"; + DLOG("bg response: %s", bgvalue); loop_schedule(&main_loop, event_create(set_bg_deferred, 1, bgvalue)); + } else { + DLOG("failed to parse bg response"); } return true; } -- cgit