aboutsummaryrefslogtreecommitdiff
path: root/xlib.py
blob: 051b21f54d1bd31ad0386be488abd7e27839bc93 (plain)
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
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):
        self.disp = Xlib.display.Display()
        root = self.disp.screen().root
        root.change_attributes(event_mask=Xlib.X.PropertyChangeMask)

        self.NET_WM_NAME = self.disp.intern_atom('_NET_WM_NAME')
        self.WM_CLASS = self.disp.intern_atom('WM_CLASS')
        self.NET_ACTIVE_WINDOW = self.disp.intern_atom('_NET_ACTIVE_WINDOW')
        component.handle(Debug("Waiting for xlib event"))
        self.running = True
        while self.running:

            try:
                self.handle_event(root, self.disp.next_event())
            except Exception as e:
                component.handle(Debug( str(e) ))
                print(e)

    def handle_event(self, root, event):
        try:
            window_id_property = root.get_full_property(self.NET_ACTIVE_WINDOW, Xlib.X.AnyPropertyType)
            if window_id_property is None:
                return
            window_id = window_id_property.value[0]
            window = self.disp.create_resource_object('window', window_id)
            window_prop = window.get_full_property(self.NET_WM_NAME, 0)
            if window_prop is None:
                return
            window_name = str(window_prop.value).lower()
            class_name = str(window.get_full_property(self.WM_CLASS, 0).value).lower()
        except Xlib.error.XError:
            return

        if window_name is None or window_name == self.active_window:
            return

        self.active_window = window_name
        found = False

        # Create a reveverse dictionnary for getting the users added first
        # In python, the elements in a dictionnary are sorted by insertion
        # order, so we get the user added first here
        for pattern in reversed(self.mapping.keys()):
            # Ignore the default layout
            if pattern == "default":
                continue
            if not (pattern.lstrip() in window_name or pattern.lstrip() in class_name):
                continue
            code = self.mapping[pattern]
            # 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
            # We found a matching configuration. Even if the match is the
            # same as the current one, we break the loop in order to
            # prevent another layer to update.
            break
        if not found and self.last_code != "default":
            default = self.mapping.get("default", None)
            if default is None:
                return

            self.queue.put ( (default, None) )
            self.last_code = "default"


    def stop(self):
        self.running = False