muesli / termenv

Advanced ANSI style & color support for your terminal applications

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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 of BEL when querying the colors, checking that the response is also terminated by ESC \.
  • Using BEL for querying the colors, but accepting BEL as well as ESC \ 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...