]> Dogcows Code - chaz/openbox/blob - openbox/keyboard.c
merge the C branch into HEAD
[chaz/openbox] / openbox / keyboard.c
1 #include "focus.h"
2 #include "openbox.h"
3 #include "keyboard.h"
4 #include "clientwrap.h"
5
6 #include <Python.h>
7 #include <glib.h>
8 #ifdef HAVE_STRING_H
9 # include <string.h>
10 #endif
11
12 typedef struct KeyBindingTree {
13 guint state;
14 guint key;
15 GList *keylist;
16 PyObject *func;
17
18 /* the next binding in the tree at the same level */
19 struct KeyBindingTree *next_sibling;
20 /* the first child of this binding (next binding in a chained sequence).*/
21 struct KeyBindingTree *first_child;
22 } KeyBindingTree;
23
24
25 static KeyBindingTree *firstnode, *curpos;
26 static guint reset_key, reset_state;
27 static gboolean grabbed, user_grabbed;
28 static PyObject *grab_func;
29
30 /***************************************************************************
31
32 Define the type 'KeyboardData'
33
34 ***************************************************************************/
35
36 typedef struct KeyboardData {
37 PyObject_HEAD
38 PyObject *keychain;
39 guint state;
40 guint keycode;
41 gboolean press;
42 } KeyboardData;
43
44 staticforward PyTypeObject KeyboardDataType;
45
46 /***************************************************************************
47
48 Type methods/struct
49
50 ***************************************************************************/
51
52 static PyObject *keybdata_new(PyObject *keychain, guint state,
53 guint keycode, gboolean press)
54 {
55 KeyboardData *data = PyObject_New(KeyboardData, &KeyboardDataType);
56 data->keychain = keychain;
57 Py_INCREF(keychain);
58 data->state = state;
59 data->keycode = keycode;
60 data->press = press;
61 return (PyObject*) data;
62 }
63
64 static void keybdata_dealloc(KeyboardData *self)
65 {
66 Py_DECREF(self->keychain);
67 PyObject_Del((PyObject*)self);
68 }
69
70 static PyObject *keybdata_getattr(KeyboardData *self, char *name)
71 {
72 if (!strcmp(name, "keychain")) {
73 Py_INCREF(self->keychain);
74 return self->keychain;
75 } else if (!strcmp(name, "state"))
76 return PyInt_FromLong(self->state);
77 else if (!strcmp(name, "keycode"))
78 return PyInt_FromLong(self->keycode);
79 else if (!strcmp(name, "press"))
80 return PyInt_FromLong(!!self->press);
81
82 PyErr_Format(PyExc_AttributeError, "no such attribute '%s'", name);
83 return NULL;
84 }
85
86 static PyTypeObject KeyboardDataType = {
87 PyObject_HEAD_INIT(NULL)
88 0,
89 "KeyboardData",
90 sizeof(KeyboardData),
91 0,
92 (destructor) keybdata_dealloc, /*tp_dealloc*/
93 0, /*tp_print*/
94 (getattrfunc) keybdata_getattr, /*tp_getattr*/
95 0, /*tp_setattr*/
96 0, /*tp_compare*/
97 0, /*tp_repr*/
98 0, /*tp_as_number*/
99 0, /*tp_as_sequence*/
100 0, /*tp_as_mapping*/
101 0, /*tp_hash */
102 };
103
104 /***************************************************************************/
105
106 guint keyboard_translate_modifier(char *str)
107 {
108 if (!strcmp("Mod1", str)) return Mod1Mask;
109 else if (!strcmp("Mod2", str)) return Mod2Mask;
110 else if (!strcmp("Mod3", str)) return Mod3Mask;
111 else if (!strcmp("Mod4", str)) return Mod4Mask;
112 else if (!strcmp("Mod5", str)) return Mod5Mask;
113 else if (!strcmp("C", str)) return ControlMask;
114 else if (!strcmp("S", str)) return ShiftMask;
115 g_warning("Invalid modifier '%s' in binding.", str);
116 return 0;
117 }
118
119 static gboolean translate(char *str, guint *state, guint *keycode)
120 {
121 char **parsed;
122 char *l;
123 int i;
124 gboolean ret = FALSE;
125 KeySym sym;
126
127 parsed = g_strsplit(str, "-", -1);
128
129 /* first, find the key (last token) */
130 l = NULL;
131 for (i = 0; parsed[i] != NULL; ++i)
132 l = parsed[i];
133 if (l == NULL)
134 goto translation_fail;
135
136 /* figure out the mod mask */
137 *state = 0;
138 for (i = 0; parsed[i] != l; ++i) {
139 guint m = keyboard_translate_modifier(parsed[i]);
140 if (!m) goto translation_fail;
141 *state |= m;
142 }
143
144 /* figure out the keycode */
145 sym = XStringToKeysym(l);
146 if (sym == NoSymbol) {
147 g_warning("Invalid key name '%s' in key binding.", l);
148 goto translation_fail;
149 }
150 *keycode = XKeysymToKeycode(ob_display, sym);
151 if (!keycode) {
152 g_warning("Key '%s' does not exist on the display.", l);
153 goto translation_fail;
154 }
155
156 ret = TRUE;
157
158 translation_fail:
159 g_strfreev(parsed);
160 return ret;
161 }
162
163 static void destroytree(KeyBindingTree *tree)
164 {
165 KeyBindingTree *c;
166
167 while (tree) {
168 destroytree(tree->next_sibling);
169 c = tree->first_child;
170 if (c == NULL) {
171 GList *it;
172 for (it = tree->keylist; it != NULL; it = it->next)
173 g_free(it->data);
174 g_list_free(tree->keylist);
175 Py_XDECREF(tree->func);
176 }
177 g_free(tree);
178 tree = c;
179 }
180 }
181
182 static KeyBindingTree *buildtree(GList *keylist)
183 {
184 GList *it;
185 KeyBindingTree *ret = NULL, *p;
186
187 if (g_list_length(keylist) <= 0)
188 return NULL; /* nothing in the list.. */
189
190 for (it = g_list_last(keylist); it != NULL; it = it->prev) {
191 p = ret;
192 ret = g_new(KeyBindingTree, 1);
193 ret->next_sibling = NULL;
194 ret->func = NULL;
195 if (p == NULL) {
196 GList *it;
197
198 /* this is the first built node, the bottom node of the tree */
199 ret->keylist = g_list_copy(keylist); /* shallow copy */
200 for (it = ret->keylist; it != NULL; it = it->next) /* deep copy */
201 it->data = g_strdup(it->data);
202 }
203 ret->first_child = p;
204 if (!translate(it->data, &ret->state, &ret->key)) {
205 destroytree(ret);
206 return NULL;
207 }
208 }
209 return ret;
210 }
211
212 static void assimilate(KeyBindingTree *node)
213 {
214 KeyBindingTree *a, *b, *tmp, *last;
215
216 if (firstnode == NULL) {
217 /* there are no nodes at this level yet */
218 firstnode = node;
219 } else {
220 a = firstnode;
221 last = a;
222 b = node;
223 while (a) {
224 last = a;
225 if (!(a->state == b->state && a->key == b->key)) {
226 a = a->next_sibling;
227 } else {
228 tmp = b;
229 b = b->first_child;
230 g_free(tmp);
231 a = a->first_child;
232 }
233 }
234 if (!(last->state == b->state && last->key == b->key))
235 last->next_sibling = b;
236 else {
237 last->first_child = b->first_child;
238 g_free(b);
239 }
240 }
241 }
242
243 static KeyBindingTree *find(KeyBindingTree *search, gboolean *conflict)
244 {
245 KeyBindingTree *a, *b;
246
247 *conflict = FALSE;
248
249 a = firstnode;
250 b = search;
251 while (a && b) {
252 if (!(a->state == b->state && a->key == b->key)) {
253 a = a->next_sibling;
254 } else {
255 if ((a->first_child == NULL) == (b->first_child == NULL)) {
256 if (a->first_child == NULL) {
257 /* found it! (return the actual node, not the search's) */
258 return a;
259 }
260 } else {
261 *conflict = TRUE;
262 return NULL; /* the chain status' don't match (conflict!) */
263 }
264 b = b->first_child;
265 a = a->first_child;
266 }
267 }
268 return NULL; // it just isn't in here
269 }
270
271 static void grab_keys(gboolean grab)
272 {
273 if (!grab) {
274 XUngrabKey(ob_display, AnyKey, AnyModifier, ob_root);
275 } else {
276 KeyBindingTree *p = firstnode;
277 while (p) {
278 XGrabKey(ob_display, p->key, p->state, ob_root, FALSE,
279 GrabModeAsync, GrabModeSync);
280 p = p->next_sibling;
281 }
282 }
283 }
284
285 static void reset_chains()
286 {
287 /* XXX kill timer */
288 curpos = NULL;
289 if (grabbed) {
290 grabbed = FALSE;
291 g_message("reset chains. user_grabbed: %d", user_grabbed);
292 if (!user_grabbed)
293 XUngrabKeyboard(ob_display, CurrentTime);
294 }
295 }
296
297 void keyboard_event(XKeyEvent *e)
298 {
299 PyObject *chain, *client, *args, *keybdata, *ret;
300 gboolean press = e->type == KeyPress;
301
302 if (focus_client) client = clientwrap_new(focus_client);
303 else client = Py_None;
304
305 if (user_grabbed) {
306 GString *str = g_string_sized_new(0);
307 KeySym sym;
308
309 /* build the 'chain' */
310 if (e->state & ControlMask)
311 g_string_append(str, "C-");
312 if (e->state & ShiftMask)
313 g_string_append(str, "S-");
314 if (e->state & Mod1Mask)
315 g_string_append(str, "Mod1-");
316 if (e->state & Mod2Mask)
317 g_string_append(str, "Mod2-");
318 if (e->state & Mod3Mask)
319 g_string_append(str, "Mod3-");
320 if (e->state & Mod4Mask)
321 g_string_append(str, "Mod4-");
322 if (e->state & Mod5Mask)
323 g_string_append(str, "Mod5-");
324
325 sym = XKeycodeToKeysym(ob_display, e->keycode, 0);
326 if (sym == NoSymbol)
327 g_string_append(str, "NoSymbol");
328 else {
329 char *name = XKeysymToString(sym);
330 if (name == NULL)
331 name = "Undefined";
332 g_string_append(str, name);
333 }
334
335 chain = PyTuple_New(1);
336 PyTuple_SET_ITEM(chain, 0, PyString_FromString(str->str));
337 g_string_free(str, TRUE);
338
339 keybdata = keybdata_new(chain, e->state, e->keycode, press);
340
341 args = Py_BuildValue("OO", keybdata, client);
342
343 ret = PyObject_CallObject(grab_func, args);
344 if (ret == NULL) PyErr_Print();
345 Py_XDECREF(ret);
346
347 Py_DECREF(args);
348 Py_DECREF(keybdata);
349 Py_DECREF(chain);
350 }
351
352 if (press) {
353 if (e->keycode == reset_key && e->state == reset_state) {
354 reset_chains();
355 XAllowEvents(ob_display, AsyncKeyboard, CurrentTime);
356 } else {
357 KeyBindingTree *p;
358 if (curpos == NULL)
359 p = firstnode;
360 else
361 p = curpos->first_child;
362 while (p) {
363 if (p->key == e->keycode && p->state == e->state) {
364 if (p->first_child != NULL) { /* part of a chain */
365 /* XXX TIMER */
366 if (!grabbed && !user_grabbed) {
367 /*grab should never fail because we should have a
368 sync grab at this point */
369 XGrabKeyboard(ob_display, ob_root, 0,
370 GrabModeAsync, GrabModeSync,
371 CurrentTime);
372 }
373 grabbed = TRUE;
374 curpos = p;
375 XAllowEvents(ob_display, AsyncKeyboard, CurrentTime);
376 } else {
377 GList *it;
378 int i;
379
380 chain = PyTuple_New(g_list_length(p->keylist));
381 for (i = 0, it = p->keylist; it != NULL;
382 it = it->next, ++i)
383 PyTuple_SET_ITEM(chain, i,
384 PyString_FromString(it->data));
385
386 keybdata = keybdata_new(chain, e->state, e->keycode,
387 press);
388
389 args = Py_BuildValue("OO", keybdata, client);
390
391 ret = PyObject_CallObject(p->func, args);
392 if (ret == NULL) PyErr_Print();
393 Py_XDECREF(ret);
394
395 Py_DECREF(args);
396 Py_DECREF(keybdata);
397 Py_DECREF(chain);
398
399 XAllowEvents(ob_display, AsyncKeyboard, CurrentTime);
400 reset_chains();
401 }
402 break;
403 }
404 p = p->next_sibling;
405 }
406 }
407 }
408
409 if (client != Py_None) { Py_DECREF(client); }
410 }
411
412 static void clearall()
413 {
414 grab_keys(FALSE);
415 destroytree(firstnode);
416 firstnode = NULL;
417 grab_keys(TRUE);
418 }
419
420 static gboolean grab_keyboard(gboolean grab)
421 {
422 gboolean ret = TRUE;
423
424 g_message("grab_keyboard(%s). grabbed: %d", (grab?"True":"False"),grabbed);
425
426 user_grabbed = grab;
427 if (!grabbed) {
428 if (grab)
429 ret = XGrabKeyboard(ob_display, ob_root, 0, GrabModeAsync,
430 GrabModeAsync, CurrentTime) == GrabSuccess;
431 else
432 XUngrabKeyboard(ob_display, CurrentTime);
433 }
434 return ret;
435 }
436
437 /***************************************************************************
438
439 Define the type 'Keyboard'
440
441 ***************************************************************************/
442
443 #define IS_KEYBOARD(v) ((v)->ob_type == &KeyboardType)
444 #define CHECK_KEYBOARD(self, funcname) { \
445 if (!IS_KEYBOARD(self)) { \
446 PyErr_SetString(PyExc_TypeError, \
447 "descriptor '" funcname "' requires a 'Keyboard' " \
448 "object"); \
449 return NULL; \
450 } \
451 }
452
453 typedef struct Keyboard {
454 PyObject_HEAD
455 } Keyboard;
456
457 staticforward PyTypeObject KeyboardType;
458
459 static PyObject *keyb_bind(Keyboard *self, PyObject *args)
460 {
461 KeyBindingTree *tree = NULL, *t;
462 gboolean conflict;
463 PyObject *item, *tuple, *func;
464 GList *keylist = NULL, *it;
465 int i, s;
466
467 CHECK_KEYBOARD(self, "grab");
468 if (!PyArg_ParseTuple(args, "OO:grab", &tuple, &func))
469 return NULL;
470
471 if (!PyTuple_Check(tuple)) {
472 PyErr_SetString(PyExc_ValueError, "expected a tuple of strings");
473 goto binderror;
474 }
475 if (!PyCallable_Check(func)) {
476 PyErr_SetString(PyExc_ValueError, "expected a callable object");
477 goto binderror;
478 }
479
480 s = PyTuple_GET_SIZE(tuple);
481 if (s <= 0) {
482 PyErr_SetString(PyExc_ValueError, "expected a tuple of strings");
483 goto binderror;
484 }
485
486 for (i = 0; i < s; ++i) {
487 item = PyTuple_GET_ITEM(tuple, i);
488 if (!PyString_Check(item)) {
489 PyErr_SetString(PyExc_ValueError, "expected a tuple of strings");
490 goto binderror;
491 }
492 keylist = g_list_append(keylist,
493 g_strdup(PyString_AsString(item)));
494 }
495
496 if (!(tree = buildtree(keylist))) {
497 PyErr_SetString(PyExc_ValueError, "invalid binding");
498 goto binderror;
499 }
500
501 t = find(tree, &conflict);
502 if (conflict) {
503 PyErr_SetString(PyExc_ValueError, "conflict with binding");
504 goto binderror;
505 }
506 if (t != NULL) {
507 /* already bound to something */
508 PyErr_SetString(PyExc_ValueError, "keychain is already bound");
509 goto binderror;
510 }
511
512 /* grab the server here to make sure no key pressed go missed */
513 XGrabServer(ob_display);
514 XSync(ob_display, FALSE);
515
516 grab_keys(FALSE);
517
518 /* set the function */
519 t = tree;
520 while (t->first_child) t = t->first_child;
521 t->func = func;
522 Py_INCREF(func);
523
524 /* assimilate this built tree into the main tree */
525 assimilate(tree); // assimilation destroys/uses the tree
526
527 grab_keys(TRUE);
528
529 XUngrabServer(ob_display);
530 XFlush(ob_display);
531
532 for (it = keylist; it != NULL; it = it->next)
533 g_free(it->data);
534 g_list_free(it);
535
536 Py_INCREF(Py_None);
537 return Py_None;
538
539 binderror:
540 if (tree != NULL) destroytree(tree);
541 for (it = keylist; it != NULL; it = it->next)
542 g_free(it->data);
543 g_list_free(it);
544 return NULL;
545 }
546
547 static PyObject *keyb_clearBinds(Keyboard *self, PyObject *args)
548 {
549 CHECK_KEYBOARD(self, "clearBinds");
550 if (!PyArg_ParseTuple(args, ":clearBinds"))
551 return NULL;
552 clearall();
553 Py_INCREF(Py_None);
554 return Py_None;
555 }
556
557 static PyObject *keyb_grab(Keyboard *self, PyObject *args)
558 {
559 PyObject *func;
560
561 CHECK_KEYBOARD(self, "grab");
562 if (!PyArg_ParseTuple(args, "O:grab", &func))
563 return NULL;
564 if (!PyCallable_Check(func)) {
565 PyErr_SetString(PyExc_ValueError, "expected a callable object");
566 return NULL;
567 }
568 if (!grab_keyboard(TRUE)) {
569 PyErr_SetString(PyExc_RuntimeError, "failed to grab keyboard");
570 return NULL;
571 }
572 grab_func = func;
573 Py_INCREF(grab_func);
574 Py_INCREF(Py_None);
575 return Py_None;
576 }
577
578 static PyObject *keyb_ungrab(Keyboard *self, PyObject *args)
579 {
580 CHECK_KEYBOARD(self, "ungrab");
581 if (!PyArg_ParseTuple(args, ":ungrab"))
582 return NULL;
583 grab_keyboard(FALSE);
584 Py_XDECREF(grab_func);
585 grab_func = NULL;
586 Py_INCREF(Py_None);
587 return Py_None;
588 }
589
590 #define METH(n, d) {#n, (PyCFunction)keyb_##n, METH_VARARGS, #d}
591
592 static PyMethodDef KeyboardMethods[] = {
593 METH(bind,
594 "bind(keychain, func)\n\n"
595 "Binds a key-chain to a function. The keychain is a tuple of strings "
596 "which define a chain of key presses. Each member of the tuple has "
597 "the format [Modifier-]...[Key]. Modifiers can be 'mod1', 'mod2', "
598 "'mod3', 'mod4', 'mod5', 'control', and 'shift'. The keys on your "
599 "keyboard that are bound to each of these modifiers can be found by "
600 "running 'xmodmap'. The Key can be any valid key definition. Key "
601 "definitions can be found by running 'xev', pressing the key while "
602 "its window is focused, and watching its output. Here are some "
603 "examples of valid keychains: ('a'), ('F7'), ('control-a', 'd'), "
604 "('control-mod1-x', 'control-mod4-g'), ('F1', 'space'). The func "
605 "must have a definition similar to 'def func(keydata, client)'. A "
606 "keychain cannot be bound to more than one function."),
607 METH(clearBinds,
608 "clearBinds()\n\n"
609 "Removes all bindings that were previously made by bind()."),
610 METH(grab,
611 "grab(func)\n\n"
612 "Grabs the entire keyboard, causing all possible keyboard events to "
613 "be passed to the given function. CAUTION: Be sure when you grab() "
614 "that you also have an ungrab() that will execute, or you will not "
615 "be able to type until you restart Openbox. The func must have a "
616 "definition similar to 'def func(keydata)'. The keyboard cannot be "
617 "grabbed if it is already grabbed."),
618 METH(ungrab,
619 "ungrab()\n\n"
620 "Ungrabs the keyboard. The keyboard cannot be ungrabbed if it is not "
621 "grabbed."),
622 { NULL, NULL, 0, NULL }
623 };
624
625 /***************************************************************************
626
627 Type methods/struct
628
629 ***************************************************************************/
630
631 static void keyb_dealloc(PyObject *self)
632 {
633 PyObject_Del(self);
634 }
635
636 static PyTypeObject KeyboardType = {
637 PyObject_HEAD_INIT(NULL)
638 0,
639 "Keyboard",
640 sizeof(Keyboard),
641 0,
642 (destructor) keyb_dealloc, /*tp_dealloc*/
643 0, /*tp_print*/
644 0, /*tp_getattr*/
645 0, /*tp_setattr*/
646 0, /*tp_compare*/
647 0, /*tp_repr*/
648 0, /*tp_as_number*/
649 0, /*tp_as_sequence*/
650 0, /*tp_as_mapping*/
651 0, /*tp_hash */
652 };
653
654 /**************************************************************************/
655
656 void keyboard_startup()
657 {
658 PyObject *input, *inputdict, *ptr;
659 gboolean b;
660
661 curpos = firstnode = NULL;
662 grabbed = user_grabbed = FALSE;
663
664 b = translate("C-G", &reset_state, &reset_key);
665 g_assert(b);
666
667 KeyboardType.ob_type = &PyType_Type;
668 KeyboardType.tp_methods = KeyboardMethods;
669 PyType_Ready(&KeyboardType);
670 PyType_Ready(&KeyboardDataType);
671
672 /* get the input module/dict */
673 input = PyImport_ImportModule("input"); /* new */
674 g_assert(input != NULL);
675 inputdict = PyModule_GetDict(input); /* borrowed */
676 g_assert(inputdict != NULL);
677
678 /* add a Keyboard instance to the input module */
679 ptr = (PyObject*) PyObject_New(Keyboard, &KeyboardType);
680 PyDict_SetItemString(inputdict, "Keyboard", ptr);
681 Py_DECREF(ptr);
682
683 Py_DECREF(input);
684 }
685
686 void keyboard_shutdown()
687 {
688 if (grabbed || user_grabbed) {
689 grabbed = FALSE;
690 grab_keyboard(FALSE);
691 }
692 grab_keys(FALSE);
693 destroytree(firstnode);
694 firstnode = NULL;
695 }
696
This page took 0.067261 seconds and 5 git commands to generate.