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.
This commit is contained in:
Josh Triplett 2016-07-12 22:52:10 -07:00
parent ef72303a1f
commit 298608f88c
3 changed files with 71 additions and 2 deletions

View File

@ -874,7 +874,7 @@ set_options_default (
/// @param name The name of the option /// @param name The name of the option
/// @param val The value of the option /// @param val The value of the option
/// @param allocated If true, do not copy default as it was already allocated. /// @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 FUNC_ATTR_NONNULL_ALL
{ {
int opt_idx = findoption((char_u *)name); int opt_idx = findoption((char_u *)name);

View File

@ -4,8 +4,10 @@
#include "nvim/api/vim.h" #include "nvim/api/vim.h"
#include "nvim/api/private/helpers.h" #include "nvim/api/private/helpers.h"
#include "nvim/ascii.h" #include "nvim/ascii.h"
#include "nvim/charset.h"
#include "nvim/main.h" #include "nvim/main.h"
#include "nvim/misc2.h" #include "nvim/misc2.h"
#include "nvim/option.h"
#include "nvim/os/os.h" #include "nvim/os/os.h"
#include "nvim/os/input.h" #include "nvim/os/input.h"
#include "nvim/event/rstream.h" #include "nvim/event/rstream.h"
@ -320,6 +322,66 @@ static bool handle_forced_escape(TermInput *input)
return false; 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 restart_reading(void **argv);
static void read_cb(Stream *stream, RBuffer *buf, size_t c, void *data, 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 { do {
if (handle_focus_event(input) if (handle_focus_event(input)
|| handle_bracketed_paste(input) || handle_bracketed_paste(input)
|| handle_forced_escape(input)) { || handle_forced_escape(input)
|| handle_background_color(input)) {
continue; continue;
} }

View File

@ -68,6 +68,7 @@ typedef struct {
int enter_insert_mode, enter_replace_mode, exit_insert_mode; int enter_insert_mode, enter_replace_mode, exit_insert_mode;
int set_rgb_foreground, set_rgb_background; int set_rgb_foreground, set_rgb_background;
int enable_focus_reporting, disable_focus_reporting; int enable_focus_reporting, disable_focus_reporting;
int get_bg;
} unibi_ext; } unibi_ext;
} TUIData; } TUIData;
@ -125,6 +126,7 @@ static void terminfo_start(UI *ui)
data->unibi_ext.exit_insert_mode = -1; data->unibi_ext.exit_insert_mode = -1;
data->unibi_ext.enable_focus_reporting = -1; data->unibi_ext.enable_focus_reporting = -1;
data->unibi_ext.disable_focus_reporting = -1; data->unibi_ext.disable_focus_reporting = -1;
data->unibi_ext.get_bg = -1;
data->out_fd = 1; data->out_fd = 1;
data->out_isatty = os_isatty(data->out_fd); data->out_isatty = os_isatty(data->out_fd);
// setup unibilium // setup unibilium
@ -140,6 +142,8 @@ static void terminfo_start(UI *ui)
// Enter alternate screen and clear // Enter alternate screen and clear
unibi_out(ui, unibi_enter_ca_mode); unibi_out(ui, unibi_enter_ca_mode);
unibi_out(ui, unibi_clear_screen); 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 // Enable bracketed paste
unibi_out(ui, data->unibi_ext.enable_bracketed_paste); unibi_out(ui, data->unibi_ext.enable_bracketed_paste);
// Enable focus reporting // 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, data->unibi_ext.disable_focus_reporting = (int)unibi_add_ext_str(ut, NULL,
"\x1b[?1004l"); "\x1b[?1004l");
data->unibi_ext.get_bg = (int)unibi_add_ext_str(ut, NULL, "\x1b]11;?\x07");
#define XTERM_SETAF \ #define XTERM_SETAF \
"\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m" "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m"
#define XTERM_SETAB \ #define XTERM_SETAB \