5

I am trying to write a program that tracks when the active window changes using Xlib. I am have trouble figuring out the best way to do this. These are my ideas so far:

  • Every second use _NET_ACTIVE_WINDOW to get the active window and if it has changed then running the related code for the event.
  • Get a list of all windows and listen to their focus in event. I would have to figure out how to keep an up to date list of open windows though.

Is their an easier/better way? I am new to programming with Xlib.

Jim
  • 51
  • 1
  • 2
  • Listening to all focus events plus registering all the handlers for all the windows seems extremely expensive. I wonder if, in fact, listening to PointerMove in general and then calling `XGetInputFocus()` from the event handler to find out who has it when moved would be less overhead. How realtime does this have to be ? If the answer to that is "a sec is fine" then the polling method seems best. – FrankH. Mar 10 '11 at 17:12
  • It doesn't have to be real time, with in a couple of seconds would be okay. I will give my first method a try. Thanks FrankH. – Jim Mar 11 '11 at 03:26

3 Answers3

7

Here's a Python implementation of what @alanc proposed.

import Xlib
import Xlib.display

disp = Xlib.display.Display()
root = disp.screen().root

NET_ACTIVE_WINDOW = disp.intern_atom('_NET_ACTIVE_WINDOW')
NET_WM_NAME = disp.intern_atom('_NET_WM_NAME')

last_seen = {'xid': None}
def get_active_window():
    window_id = root.get_full_property(NET_ACTIVE_WINDOW,
                                       Xlib.X.AnyPropertyType).value[0]

    focus_changed = (window_id != last_seen['xid'])
    last_seen['xid'] = window_id

    return window_id, focus_changed

def get_window_name(window_id):
    try:
        window_obj = disp.create_resource_object('window', window_id)
        window_name = window_obj.get_full_property(NET_WM_NAME, 0).value
    except Xlib.error.XError:
        window_name = None

    return window_name


if __name__ == '__main__':
    root.change_attributes(event_mask=Xlib.X.PropertyChangeMask)
    while True:
        win, changed = get_active_window()
        if changed:
            print(get_window_name(win))

        while True:
            event = disp.next_event()
            if (event.type == Xlib.X.PropertyNotify and
                    event.atom == NET_ACTIVE_WINDOW):
                break

The more fully-commented version I wrote as an example for someone is in this gist.

ssokolow
  • 14,938
  • 7
  • 52
  • 57
3

Can't you just select PropertyChange on the root window to get sent PropertyNotify events when any property is updated and then check each event to see if it was for _NET_ACTIVE_WINDOW?

alanc
  • 4,102
  • 21
  • 24
0

I've been looking for "active windows change event grabber" as well. What I use in mean time (and might be helpful for you) is combination of xdotool and xwininfo.

xwininfo -id "$(xdotool getactivewindow)"
Grzegorz Wierzowiecki
  • 10,545
  • 9
  • 50
  • 88