pythonfocusdetectxlibapplication-name

How to “correctly” detect application name when changing focus event occurs with python xlib


I want to detect applications window name when changing focus event occurs with python xlib, so in the first step I use this code:

#!/usr/bin/env python
#-*- coding:utf-8 -*-
import Xlib.display
import time


display = Xlib.display.Display()
while True:
    window = display.get_input_focus().focus
    wmname = window.get_wm_name()
    wmclass = window.get_wm_class()
    if wmclass is None and wmname is None:
        window = window.query_tree().parent
        wmname = window.get_wm_name()
    print "WM Name: %s" % ( wmname, )
    time.sleep(3)

But I want a correct way, then I research about xlib events and find Input Focus Events and write this code:

#!/usr/bin/env python
#-*- coding:utf-8 -*-
import Xlib.display
from Xlib import X

def main():
    display = Xlib.display.Display(':0')
    root = display.screen().root
    root.change_attributes(event_mask=Xlib.X.FocusChangeMask)

    while True:
        event = root.display.next_event()
        #if event.type == X.FocusIn or event.type == X.FocusOut:
        if event.type == X.FocusOut :
            window = display.get_input_focus().focus
            wmname = window.get_wm_name()
            wmclass = window.get_wm_class()
            if wmclass is None and wmname is None:
                window = window.query_tree().parent
                wmname = window.get_wm_name()
            print "WM Name: %s" % ( wmname, )

if __name__ == "__main__":
    main()

Sadly it's not work correctly especially in tabbed browsing on google chrome and firefox, so Is there a correct way for this situation?


Solution

  • Your code is almost right, but it misses two things:

    That being said, here is a working sample:

    #!/usr/bin/python3
    import Xlib
    import Xlib.display
    
    disp = Xlib.display.Display()
    root = disp.screen().root
    
    NET_WM_NAME = disp.intern_atom('_NET_WM_NAME')
    NET_ACTIVE_WINDOW = disp.intern_atom('_NET_ACTIVE_WINDOW')
    
    root.change_attributes(event_mask=Xlib.X.FocusChangeMask)
    while True:
        try:
            window_id = root.get_full_property(NET_ACTIVE_WINDOW, Xlib.X.AnyPropertyType).value[0]
            window = disp.create_resource_object('window', window_id)
            window.change_attributes(event_mask=Xlib.X.PropertyChangeMask)
            window_name = window.get_full_property(NET_WM_NAME, 0).value
        except Xlib.error.XError: #simplify dealing with BadWindow
            window_name = None
        print(window_name)
        event = disp.next_event()