2

Here is some shell code extracted from https://github.com/rocky/bash-term-background to get terminal background colors. I'd like to mimic this behavior in Python so it can retrieve the values too:

stty -echo
# Issue command to get both foreground and
# background color
#            fg       bg
echo -ne '\e]10;?\a\e]11;?\a'
IFS=: read -t 0.1 -d $'\a' x fg
IFS=: read -t 0.1 -d $'\a' x bg
stty echo
# RGB values are in $fg and $bg

I can translate most of this, but the part I'm having problem with is echo -ne '\e]10;?\a\e]11;?\a'.

I would think that:

output = subprocess.check_output("echo -ne '\033]10;?\07\033]11;?\07'", shell=True)

would be a reasonable translation in Python 2.7, but I am not getting any output. Run in bash in an Xterm-compatible terminal gives:

rgb:e5e5e5/e5e5e6
rgb:000000/000000

But in python I am not seeing anything.

Update: As Mark Setchell suggests perhaps part of the problem is running in a subprocess. So when I change the python code to:

 print(check_output(["echo", "-ne" "'\033]10;?\07\033]11;?07'"]))

I now see the RGB values output, but only after the program terminates. So this suggests the problem is hooking up to see that output which I guess xterm is sending asynchronously.

2nd Update: based on meuh's code I've placed a fuller version of this in https://github.com/rocky/python-term-background

rocky
  • 7,226
  • 3
  • 33
  • 74
  • 1
    Are you then starting another, entirely separate, subprocess on the next line to read the output? – Mark Setchell Aug 01 '17 at 06:37
  • The `shell=True` suggests yes. When I remove that parameter I now see the output from xterm. but not captured in a variable. So possibly all I need to do is either redirect stdout before hand and wait on that, or figure out how to hook up reading directly from the terminal. I have revised the question to include this new important piece of information. – rocky Aug 01 '17 at 10:41

1 Answers1

3

You need to just write the escape sequence to stdout and read the response on stdin after setting it to raw mode:

#!/usr/bin/python3
import os, select, sys, time, termios, tty

fp = sys.stdin
fd = fp.fileno()

if os.isatty(fd):
    old_settings = termios.tcgetattr(fd)
    tty.setraw(fd)
    print('\033]10;?\07\033]11;?\07')
    time.sleep(0.01)
    r, w, e = select.select([ fp ], [], [], 0)
    if fp in r:
        data = fp.read(48)
    else:
        data = None
        print("no input available")
    termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
    if data:
        print("got "+repr(data)+"\n")
else:
    print("Not a tty")
rocky
  • 7,226
  • 3
  • 33
  • 74
meuh
  • 11,500
  • 2
  • 29
  • 45
  • Thanks. This is close. There are two easily fixed problems that I have suggested in an edit. And one I am not sure I know how to fix though. First, we should check to see if stdin is a tty. Second, the read should have a timeout on it. The last problem though is that on a non-xterm-compatible terminal you see the escape string echoed, and I don't want that. – rocky Aug 02 '17 at 18:47
  • Hmm. Maybe I need something like the POSIX shell `stty -echo` command? – rocky Aug 02 '17 at 18:53
  • I think `setraw()` already clears the echo bit. You could look for the `TERM` value and only accept those you know like `xterm`. – meuh Aug 02 '17 at 18:58
  • I think another problem, is that one can't assume that just because stdin is a tty that is true for stdout, so that needs checking separately. Also, I see that I can probably hide the output by adding backspace characters equal to the length of the string. That along with checking for a TERM variable may be the best one can expect. If no one has any better ideas, in a day I'll accept this. – rocky Aug 02 '17 at 19:18
  • This works great but I'd love to have more explanation of why we have to use all these low-level methods and it can't be done with subprocess run/popen (I guess it can't, I couldn't find a recipe that worked). And why does `print('\033]10;?\07\033]11;?\07')` work but `sys.stdout.write(...)` doesn't...? I just wish I understood why. – Anentropic Jan 30 '22 at 21:12
  • 1
    hmm, I think there are two reasons 1) the response we're trying to capture comes _after_ the `echo` command exits, so if that echo is our subproc we never see the query response 2) the response is sent to _stdin_ rather than stdout, and `Popen.stdin` doesn't support reading, only writing – Anentropic Jan 31 '22 at 10:50