Skip to content

Conversation

joshtriplett
Copy link
Contributor

(Also reported as a vim issue, though I have more hope of a fix here.)

Vim's automatic background color detection works in GUI mode, but not in a terminal.

On any xterm-compatible terminal, vim could detect the background color via terminal escapes (documented in http://invisible-island.net/xterm/ctlseqs/ctlseqs.html) and use that to automatically detect whether to use bg=dark or bg=light (by default or with :set bg&).

To detect the background color:

  • Include `\e]11;?\a' during terminal initialization.
  • Asynchronously, when processing input, watch for the escape sequence \e]11;COLOR\a (or equivalent using OSC instead of \e] for the escape, and ST or \e\\ instead of \a for the terminator, which the existing terminal escape sequence handling probably already handles). If the COLOR portion matches rgb:RRRR/GGGG/BBBB where R, G, and B are hex digits, then compute the luminance 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.

@justinmk justinmk added the tui label Jul 9, 2016
@justinmk justinmk added this to the todo milestone Jul 9, 2016
@joshtriplett
Copy link
Contributor Author

Apparently vim already includes support for this, though neovim does not. Search for rbg_status in vim to find the implementation.

It looks like the terminal input handling drastically changed in neovim, making this hard to directly port over. I attached a WIP commit providing an implementation of this for neovim. It almost works: the background detection works perfectly, as does the setting of the default (so set bg& picks up the right default based on the terminal background), but the call to set_option_value to set the current background sometimes (but not always) causes a SIGABRT deep in init_highlight, while processing /usr/share/nvim/runtime/syntax/syncolor.vim. I haven't yet figured out why, and I'd appreciate some help debugging this.

@justinmk
Copy link
Member

justinmk commented Jul 13, 2016

The TUI runs on a separate thread. Most likely that is the cause of the segfault.

but the call to set_option_value to set the current background sometimes (but not always) causes a SIGABRT deep in init_highlight

This is because init_highlight evaluates vimscript, which must always be done only on the main thread. See inline comment for how to avoid this.

if (!option_was_set((char_u *)"bg")) {
set_option_value((char_u *)"bg", 0, (char_u *)bgvalue, 0);
}
}
Copy link
Member

@justinmk justinmk Jul 13, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try putting this block in a function e.g. set_bg_color_deferred, then schedule it on the main thread like this

loop_schedule(&main_loop, event_create(1, set_bg_color_deferred, ...));

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure enough, that was the problem. New version momentarily.

@joshtriplett joshtriplett force-pushed the terminal-background branch from c29dd00 to 50030e8 Compare July 13, 2016 06:31
@joshtriplett
Copy link
Contributor Author

Just pushed a new version, which now works reliably thanks to @justinmk's advice about running on the main loop.

@justinmk
Copy link
Member

LGTM. Is there a termcode to set the background? It could be used to feed("\027[...") in tui_spec.lua, to set up a test. Otherwise I can't think of a way to test it, it might be too much trouble. But tests help ASan and coverity.

@joshtriplett
Copy link
Contributor Author

Is there a termcode to set the background?

Yes, the same code the terminal writes back to the application: \x1b]rgb:RRRR/GGGG/BBBB\a. However, note that this would change the background of the running terminal. (And the test itself would only work in a terminal, so it might not work on a CI bot.)

It could be used to feed("\027[...") in tui_spec.lua, to set up a test. Otherwise I can't think of a way to test it, it might be too much trouble. But tests help ASan and coverity.

Looking at tui_spec.lua, it seems possible to add tests for this: just feed the terminal the escape sequences for various colors, followed by :set bg, and see if it has the right background color.

@joshtriplett

This comment has been minimized.

@joshtriplett joshtriplett force-pushed the terminal-background branch from 50030e8 to 0a30531 Compare July 13, 2016 09:29
@justinmk

This comment has been minimized.

@justinmk justinmk modified the milestones: 0.2, todo Jul 13, 2016
@joshtriplett

This comment has been minimized.

@joshtriplett
Copy link
Contributor Author

joshtriplett commented Jul 13, 2016

I'm not sure why varying subsets of the tests fail on some platforms, while some of the tests pass. I don't know what would cause spurious test failures.

Is it a race condition to send the control sequence that will change the background, followed by echo &background? Could the background remain unset through the echo?

@joshtriplett
Copy link
Contributor Author

One interesting caveat about this change: setting the background causes a redraw, which dismisses the vim intro message. So if you run on a terminal that supports background color detection, and you don't already have set shortmess+=I, this will cause that message to immediately disappear.

} else if (isxdigit(c)) {
if (component < 3 && rgb_max[component] != 0xffff) {
uint16_t digit = (uint16_t)((c >= '0' && c <= '9')
? c - '0' : 10 + tolower(c) - 'a');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hex2nr() in charset.h could be used here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks; done.

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.
@joshtriplett joshtriplett force-pushed the terminal-background branch from 0a30531 to 5372d9a Compare July 24, 2016 08:56
} else if (bad) {
// ignore
} else if (c == '/') {
if (component < 3) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the sequence not already bad if we see three '/'? Should bad be set?

@justinmk
Copy link
Member

@joshtriplett Any comment on @oni-link's question? And could you rebase this?

@justinmk justinmk modified the milestones: 0.2, 0.3 Oct 31, 2016
@justinmk justinmk modified the milestones: 0.6, 0.4 Jan 16, 2019
justinmk added a commit to justinmk/neovim that referenced this pull request Jan 16, 2019
@justinmk justinmk mentioned this pull request Jan 16, 2019
3 tasks
justinmk added a commit to justinmk/neovim that referenced this pull request Jan 16, 2019
justinmk added a commit to justinmk/neovim that referenced this pull request Jan 17, 2019
justinmk added a commit to justinmk/neovim that referenced this pull request Jan 17, 2019
@justinmk justinmk changed the title Background color detection for terminals TUI: detect background color Jan 28, 2019
justinmk added a commit to justinmk/neovim that referenced this pull request Feb 17, 2019
justinmk added a commit to justinmk/neovim that referenced this pull request Feb 17, 2019
@justinmk
Copy link
Member

Merged in #9509

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants