Thoughts on using OSC codes to automate changing terminal color schemes

Operating System Controls (OSC) are escape sequences used to change and query text parameters, such as text foreground and background. They are most notably present in Xterm, but also in many other compatible terminals. Neovim uses them to automatically set its background color variant based on the background of running terminal. The interface was first introduced in 1970s so there’s a lot of historical baggage (it’s awful), but it has its uses and I thought about adopting it as a part of automated changing of terminal color scheme, an idea mentioned in the post about my monochrome Vim color scheme.

Implementing OSC responses for background and foreground color queries is harder than it initially looks like because the final appearence of terminal window is often a product of many stacked virtual terminals (thanks to Nicholas Mariott who kindly pointed this out to me when I tried to implement OSC responses in tmux). Imagine Xterm instance which hosts several nested sessions of terminal multiplexers (like screen or tmux) and they run Vim, which also supplies its own terminal. If the topmost one (the one which receives the query) doesn’t set a background itself (i.e. it is “transparent”), it can’t reasonably guess displayed colors. It could pass the query further to the underlying terminal, but there may be several of them, with different background colors and all of them might be visible, at least partially. It could also swallow the query, but it would set us back to the starting point, with a pile of applications which don’t know how to talk to each other.

There’s also a second problem with color scheme automation and implementing OSC responses doesn’t solve it. Terminals operate on grid layouts and their size is proportional to the number of cells (letter boxes). Not including line spacing, it equals to a number of characters which fully fit a window vertically and horizontally, multiplied by respectively height and width of a single character. In other words, terminal might not occupy the whole available space. With a stack of terminals with uncoordinated colors, this fact introduces an unpleasant visual artifact: small borders around some of the nested terminals.

We can’t send OSC to the whole stack so some other method must be used to orchestrate colors of terminals and user-facing applications. One way to do it is by generating color definitions in formats native for supported applications and loading them by mechanisms provided by those applications (like reloading Xresources or sourcing RC files). It is something similar to what pywal does.

I have to answer the ultimate question as well: is the idea of automatic color scheme switch worth my time. For me, light color scheme is only useful in very bright, direct sunlight and I actively avoid such working conditions (I use curtains). Without a light sensor, any automation would be then based on a time of a day: a wild and inaccurate guess, which I feel would be more often irritating than helpful.

Maybe it’s time then to buy a cheap sensor, wire it to another cheap microcontroller and hot glue the whole thing over the screen?