~ubuntu-branches/ubuntu/karmic/parti-all/karmic

« back to all changes in this revision

Viewing changes to wimpiggy/keys.py

  • Committer: Bazaar Package Importer
  • Author(s): Evan Dandrea
  • Date: 2009-06-02 12:44:00 UTC
  • Revision ID: james.westby@ubuntu.com-20090602124400-xbkgh9vxjciey732
Tags: upstream-0.0.6
ImportĀ upstreamĀ versionĀ 0.0.6

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# This file is part of Parti.
 
2
# Copyright (C) 2008, 2009 Nathaniel Smith <njs@pobox.com>
 
3
# Parti is released under the terms of the GNU GPL v2, or, at your option, any
 
4
# later version. See the file COPYING for details.
 
5
 
 
6
import gobject
 
7
import gtk
 
8
from wimpiggy.util import one_arg_signal
 
9
from wimpiggy.error import *
 
10
from wimpiggy.lowlevel import (get_display_for,
 
11
                               get_modifier_map, grab_key, ungrab_all_keys,
 
12
                               add_event_receiver, remove_event_receiver)
 
13
 
 
14
from wimpiggy.log import Logger
 
15
log = Logger()
 
16
 
 
17
class HotkeyManager(gobject.GObject):
 
18
    __gsignals__ = {
 
19
        "hotkey": (gobject.SIGNAL_RUN_LAST | gobject.SIGNAL_DETAILED,
 
20
                   gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
 
21
 
 
22
        "wimpiggy-key-press-event": one_arg_signal,
 
23
        }
 
24
 
 
25
    def __init__(self, window):
 
26
        gobject.GObject.__init__(self)
 
27
        self.window = window
 
28
        self.hotkeys = {}
 
29
 
 
30
        disp = get_display_for(self.window)
 
31
        self.keymap = gtk.gdk.keymap_get_for_display(disp)
 
32
        self.keymap_id = self.keymap.connect("keys-changed",
 
33
                                             self._keys_changed)
 
34
        self._keys_changed()
 
35
 
 
36
        add_event_receiver(self.window, self)
 
37
 
 
38
    def destroy(self):
 
39
        self.keymap.disconnect(self.keymap_id)
 
40
        self.keymap = None
 
41
        self.keymap_id = None
 
42
 
 
43
        trap.swallow(self.unbind_all)
 
44
        remove_event_receiver(self.window, self)
 
45
        self.window = None
 
46
 
 
47
    def _keys_changed(self, *args):
 
48
        self.modifier_map = grok_modifier_map(self.window)
 
49
        self.nuisances = set()
 
50
        for i in range(256):
 
51
            if not(i & ~self.modifier_map["nuisance"]):
 
52
                self.nuisances.add(i)
 
53
        trap.swallow(self._rebind)
 
54
 
 
55
    def _rebind(self, *args):
 
56
        try:
 
57
            gtk.gdk.x11_grab_server()
 
58
            self._unbind_all()
 
59
            self._bind_all()
 
60
        finally:
 
61
            gtk.gdk.x11_ungrab_server()
 
62
 
 
63
    def _unbind_all(self):
 
64
        ungrab_all_keys(self.window)
 
65
 
 
66
    def _bind_all(self):
 
67
        self.normalized_hotkeys = {}
 
68
        for hotkey, target in self.hotkeys.iteritems():
 
69
            modifier_mask, keycodes = parse_key(hotkey, self.keymap,
 
70
                                                self.modifier_map)
 
71
            for keycode in keycodes:
 
72
                # Claim a passive grab on all the different forms of this key
 
73
                for nuisance_mask in self.nuisances:
 
74
                    grab_key(self.window, keycode,
 
75
                             modifier_mask | nuisance_mask)
 
76
                # Save off the normalized form to make it easy to lookup later
 
77
                # when we see the key appear
 
78
                unparsed = unparse_key(modifier_mask, keycode,
 
79
                                       self.keymap, self.modifier_map)
 
80
                self.normalized_hotkeys[unparsed] = target
 
81
 
 
82
    def do_wimpiggy_key_press_event(self, event):
 
83
        log("got hotkey event, maybe")
 
84
        unparsed = unparse_key(event.state, event.hardware_keycode,
 
85
                               self.keymap, self.modifier_map)
 
86
        log("unparsed = %s", unparsed)
 
87
        if unparsed in self.normalized_hotkeys:
 
88
            target = self.normalized_hotkeys[unparsed]
 
89
            self.emit("hotkey::%s" % (target,), target)
 
90
 
 
91
    def add_hotkeys(self, hotkeys):
 
92
        self.hotkeys.update(hotkeys)
 
93
        self._rebind()
 
94
 
 
95
    def del_hotkeys(self, keys):
 
96
        for k in keys:
 
97
            if k in self.hotkeys:
 
98
                del self.hotkeys[k]
 
99
        self._rebind()
 
100
 
 
101
gobject.type_register(HotkeyManager)
 
102
 
 
103
 
 
104
def grok_modifier_map(display_source):
 
105
    """Return an dict mapping modifier names to corresponding X modifier
 
106
    bitmasks."""
 
107
    modifier_map = {
 
108
        "shift": 1 << 0,
 
109
        "lock": 1 << 1,
 
110
        "control": 1 << 2,
 
111
        "mod1": 1 << 3,
 
112
        "mod2": 1 << 4,
 
113
        "mod3": 1 << 5,
 
114
        "mod4": 1 << 6,
 
115
        "mod5": 1 << 7,
 
116
        "scroll": 0,
 
117
        "num": 0,
 
118
        "meta": 0,
 
119
        "super": 0,
 
120
        "hyper": 0,
 
121
        "alt": 0,
 
122
        }
 
123
    meanings = {
 
124
        "Scroll_Lock": "scroll",
 
125
        "Num_Lock": "num",
 
126
        "Meta_L": "meta",
 
127
        "Meta_R": "meta",
 
128
        "Super_L": "super",
 
129
        "Super_R": "super",
 
130
        "Hyper_L": "hyper",
 
131
        "Hyper_R": "hyper",
 
132
        "Alt_L": "alt",
 
133
        "Alt_R": "alt",
 
134
        }
 
135
 
 
136
    disp = get_display_for(display_source)
 
137
    (max_keypermod, keycodes) = get_modifier_map(disp)
 
138
    assert len(keycodes) == 8 * max_keypermod
 
139
    keymap = gtk.gdk.keymap_get_for_display(disp)
 
140
    for i in range(8):
 
141
        for j in range(max_keypermod):
 
142
            keycode = keycodes[i * max_keypermod + j]
 
143
            if keycode:
 
144
                entries = keymap.get_entries_for_keycode(keycode)
 
145
                if entries is None:
 
146
                    # This keycode has no entry in the keymap:
 
147
                    continue
 
148
                for (keyval, _, _, _) in entries:
 
149
                    keyval_name = gtk.gdk.keyval_name(keyval)
 
150
                    if keyval_name in meanings:
 
151
                        modifier_map[meanings[keyval_name]] |= (1 << i)
 
152
    modifier_map["nuisance"] = (modifier_map["lock"]
 
153
                                | modifier_map["scroll"]
 
154
                                | modifier_map["num"])
 
155
    return modifier_map
 
156
 
 
157
def parse_key(name, keymap, modifier_map):
 
158
    modifier_mask = 0
 
159
    name = name.strip().lower()
 
160
    while name.startswith("<"):
 
161
        ket = name.index(">")
 
162
        modifier_name = name[1:ket]
 
163
        extra_mask = modifier_map[modifier_name]
 
164
        assert extra_mask
 
165
        modifier_mask |= extra_mask
 
166
        name = name[ket+1:]
 
167
    keycodes = []
 
168
    try:
 
169
        keycodes.append(int(name))
 
170
    except ValueError:
 
171
        keyval = gtk.gdk.keyval_from_name(name)
 
172
        entries = keymap.get_entries_for_keyval(keyval)
 
173
        for entry in entries:
 
174
            keycodes.append(entry[0])
 
175
    modifier_mask &= ~modifier_map["nuisance"]
 
176
    return (modifier_mask, keycodes)
 
177
 
 
178
def unparse_key(modifier_mask, keycode, keymap, modifier_map):
 
179
    name = None
 
180
    modifier_mask &= ~modifier_map["nuisance"]
 
181
 
 
182
    keyval_entries = keymap.get_entries_for_keycode(keycode)
 
183
    if keyval_entries is not None:
 
184
        for keyval_entry in keyval_entries:
 
185
            if keyval_entry[0]:
 
186
                name = gtk.gdk.keyval_name(keyval_entry[0])
 
187
                break
 
188
    if name is None:
 
189
        name = str(keycode)
 
190
 
 
191
    mods = modifier_map.keys()
 
192
    def sort_modn_to_end(a, b):
 
193
        a = (a.startswith("mod"), a)
 
194
        b = (b.startswith("mod"), b)
 
195
        return cmp(a, b)
 
196
    mods.sort(sort_modn_to_end)
 
197
    for mod in mods:
 
198
        mask = modifier_map[mod]
 
199
        if mask & modifier_mask:
 
200
            name = "<%s>%s" % (mod, name)
 
201
            modifier_mask &= ~mask
 
202
    return name