1

Problem/Goal description

Ideally, I would like a good way of detecting from a shell script whether or not the window has focus. By a "good" way, I mean some way which requires minimal steps and preferably does not require sifting through each open window blindly to find mine based on title.

The purpose is for controlling notifications in many different scripts -- so I'm just looking for a general solution that can apply to any and all of them.

What I've come up with so far is roundabout and hacky -- it is as follows:

  1. Set my title to something unique or mechanically relevant (in my model, it is my PTS path or, more robustly, a UUID). Hope desperately that this title is not overridden by something.

  2. Get a list of all open windows, by title.

  3. Iterate through list to identify my window by matching it to the title element. (Note the possibility of errors here if another window happens to have that same title element.)

  4. Detect whether said window has focus or not.

It should be noted that I do not want to implement this, and will only do it as a last resort. So what I'm asking for here is something that is not this.

Compromises

This solution is obviously terrible, so I'd like to know if there is anything remotely better, in any way. I would prefer something portable, elegant, and perfect, but I recognize the potential need to compromise. By better I mean any of the following:

  1. A solution that only works with a specific terminal emulator, e.g. by having the terminal emulator itself set an environment variable allowing the script to detect which window it is in.

  2. A solution that does not require setting the title, and instead uses some other invisible marker in window state that is accessible and detectable from a shell script attached to said window.

  3. Recusing up the parent process ladder to find the parent terminal emulator PID, and working from there (Note that a solution that works by recusing up the the process tree to detect the parent process that started the script will only work if the script is running locally, so this solution is incomplete but still good!)

Conditions

I was getting questions about exactly what conditions my preferred solution is supposed to function under, and the answer is as many as possible. But at minimum, I would like something that works:

  1. In a single-tab terminal session running natively (default scenario).

  2. In terminal multiplexers like tmux. (Portability between different terminal multiplexers is preferred but really not required.)

Extras that I'd really appreciate (in order of importance), include:

  1. Ability to function on remote connections over telnet and SSH.

  2. Ability to distinguish which tab is open in a multi-tab terminal session.


Summary

I want a good way of finding what terminal emulator window my shell script is attached to, so that I can detect whether it has focus or not.

Note that I'm already aware of the mechanics of how to to iterate through open windows, and how to detect whether they have focus or not and what titles they have. I am aware of the existance of xdotool and xprop and this question is not about the basic mechanics of those tools (unless there is some hidden black magic feature I don't know about that side-steps the intrinsic hackiness of my current solution.)

The reason I don't want to that is because it's terrible. Any other solution that accomplishes the same thing?

  • Use xdotool – Ipor Sircer Nov 6 '18 at 7:45
  • @IporSircer Can you be more specific? – Alexandria P. Nov 6 '18 at 7:52
  • Read the manual: manpages.debian.org/stretch/xdotool/xdotool.1.en.html – Ipor Sircer Nov 6 '18 at 7:55
  • @IporSircer Uh... no offense, but I'm not sure that you understood my question. I know what xdotool does. I've used it before. I don't know what you intend me to do with it to solve the stated problem. – Alexandria P. Nov 6 '18 at 7:56
  • @IporSircer Perhaps I should clarify. I'm aware that you can search for windows with xdotool. I'm not asking for help implementing my hacky solution -- I'm asking for ideas about alternative solutions. – Alexandria P. Nov 6 '18 at 7:59
1
if [ "$(xdotool getwindowfocus)" -eq "$WINDOWID" ]; then
   echo I have the focus
fi

This will not work inside screen/tmux if they were started from somewhere else and just attached in the current window.

  • Cool! I had no idea about $WINDOWIDand that's definitely looking like a useful thing... although you're right that process env var inheritance is definitely a problem... I think I could hack something together to get around that. – Alexandria P. Nov 7 '18 at 4:13
  • Okay, so, as far as I know, there's no non-hacky way for tmux to change environment variables in a shell it connects to -- but I can imagine a way of doing it, which would require hooks into the shell itself that are reading tmux's instructions somehow... Does that already exist? I want to try not to reinvent the wheel, here. – Alexandria P. Nov 7 '18 at 4:18
  • I guess you'll have to live with that limitation; even if tmux had some attach/detach hooks (I vaguely remember that it does), they will have to be installed for each user, which will turn everything into a mess. FWIW, passing of the $WINDOWID envvar is no better or worse than the passing of $DISPLAY or $LANG, and all terminal emulators are setting it. – qubert Nov 7 '18 at 11:35
  • Why is installing them for each user and machine a problem? That's how I handle all my dotfiles. – Alexandria P. Nov 8 '18 at 2:32
2

There's a FocusIn/FocusOut mode. To enable:

echo -ne '\e[?1004h'

To disable:

echo -ne '\e[?1004l'

On each focus event, you receive either \e[I (in) or \e[O (out) from the input stream.

GNOME Terminal (and other VTE based terminals) also report the current state when you enable this mode. That is, you can enable and then immediately disable it for querying the value once.

You can combine read with a timeout, or specifying to read 3 characters to get the response. Note however that it's subject to race condition, e.g. in case you have typed ahead certain characters.

  • Oh, that is awesome! It looks like exactly what I was looking for -- satisfying pretty much every use case. I believe since this is sent as character codes, I could get this to work even for remote connections over SSH... I'm really not sure how to do it with read though -- personally, my first thought upon reading your first paragraph was to pipe straight from the terminal device and watch for characters matching these. I'm testing that out now with cat -v. If there's any traps I haven't thought about with that approach, let me know! – Alexandria P. Nov 7 '18 at 4:03
  • Yeah, so it looks like read requests on terminal devices themselves interfere with terminal operation? Once you request a character it moves to the next character and other things that are reading it don't get to see it. I wonder if there's some way to do it without that. cat definitely won't work. – Alexandria P. Nov 7 '18 at 4:08
  • It's looking like, to get a normal terminal working with this sort of detection in the background, I'd have to have some process in between the each terminal emulator and the shell, as the filling in a pipe sandwich, to intercept those requests and act on them. Correct me if I'm wrong? – Alexandria P. Nov 7 '18 at 4:15
  • I meant the read shell command, see help read, such as echo -ne '\e[?1004h\e[?1004l'; read -N 3 -t 0.5; echo $REPLY. An intermediate layer stripping off the response can provide partial help in case of troubles (e.g. the response arriving later than expected, it at least won't screw up the rest of your script; plus can fix the typeahead case). It's pretty cumbersome, though. Asynchronous escape sequences are unfortunately a pain in the ... to handle robustly, due to their asynchronous nature, but the simple way of handling works most of the time. – egmont Nov 7 '18 at 8:55
  • @AlexandriaP. fwiw, neither this will work inside tmux/screen; they do their own terminal emulation and block the escapes they don't know about. – qubert Nov 7 '18 at 11:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.