I'd like to monitor which window is active on a Linux system running X, and when that active window gets resized or moved. I can monitor the active window (it's held in the _NET_ACTIVE_WINDOW
property on the root window, and I can register for PropertyNotify
events on the root window to discover when that property changes). However, I don't know how to monitor the active window to know whether it gets resized or moved.
import Xlib
import Xlib.display
disp = Xlib.display.Display()
Xroot = disp.screen().root
NET_ACTIVE_WINDOW = disp.intern_atom('_NET_ACTIVE_WINDOW')
Xroot.change_attributes(event_mask=Xlib.X.PropertyChangeMask)
while True:
# loop until an event happens that we care about
# we care about a change to which window is active
# (NET_ACTIVE_WINDOW property changes on the root)
# or about the currently active window changing
# in size or position (don't know how to do this)
event = disp.next_event()
if (event.type == Xlib.X.PropertyNotify and
event.atom == NET_ACTIVE_WINDOW):
active = disp.get_input_focus().focus
try:
name = active.get_wm_class()[1]
except TypeError:
name = "unknown"
print("The active window has changed! It is now", name)
Is there a way to do this? It might involve listening to ConfigureNotify events on the currently active window (and calling change_attributes
on that window when it becomes active to set an appropriate mask), but I can't get this to work.
(Note: I'm not using Gtk, so no Gtk solutions, please.)
Update: there is a rather suspect approach to detecting window resizes, by watching the active window for the value of the _NET_WM_OPAQUE_REGION
property changing (since I correctly receive PropertyChange events, although I don't receive ConfigureNotify events). However, it's not clear that all window managers set this property, and this only changes on a window resize; it does not change on a window move (and nor does any other property).
The way to do this is to select for SubstructureNotifyMask
on the root window, and then read all ConfigureNotify
events and ignore those that are not for the window we care about, thus:
import Xlib
import Xlib.display
disp = Xlib.display.Display()
Xroot = disp.screen().root
NET_ACTIVE_WINDOW = disp.intern_atom('_NET_ACTIVE_WINDOW')
Xroot.change_attributes(event_mask=Xlib.X.PropertyChangeMask |
Xlib.X.SubstructureNotifyMask)
windows = []
while True:
# loop until an event happens that we care about
# we care about a change to which window is active
# (NET_ACTIVE_WINDOW property changes on the root)
# or about the currently active window changing
# in size or position (ConfigureNotify event for
# our window or one of its ancestors)
event = disp.next_event()
if (event.type == Xlib.X.PropertyNotify and
event.atom == NET_ACTIVE_WINDOW):
active = disp.get_input_focus().focus
try:
name = active.get_wm_class()[1]
except TypeError:
name = "unknown"
print("The active window has changed! It is now", name)
# Because an X window is not necessarily just what one thinks of
# as a window (the window manager may add an invisible frame, and
# so on), we record not just the active window but its ancestors
# up to the root, and treat a ConfigureNotify on any of those
# ancestors as meaning that the active window has been moved or resized
pointer = active
windows = []
while pointer.id != Xroot.id:
windows.append(pointer)
pointer = pointer.query_tree().parent
elif event.type == Xlib.X.ConfigureNotify and event.window in windows:
print("Active window size/position is now", event.x, event.y,
event.width, event.height)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With