4 #include "clientwrap.h"
12 typedef struct KeyBindingTree
{
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
;
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
{
44 staticforward PyTypeObject KeyboardDataType
;
46 /***************************************************************************
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
;
59 data
->keycode
= keycode
;
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
);
86 static PyTypeObject KeyboardDataType
= {
87 PyObject_HEAD_INIT(NULL
)
92 (destructor
) keybdata_dealloc
, /*tp_dealloc*/
94 (getattrfunc
) keybdata_getattr
, /*tp_getattr*/
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
);
119 static gboolean
translate(char *str
, guint
*state
, guint
*keycode
)
124 gboolean ret
= FALSE
;
127 parsed
= g_strsplit(str
, "-", -1);
129 /* first, find the key (last token) */
131 for (i
= 0; parsed
[i
] != NULL
; ++i
)
134 goto translation_fail
;
136 /* figure out the mod mask */
138 for (i
= 0; parsed
[i
] != l
; ++i
) {
139 guint m
= keyboard_translate_modifier(parsed
[i
]);
140 if (!m
) goto translation_fail
;
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
);
152 g_warning("Key '%s' does not exist on the display.", l
);
153 goto translation_fail
;
163 static void destroytree(KeyBindingTree
*tree
)
168 destroytree(tree
->next_sibling
);
169 c
= tree
->first_child
;
172 for (it
= tree
->keylist
; it
!= NULL
; it
= it
->next
)
174 g_list_free(tree
->keylist
);
175 Py_XDECREF(tree
->func
);
182 static KeyBindingTree
*buildtree(GList
*keylist
)
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
) {
192 ret
= g_new(KeyBindingTree
, 1);
193 ret
->next_sibling
= NULL
;
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
)) {
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 */
225 if (!(a
->state
== b
->state
&& a
->key
== b
->key
)) {
234 if (!(last
->state
== b
->state
&& last
->key
== b
->key
))
235 last
->next_sibling
= b
;
237 last
->first_child
= b
->first_child
;
243 static KeyBindingTree
*find(KeyBindingTree
*search
, gboolean
*conflict
)
245 KeyBindingTree
*a
, *b
;
252 if (!(a
->state
== b
->state
&& a
->key
== b
->key
)) {
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) */
262 return NULL
; /* the chain status' don't match (conflict!) */
268 return NULL
; // it just isn't in here
271 static void grab_keys(gboolean grab
)
274 XUngrabKey(ob_display
, AnyKey
, AnyModifier
, ob_root
);
276 KeyBindingTree
*p
= firstnode
;
278 XGrabKey(ob_display
, p
->key
, p
->state
, ob_root
, FALSE
,
279 GrabModeAsync
, GrabModeSync
);
285 static void reset_chains()
291 g_message("reset chains. user_grabbed: %d", 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
;
306 GString
*str
= g_string_sized_new(0);
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);
327 g_string_append(str
, "NoSymbol");
329 char *name
= XKeysymToString(sym
);
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();
353 if (e
->keycode
== reset_key
&& e
->state
== reset_state
) {
355 XAllowEvents(ob_display
, AsyncKeyboard
, CurrentTime
);
361 p
= curpos
->first_child
;
363 if (p
->key
== e
->keycode
&& p
->state
== e
->state
) {
364 if (p
->first_child
!= NULL
) { /* part of a chain */
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
,
375 XAllowEvents(ob_display
, AsyncKeyboard
, CurrentTime
);
380 chain
= PyTuple_New(g_list_length(p
->keylist
));
381 for (i
= 0, it
= p
->keylist
; it
!= NULL
;
383 PyTuple_SET_ITEM(chain
, i
,
384 PyString_FromString(it
->data
));
386 keybdata
= keybdata_new(chain
, e
->state
, e
->keycode
,
389 args
= Py_BuildValue("OO", keybdata
, client
);
391 ret
= PyObject_CallObject(p
->func
, args
);
392 if (ret
== NULL
) PyErr_Print();
399 XAllowEvents(ob_display
, AsyncKeyboard
, CurrentTime
);
409 if (client
!= Py_None
) { Py_DECREF(client
); }
412 static void clearall()
415 destroytree(firstnode
);
420 static gboolean
grab_keyboard(gboolean grab
)
424 g_message("grab_keyboard(%s). grabbed: %d", (grab
?"True":"False"),grabbed
);
429 ret
= XGrabKeyboard(ob_display
, ob_root
, 0, GrabModeAsync
,
430 GrabModeAsync
, CurrentTime
) == GrabSuccess
;
432 XUngrabKeyboard(ob_display
, CurrentTime
);
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' " \
453 typedef struct Keyboard
{
457 staticforward PyTypeObject KeyboardType
;
459 static PyObject
*keyb_bind(Keyboard
*self
, PyObject
*args
)
461 KeyBindingTree
*tree
= NULL
, *t
;
463 PyObject
*item
, *tuple
, *func
;
464 GList
*keylist
= NULL
, *it
;
467 CHECK_KEYBOARD(self
, "grab");
468 if (!PyArg_ParseTuple(args
, "OO:grab", &tuple
, &func
))
471 if (!PyTuple_Check(tuple
)) {
472 PyErr_SetString(PyExc_ValueError
, "expected a tuple of strings");
475 if (!PyCallable_Check(func
)) {
476 PyErr_SetString(PyExc_ValueError
, "expected a callable object");
480 s
= PyTuple_GET_SIZE(tuple
);
482 PyErr_SetString(PyExc_ValueError
, "expected a tuple of strings");
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");
492 keylist
= g_list_append(keylist
,
493 g_strdup(PyString_AsString(item
)));
496 if (!(tree
= buildtree(keylist
))) {
497 PyErr_SetString(PyExc_ValueError
, "invalid binding");
501 t
= find(tree
, &conflict
);
503 PyErr_SetString(PyExc_ValueError
, "conflict with binding");
507 /* already bound to something */
508 PyErr_SetString(PyExc_ValueError
, "keychain is already bound");
512 /* grab the server here to make sure no key pressed go missed */
513 XGrabServer(ob_display
);
514 XSync(ob_display
, FALSE
);
518 /* set the function */
520 while (t
->first_child
) t
= t
->first_child
;
524 /* assimilate this built tree into the main tree */
525 assimilate(tree
); // assimilation destroys/uses the tree
529 XUngrabServer(ob_display
);
532 for (it
= keylist
; it
!= NULL
; it
= it
->next
)
540 if (tree
!= NULL
) destroytree(tree
);
541 for (it
= keylist
; it
!= NULL
; it
= it
->next
)
547 static PyObject
*keyb_clearBinds(Keyboard
*self
, PyObject
*args
)
549 CHECK_KEYBOARD(self
, "clearBinds");
550 if (!PyArg_ParseTuple(args
, ":clearBinds"))
557 static PyObject
*keyb_grab(Keyboard
*self
, PyObject
*args
)
561 CHECK_KEYBOARD(self
, "grab");
562 if (!PyArg_ParseTuple(args
, "O:grab", &func
))
564 if (!PyCallable_Check(func
)) {
565 PyErr_SetString(PyExc_ValueError
, "expected a callable object");
568 if (!grab_keyboard(TRUE
)) {
569 PyErr_SetString(PyExc_RuntimeError
, "failed to grab keyboard");
573 Py_INCREF(grab_func
);
578 static PyObject
*keyb_ungrab(Keyboard
*self
, PyObject
*args
)
580 CHECK_KEYBOARD(self
, "ungrab");
581 if (!PyArg_ParseTuple(args
, ":ungrab"))
583 grab_keyboard(FALSE
);
584 Py_XDECREF(grab_func
);
590 #define METH(n, d) {#n, (PyCFunction)keyb_##n, METH_VARARGS, #d}
592 static PyMethodDef KeyboardMethods
[] = {
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."),
609 "Removes all bindings that were previously made by bind()."),
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."),
620 "Ungrabs the keyboard. The keyboard cannot be ungrabbed if it is not "
622 { NULL
, NULL
, 0, NULL
}
625 /***************************************************************************
629 ***************************************************************************/
631 static void keyb_dealloc(PyObject
*self
)
636 static PyTypeObject KeyboardType
= {
637 PyObject_HEAD_INIT(NULL
)
642 (destructor
) keyb_dealloc
, /*tp_dealloc*/
649 0, /*tp_as_sequence*/
654 /**************************************************************************/
656 void keyboard_startup()
658 PyObject
*input
, *inputdict
, *ptr
;
661 curpos
= firstnode
= NULL
;
662 grabbed
= user_grabbed
= FALSE
;
664 b
= translate("C-G", &reset_state
, &reset_key
);
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
);
686 void keyboard_shutdown()
688 if (grabbed
|| user_grabbed
) {
690 grab_keyboard(FALSE
);
693 destroytree(firstnode
);