2014-05-21 4 views
4

Я хочу, чтобы обнаружить имя окна приложения при изменении события фокусировки происходит с питоном Xlib, поэтому на первом этапе я использую этот код:Как «правильно» обнаружить имя приложения при изменении события фокусировки происходит с питоном Xlib

#!/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) 

Но я хочу, правильный путь, то я исследую о XLIB событиях и найти Input Focus Events и написать этот код:

#!/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() 

к сожалению, это не правильно работать, особенно в вкладках на Google Chrome и Firefox, так что есть правильный путь для этой ситуации?

ответ

4

Ваш код почти сразу, но он пропускает две вещи:

  • , а не слушать только изменение фокуса, она также должна слушать окно событий собственности, которые включают в себя изменения WM_NAME собственности, которые также случаются, когда вы в вашем браузере.
  • вместо прослушивания только в корневом окне, он должен слушать каждое окно (которое фокусируется). Вы можете присоединить обработчик событий так же, как и к корневому окну.

Это, как говорится, вот рабочий пример:

#!/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() 
1

@ rr- Как я только что исправил в другом месте, вы хотите, чтобы запросить и текущий _NET_WM_NAME (UTF-8) и наследие WM_NAME (не-UTF8) или по умолчанию конфигурация xterm не вернет название.

Я только что опубликовал полный рабочий пример на your Unix & Linux StackExchange question.

Чтобы избежать отправки людей на перекрестных ссылок охоты, вот копия кода, который я разместил там:

#!/usr/bin/python 
from contextlib import contextmanager 
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') # UTF-8 
WM_NAME = disp.intern_atom('WM_NAME')   # Legacy encoding 

last_seen = { 'xid': None, 'title': None } 

@contextmanager 
def window_obj(win_id): 
    """Simplify dealing with BadWindow (make it either valid or None)""" 
    window_obj = None 
    if win_id: 
     try: 
      window_obj = disp.create_resource_object('window', win_id) 
     except Xlib.error.XError: 
      pass 
    yield window_obj 

def get_active_window(): 
    win_id = root.get_full_property(NET_ACTIVE_WINDOW, 
             Xlib.X.AnyPropertyType).value[0] 

    focus_changed = (win_id != last_seen['xid']) 
    if focus_changed: 
     with window_obj(last_seen['xid']) as old_win: 
      if old_win: 
       old_win.change_attributes(event_mask=Xlib.X.NoEventMask) 

     last_seen['xid'] = win_id 
     with window_obj(win_id) as new_win: 
      if new_win: 
       new_win.change_attributes(event_mask=Xlib.X.PropertyChangeMask) 

    return win_id, focus_changed 

def _get_window_name_inner(win_obj): 
    """Simplify dealing with _NET_WM_NAME (UTF-8) vs. WM_NAME (legacy)""" 
    for atom in (NET_WM_NAME, WM_NAME): 
     try: 
      window_name = win_obj.get_full_property(atom, 0) 
     except UnicodeDecodeError: # Apparently a Debian distro package bug 
      title = "<could not decode characters>" 
     else: 
      if window_name: 
       win_name = window_name.value 
       if isinstance(win_name, bytes): 
        # Apparently COMPOUND_TEXT is so arcane that this is how 
        # tools like xprop deal with receiving it these days 
        win_name = win_name.decode('latin1', 'replace') 
       return win_name 
      else: 
       title = "<unnamed window>" 

    return "{} (XID: {})".format(title, win_obj.id) 

def get_window_name(win_id): 
    if not win_id: 
     last_seen['title'] = "<no window id>" 
     return last_seen['title'] 

    title_changed = False 
    with window_obj(win_id) as wobj: 
     if wobj: 
      win_title = _get_window_name_inner(wobj) 
      title_changed = (win_title != last_seen['title']) 
      last_seen['title'] = win_title 

    return last_seen['title'], title_changed 

def handle_xevent(event): 
    if event.type != Xlib.X.PropertyNotify: 
     return 

    changed = False 
    if event.atom == NET_ACTIVE_WINDOW: 
     if get_active_window()[1]: 
      changed = changed or get_window_name(last_seen['xid'])[1] 
    elif event.atom in (NET_WM_NAME, WM_NAME): 
     changed = changed or get_window_name(last_seen['xid'])[1] 

    if changed: 
     handle_change(last_seen) 

def handle_change(new_state): 
    """Replace this with whatever you want to actually do""" 
    print(new_state) 

if __name__ == '__main__': 
    root.change_attributes(event_mask=Xlib.X.PropertyChangeMask) 

    get_window_name(get_active_window()[0]) 
    handle_change(last_seen) 

    while True: # next_event() sleeps until we get an event 
     handle_xevent(disp.next_event()) 

Там также более прокомментирован версия в this GitHub Gist.