1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
#!/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'" % (pattern, 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
|