#!/usr/bin/python3 import Xlib import Xlib.display from zope import interface from interfaces import desktopEvent from threading import Thread from interfaces.message import IMessage, Debug from zope import component @interface.implementer(desktopEvent.IDesktop) class Listener(Thread): def __init__(self, mapping, queue): Thread.__init__(self) self.queue = queue self.mapping = mapping self.active_window = None self.last_code = None self.running = False def getForegroundWindowTitle(self): """ Return the name of the selected window """ return self.active_window def run(self): disp = Xlib.display.Display() NET_WM_NAME = disp.intern_atom('_NET_WM_NAME') WM_CLASS = disp.intern_atom('WM_CLASS') NET_ACTIVE_WINDOW = disp.intern_atom('_NET_ACTIVE_WINDOW') root = disp.screen().root root.change_attributes(event_mask=Xlib.X.PropertyChangeMask) component.handle(Debug("Waiting for xlib event")) self.running = True while self.running: event = disp.next_event() 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_prop = window.get_full_property(NET_WM_NAME, 0) if window_prop is None: continue window_name = str(window_prop.value).lower() class_name = str(window.get_full_property(WM_CLASS, 0).value).lower() except Xlib.error.XError: continue if window_name is None or window_name == self.active_window: continue self.active_window = window_name found = False for pattern, code in self.mapping.items(): # Ignore the default layout if pattern == "default": continue if not (pattern in window_name or pattern in class_name): continue # We found something. At this point, even if we do not update # the layer (because it is the same as the previous) we do not # switch back to the default layer. found = True if code != self.last_code: component.handle(Debug("Switching to '%s' for '%s'" % (code, window_name))) self.queue.put ( (code, None) ) self.last_code = code break if not found and self.last_code != "default": default = self.mapping.get("default", None) if default is None: continue self.queue.put ( (default, None) ) self.last_code = "default" def stop(self): self.running = False