mackstann / whimsy

A dynamic, hackable window manager written in Python

This URL has Read+Write access

whimsy / whimsy / filters / __init__.py
100644 99 lines (80 sloc) 3.301 kb
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
# Written by Nick Welch in the years 2005-2008. Author disclaims copyright.
 
from Xlib import X, Xutil
from Xlib import error as Xerror
 
from whimsy import util
 
class if_event_type(object):
    'true if the event type is one of *evtypes'
    def __init__(self, *evtypes):
        self.evtypes = evtypes
    def __call__(self, ev, **kw):
        return ev.type in self.evtypes
 
def if_client(wm, ev, **kw):
    'true if the window is an application window managed by the window manager'
    return 'win' in kw and util.window_type(wm, kw['win']) == 'client'
 
def if_root(wm, ev, **kw):
    'true if the window is the root window (desktop/background)'
    return 'win' in kw and util.window_type(wm, kw['win']) == 'root'
 
class if_state(object):
    'true if modifier (shift/control/etc) keys currently match mods'
    def __init__(self, mods):
        self.mods = mods
    def __call__(self, ev, **kw):
        return self.mods.matches(ev.state)
 
class if_(object):
    'convenience class for filtering by event type and/or window type'
    def __init__(self, evtype, wintype=None):
        self.evtype = evtype
        self.wintype = wintype
    def __call__(self, wm, ev, **kw):
        if ev.type != self.evtype or 'win' not in kw:
            return False
        if self.wintype is None:
            return True
        return util.window_type(wm, kw['win']) == self.wintype
 
class click_counter(object):
    """built like an action but yields a filter which is its main purpose -- to
filter for double clicks, triple clicks, etc"""
 
    def __init__(self, pixel_distance=15, timeout_ms=400):
        self.pixel_distance = pixel_distance
        self.timeout_ms = timeout_ms
        self.count = 0
 
    def if_multi(self, desired_count):
        """returns a filter that will tell you whether the current click is a
double click or triple click or whatever you specify"""
        def filt(**kw):
            return self.count == desired_count
        return filt
 
    def __call__(self, ev, **kw):
        """should be called on every button press event, to keep track of fast
successive clicks"""
 
        try:
            prev = self.prev_click
        except:
            is_repeat = False
        else:
            is_repeat = (
                ev.window.id == prev.window.id and
                ev.detail == prev.detail and
                ev.state == prev.state and
                (ev.time - prev.time) <= self.timeout_ms and
                abs(ev.root_x - prev.root_x) <= self.pixel_distance and
                abs(ev.root_y - prev.root_y) <= self.pixel_distance
            )
 
        if is_repeat:
            self.count += 1
        else:
            self.count = 1
 
        self.prev_click = ev
 
# TODO: move these into window_manager, minus the if_; keep if_ versions here
# as wrapper filters
def if_should_manage_existing_window(win, **kw):
    attr = win.get_attributes()
    return (
        not attr.override_redirect
        and attr.map_state == X.IsViewable
        and getattr(win.get_wm_hints(), 'initial_state', not Xutil.NormalState)
            == Xutil.NormalState
    )
 
def if_should_manage_new_window(win, **kw):
    catch = Xerror.CatchError(Xerror.BadWindow)
    return not win.get_attributes().override_redirect and not catch.get_error()