merge the C branch into HEAD
[openbox.git] / openbox / keyboard.c
blob87cd5036a26b348a23210476fb4ece5be47d8d6f
1 #include "focus.h"
2 #include "openbox.h"
3 #include "keyboard.h"
4 #include "clientwrap.h"
6 #include <Python.h>
7 #include <glib.h>
8 #ifdef HAVE_STRING_H
9 # include <string.h>
10 #endif
12 typedef struct KeyBindingTree {
13 guint state;
14 guint key;
15 GList *keylist;
16 PyObject *func;
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;
25 static KeyBindingTree *firstnode, *curpos;
26 static guint reset_key, reset_state;
27 static gboolean grabbed, user_grabbed;
28 static PyObject *grab_func;
30 /***************************************************************************
32 Define the type 'KeyboardData'
34 ***************************************************************************/
36 typedef struct KeyboardData {
37 PyObject_HEAD
38 PyObject *keychain;
39 guint state;
40 guint keycode;
41 gboolean press;
42 } KeyboardData;
44 staticforward PyTypeObject KeyboardDataType;
46 /***************************************************************************
48 Type methods/struct
50 ***************************************************************************/
52 static PyObject *keybdata_new(PyObject *keychain, guint state,
53 guint keycode, gboolean press)
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;
64 static void keybdata_dealloc(KeyboardData *self)
66 Py_DECREF(self->keychain);
67 PyObject_Del((PyObject*)self);
70 static PyObject *keybdata_getattr(KeyboardData *self, char *name)
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);
82 PyErr_Format(PyExc_AttributeError, "no such attribute '%s'", name);
83 return NULL;
86 static PyTypeObject KeyboardDataType = {
87 PyObject_HEAD_INIT(NULL)
89 "KeyboardData",
90 sizeof(KeyboardData),
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 */
104 /***************************************************************************/
106 guint keyboard_translate_modifier(char *str)
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;
119 static gboolean translate(char *str, guint *state, guint *keycode)
121 char **parsed;
122 char *l;
123 int i;
124 gboolean ret = FALSE;
125 KeySym sym;
127 parsed = g_strsplit(str, "-", -1);
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;
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;
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;
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;
156 ret = TRUE;
158 translation_fail:
159 g_strfreev(parsed);
160 return ret;
163 static void destroytree(KeyBindingTree *tree)
165 KeyBindingTree *c;
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);
177 g_free(tree);
178 tree = c;
182 static KeyBindingTree *buildtree(GList *keylist)
184 GList *it;
185 KeyBindingTree *ret = NULL, *p;
187 if (g_list_length(keylist) <= 0)
188 return NULL; /* nothing in the list.. */
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;
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);
203 ret->first_child = p;
204 if (!translate(it->data, &ret->state, &ret->key)) {
205 destroytree(ret);
206 return NULL;
209 return ret;
212 static void assimilate(KeyBindingTree *node)
214 KeyBindingTree *a, *b, *tmp, *last;
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;
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);
243 static KeyBindingTree *find(KeyBindingTree *search, gboolean *conflict)
245 KeyBindingTree *a, *b;
247 *conflict = FALSE;
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;
260 } else {
261 *conflict = TRUE;
262 return NULL; /* the chain status' don't match (conflict!) */
264 b = b->first_child;
265 a = a->first_child;
268 return NULL; // it just isn't in here
271 static void grab_keys(gboolean grab)
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;
285 static void reset_chains()
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);
297 void keyboard_event(XKeyEvent *e)
299 PyObject *chain, *client, *args, *keybdata, *ret;
300 gboolean press = e->type == KeyPress;
302 if (focus_client) client = clientwrap_new(focus_client);
303 else client = Py_None;
305 if (user_grabbed) {
306 GString *str = g_string_sized_new(0);
307 KeySym sym;
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-");
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);
335 chain = PyTuple_New(1);
336 PyTuple_SET_ITEM(chain, 0, PyString_FromString(str->str));
337 g_string_free(str, TRUE);
339 keybdata = keybdata_new(chain, e->state, e->keycode, press);
341 args = Py_BuildValue("OO", keybdata, client);
343 ret = PyObject_CallObject(grab_func, args);
344 if (ret == NULL) PyErr_Print();
345 Py_XDECREF(ret);
347 Py_DECREF(args);
348 Py_DECREF(keybdata);
349 Py_DECREF(chain);
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);
373 grabbed = TRUE;
374 curpos = p;
375 XAllowEvents(ob_display, AsyncKeyboard, CurrentTime);
376 } else {
377 GList *it;
378 int i;
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));
386 keybdata = keybdata_new(chain, e->state, e->keycode,
387 press);
389 args = Py_BuildValue("OO", keybdata, client);
391 ret = PyObject_CallObject(p->func, args);
392 if (ret == NULL) PyErr_Print();
393 Py_XDECREF(ret);
395 Py_DECREF(args);
396 Py_DECREF(keybdata);
397 Py_DECREF(chain);
399 XAllowEvents(ob_display, AsyncKeyboard, CurrentTime);
400 reset_chains();
402 break;
404 p = p->next_sibling;
409 if (client != Py_None) { Py_DECREF(client); }
412 static void clearall()
414 grab_keys(FALSE);
415 destroytree(firstnode);
416 firstnode = NULL;
417 grab_keys(TRUE);
420 static gboolean grab_keyboard(gboolean grab)
422 gboolean ret = TRUE;
424 g_message("grab_keyboard(%s). grabbed: %d", (grab?"True":"False"),grabbed);
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);
434 return ret;
437 /***************************************************************************
439 Define the type 'Keyboard'
441 ***************************************************************************/
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; \
453 typedef struct Keyboard {
454 PyObject_HEAD
455 } Keyboard;
457 staticforward PyTypeObject KeyboardType;
459 static PyObject *keyb_bind(Keyboard *self, PyObject *args)
461 KeyBindingTree *tree = NULL, *t;
462 gboolean conflict;
463 PyObject *item, *tuple, *func;
464 GList *keylist = NULL, *it;
465 int i, s;
467 CHECK_KEYBOARD(self, "grab");
468 if (!PyArg_ParseTuple(args, "OO:grab", &tuple, &func))
469 return NULL;
471 if (!PyTuple_Check(tuple)) {
472 PyErr_SetString(PyExc_ValueError, "expected a tuple of strings");
473 goto binderror;
475 if (!PyCallable_Check(func)) {
476 PyErr_SetString(PyExc_ValueError, "expected a callable object");
477 goto binderror;
480 s = PyTuple_GET_SIZE(tuple);
481 if (s <= 0) {
482 PyErr_SetString(PyExc_ValueError, "expected a tuple of strings");
483 goto binderror;
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;
492 keylist = g_list_append(keylist,
493 g_strdup(PyString_AsString(item)));
496 if (!(tree = buildtree(keylist))) {
497 PyErr_SetString(PyExc_ValueError, "invalid binding");
498 goto binderror;
501 t = find(tree, &conflict);
502 if (conflict) {
503 PyErr_SetString(PyExc_ValueError, "conflict with binding");
504 goto binderror;
506 if (t != NULL) {
507 /* already bound to something */
508 PyErr_SetString(PyExc_ValueError, "keychain is already bound");
509 goto binderror;
512 /* grab the server here to make sure no key pressed go missed */
513 XGrabServer(ob_display);
514 XSync(ob_display, FALSE);
516 grab_keys(FALSE);
518 /* set the function */
519 t = tree;
520 while (t->first_child) t = t->first_child;
521 t->func = func;
522 Py_INCREF(func);
524 /* assimilate this built tree into the main tree */
525 assimilate(tree); // assimilation destroys/uses the tree
527 grab_keys(TRUE);
529 XUngrabServer(ob_display);
530 XFlush(ob_display);
532 for (it = keylist; it != NULL; it = it->next)
533 g_free(it->data);
534 g_list_free(it);
536 Py_INCREF(Py_None);
537 return Py_None;
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;
547 static PyObject *keyb_clearBinds(Keyboard *self, PyObject *args)
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;
557 static PyObject *keyb_grab(Keyboard *self, PyObject *args)
559 PyObject *func;
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;
568 if (!grab_keyboard(TRUE)) {
569 PyErr_SetString(PyExc_RuntimeError, "failed to grab keyboard");
570 return NULL;
572 grab_func = func;
573 Py_INCREF(grab_func);
574 Py_INCREF(Py_None);
575 return Py_None;
578 static PyObject *keyb_ungrab(Keyboard *self, PyObject *args)
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;
590 #define METH(n, d) {#n, (PyCFunction)keyb_##n, METH_VARARGS, #d}
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 }
625 /***************************************************************************
627 Type methods/struct
629 ***************************************************************************/
631 static void keyb_dealloc(PyObject *self)
633 PyObject_Del(self);
636 static PyTypeObject KeyboardType = {
637 PyObject_HEAD_INIT(NULL)
639 "Keyboard",
640 sizeof(Keyboard),
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 */
654 /**************************************************************************/
656 void keyboard_startup()
658 PyObject *input, *inputdict, *ptr;
659 gboolean b;
661 curpos = firstnode = NULL;
662 grabbed = user_grabbed = FALSE;
664 b = translate("C-G", &reset_state, &reset_key);
665 g_assert(b);
667 KeyboardType.ob_type = &PyType_Type;
668 KeyboardType.tp_methods = KeyboardMethods;
669 PyType_Ready(&KeyboardType);
670 PyType_Ready(&KeyboardDataType);
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);
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);
683 Py_DECREF(input);
686 void keyboard_shutdown()
688 if (grabbed || user_grabbed) {
689 grabbed = FALSE;
690 grab_keyboard(FALSE);
692 grab_keys(FALSE);
693 destroytree(firstnode);
694 firstnode = NULL;