]> icculus.org git repositories - dana/openbox.git/blob - openbox/keyboard.c
merge the C branch into HEAD
[dana/openbox.git] / 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