Unable to query back/foreground color with gnome-terminal/tilix (libvte)
fd0 opened this issue · comments
Hi,
thanks for this awesome library! I've discovered that with the current code, I'm unable to query the foreground/background color in terminals using libvte, which includes gnome-terminal and tilix on Debian stable (libvte 0.54.2). It works with urxvt though.
I've looked into the issue, building the examples/hello-world
and running it with strace reveals what's happening (using tilix with a light background color, #fafafa
):
$ cd examples/hello-world
$ go build
$ strace -e read,write -s 999 ./hello-world
[...]
write(1, "\33]11;?\7", 7) = 7
read(1, "\33", 1) = 1
read(1, "]", 1) = 1
read(1, "1", 1) = 1
read(1, "1", 1) = 1
read(1, ";", 1) = 1
read(1, "r", 1) = 1
read(1, "g", 1) = 1
read(1, "b", 1) = 1
read(1, ":", 1) = 1
read(1, "f", 1) = 1
read(1, "a", 1) = 1
read(1, "f", 1) = 1
read(1, "a", 1) = 1
read(1, "/", 1) = 1
read(1, "f", 1) = 1
read(1, "a", 1) = 1
read(1, "f", 1) = 1
read(1, "a", 1) = 1
read(1, "/", 1) = 1
read(1, "f", 1) = 1
read(1, "a", 1) = 1
read(1, "f", 1) = 1
read(1, "a", 1) = 1
read(1, "\33", 1) = 1
read(1, "\\", 1) = 1
write(1, "\n\t\33[1mHas dark background?\33[0m true\n", 36
Has dark background? true
) = 36
+++ exited with 0 +++
We can see here that termenv sends the query to the terminal using a BEL
character as the terminator ("\33]11;?\7"
). Then tilix/libvte responds with the color, terminated by ESC \
, which is rejected by termenv.
On urxvt, the response is terminated by BEL
(with dark background #111111
this time):
$ strace -e read,write -s 999 ./hello-world
[...]
write(1, "\33]11;?\7", 7) = 7
read(1, "\33", 1) = 1
read(1, "]", 1) = 1
read(1, "1", 1) = 1
read(1, "1", 1) = 1
read(1, ";", 1) = 1
read(1, "r", 1) = 1
read(1, "g", 1) = 1
read(1, "b", 1) = 1
read(1, ":", 1) = 1
read(1, "1", 1) = 1
read(1, "1", 1) = 1
read(1, "0", 1) = 1
read(1, "0", 1) = 1
read(1, "/", 1) = 1
read(1, "1", 1) = 1
read(1, "1", 1) = 1
read(1, "0", 1) = 1
read(1, "0", 1) = 1
read(1, "/", 1) = 1
read(1, "1", 1) = 1
read(1, "1", 1) = 1
read(1, "0", 1) = 1
read(1, "0", 1) = 1
read(1, "\7", 1) = 1
[...]
It looks like a bug in libvte 0.54.4. I've also tested this on Fedora 32 using libvte 0.60.3, there it is fixed. I was also able to find a warning in the source code:
if (seq.st() == 7 /* BEL */)
warn("OSC terminated by BEL may be ignored; use ST (ESC \\) instead.");
According to the standard I was able to find, OSC may be terminated by either BEL
or ESC \
(named ST
):
Operating System Commands
OSC Ps ; Pt BEL
OSC Ps ; Pt ST
I suspect that there are many more terminal emulators which just support ESC \
as terminator. It feels to me they all just copied code from xterm at some point...
I can imagine solving this issue in two ways:
- Using
ESC \
instead ofBEL
when querying the colors, checking that the response is also terminated byESC \
. - Using
BEL
for querying the colors, but acceptingBEL
as well asESC \
as the response terminator.
If either solution would be acceptable, I'm willing to submit a PR. Please let me know! :)
Nice find, thank you @fd0!
In that case we should probably accept both the BEL
as well as the ESC \
response terminator. Sounds like that's the most "compatible" approach, if I understand you correctly?
In my opinion, the most compatible approach would be similar to the first one: send ST
(ESC \
) and accept either ESC \
or BEL
. Shall I prepare a PR?
That sounds good, I'd appreciate a PR!
PR is submitted. BTW, I noticed that running the tests interactively (i.e. with a real terminal) fails:
$ go test
--- FAIL: TestTermEnv (0.00s)
termenv_test.go:23: Expected 97, got 38;2;105;113;121
termenv_test.go:30: Expected 48;2;0;0;0, got 48;2;250;250;250
FAIL
exit status 1
FAIL github.com/muesli/termenv 0.003s
Running it through cat
(so that stdout
is not directly connected to the terminal) makes them pass:
$ go test | cat
PASS
ok github.com/muesli/termenv 0.002s
(current master
branch, tested on tilix and alacritty)
Indeed, you're right. I need to figure out a better way to detect the code actually functioning as expected, while still passing on both inside a GitHub Workflow as well as on your average development machine.
Ah, I also found the following in the console_codes(4)
manpage:
It accepts ESC ] (OSC) for the setting of certain resources. In
addition to the ECMA-48 string terminator (ST), xterm(1) accepts a
BEL to terminate an OSC string.
This sounds like ESC \
(ST
) is the ECMA standard, and BEL
is an xterm-specific extension. So now we implemented the correct request.
I'm wondering if there is a way to detect if the current terminal supports querying the fg/bg colors via OSC
before doing that. If we know the terminal supports it, we could just wait for its response, instead of timing out...