Which window I have focused is a signal to the computer for the state I want it to be in. For instance, I normally leave my speaker muted so, for example, I don't accidentally play sound from a website with unexpected videos. But this means that when I do want sound, I need to manually unmute the sound, even though I've already told the computer that I want to watch Netflix, which always involves turning on the sound.
Of course, for the particular problem of unmuting the sound, adding a keyboard shortcut and rereading xkcd 1205: Is It Worth the Time? probably would have been a more appropriate solution. But I wanted a general solution to the problem.
Then the following script will unmute the speakers if Netflix is
#!/bin/sh x11_watch_active_window.py | while read -r FocusApp do if [ "Netflix - Google Chrome" = "$FocusApp" ] then echo Netflix is focused, unmuting. pactl set-sink-mute 0 0 fi done
Current window using
I was originally using this script which uses
xdotool to get the current window title with the
xdotool getwindowfocus getwindowname. Since that just gets
the current title to turn that into a way to watch for the window focus
changing, we have to run that in a loop, calling
sleep to wait 5
seconds (or some other interval) between each call.
xdotool watch for
xdotool does have the ability to watch for events, including the
xdotool search . behave %@ focus getwindowname | uniq
but with some experimentation it's clear that this attaches and event
listener to all windows that exist when the command is run, and not on
any new windows. The pipe to
uniq is because each focus change
appears to correspond to multiple events, presumably separate unfocus
and focus events but there appear to be two focus events for some
reason. Since unfocus events are included, we can actually do slightly
xdotool search . behave %@ focus\ exec xdotool getwindowfocus getwindowname | uniq
But, of course, that won't get notified of focus changes between two different windows both opened after that command was run.
I found this StackOverflow answer
which uses the
which modern window managers set on the root window with the ID of the
currently focused window. Since it's a property, we can subscribe to a
property change event, and that's just one event instead of watching the
node-x11 to watch that property; the same can be done in the
xprop -spy -root -notype _NET_ACTIVE_WINDOW\ | stdbuf -oL sed 's/^.*# \([^,]*\),.*$/\1/'\ | stdbuf -oL grep -vF 0x0\ | xargs -n1 xdotool getwindowname
I was having issues where it would work without the pipe but stop
outputting if I made the pipeline too long. It turned out the
problem was the pipe was buffering the results waiting for more
text before passing through the output, which doesn't make sense
in this situation. There are multiple workarounds;
stdbuf looked like the most portable choice.
above was a more complete Python solution:
x11_watch_active_window.py. In addition to
watching for active window changes, it also watches for title changes on
the active window, which is useful given what I was actually watching
for was a browser window navigated to a particular page. It also appears
to be written to be robust to various old and broken X11 setups.
My own version just changes the output
to be just the title (as I'm not using the window ID) and sets the
flush option on the
read loop works properly.