presses work when clicks dont on the frame around the titlebar..
[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
107
108 static gboolean grab_keyboard(gboolean grab)
109 {
110     gboolean ret = TRUE;
111
112     g_message("grab_keyboard(%s). grabbed: %d", (grab?"True":"False"),grabbed);
113
114     user_grabbed = grab;
115     if (!grabbed) {
116         if (grab)
117             ret = XGrabKeyboard(ob_display, ob_root, 0, GrabModeAsync, 
118                                 GrabModeAsync, CurrentTime) == GrabSuccess;
119         else
120             XUngrabKeyboard(ob_display, CurrentTime);
121     }
122     return ret;
123 }
124
125 /***************************************************************************
126  
127    Define the type 'Keyboard'
128
129  ***************************************************************************/
130
131 #define IS_KEYBOARD(v)  ((v)->ob_type == &KeyboardType)
132 #define CHECK_KEYBOARD(self, funcname) { \
133     if (!IS_KEYBOARD(self)) { \
134         PyErr_SetString(PyExc_TypeError, \
135                         "descriptor '" funcname "' requires a 'Keyboard' " \
136                         "object"); \
137         return NULL; \
138     } \
139 }
140
141 typedef struct Keyboard {
142     PyObject_HEAD
143 } Keyboard;
144
145 staticforward PyTypeObject KeyboardType;
146
147
148 static PyObject *keyb_clearBinds(Keyboard *self, PyObject *args)
149 {
150     CHECK_KEYBOARD(self, "clearBinds");
151     if (!PyArg_ParseTuple(args, ":clearBinds"))
152         return NULL;
153     clearall();
154     Py_INCREF(Py_None);
155     return Py_None;
156 }
157
158 static PyObject *keyb_grab(Keyboard *self, PyObject *args)
159 {
160     PyObject *func;
161
162     CHECK_KEYBOARD(self, "grab");
163     if (!PyArg_ParseTuple(args, "O:grab", &func))
164         return NULL;
165     if (!PyCallable_Check(func)) {
166         PyErr_SetString(PyExc_ValueError, "expected a callable object");
167         return NULL;
168     }
169     if (!grab_keyboard(TRUE)) {
170         PyErr_SetString(PyExc_RuntimeError, "failed to grab keyboard");
171         return NULL;
172     }
173     grab_func = func;
174     Py_INCREF(grab_func);
175     Py_INCREF(Py_None);
176     return Py_None;
177 }
178
179 static PyObject *keyb_ungrab(Keyboard *self, PyObject *args)
180 {
181     CHECK_KEYBOARD(self, "ungrab");
182     if (!PyArg_ParseTuple(args, ":ungrab"))
183         return NULL;
184     grab_keyboard(FALSE);
185     Py_XDECREF(grab_func);
186     grab_func = NULL;
187     Py_INCREF(Py_None);
188     return Py_None;
189 }
190
191 #define METH(n, d) {#n, (PyCFunction)keyb_##n, METH_VARARGS, #d}
192
193 static PyMethodDef KeyboardMethods[] = {
194     METH(bind,
195          "bind(keychain, func)\n\n"
196          "Binds a key-chain to a function. The keychain is a tuple of strings "
197          "which define a chain of key presses. Each member of the tuple has "
198          "the format [Modifier-]...[Key]. Modifiers can be 'mod1', 'mod2', "
199          "'mod3', 'mod4', 'mod5', 'control', and 'shift'. The keys on your "
200          "keyboard that are bound to each of these modifiers can be found by "
201          "running 'xmodmap'. The Key can be any valid key definition. Key "
202          "definitions can be found by running 'xev', pressing the key while "
203          "its window is focused, and watching its output. Here are some "
204          "examples of valid keychains: ('a'), ('F7'), ('control-a', 'd'), "
205          "('control-mod1-x', 'control-mod4-g'), ('F1', 'space'). The func "
206          "must have a definition similar to 'def func(keydata, client)'. A "
207          "keychain cannot be bound to more than one function."),
208     METH(clearBinds,
209          "clearBinds()\n\n"
210          "Removes all bindings that were previously made by bind()."),
211     METH(grab,
212          "grab(func)\n\n"
213          "Grabs the entire keyboard, causing all possible keyboard events to "
214          "be passed to the given function. CAUTION: Be sure when you grab() "
215          "that you also have an ungrab() that will execute, or you will not "
216          "be able to type until you restart Openbox. The func must have a "
217          "definition similar to 'def func(keydata)'. The keyboard cannot be "
218          "grabbed if it is already grabbed."),
219     METH(ungrab,
220          "ungrab()\n\n"
221          "Ungrabs the keyboard. The keyboard cannot be ungrabbed if it is not "
222          "grabbed."),
223     { NULL, NULL, 0, NULL }
224 };
225
226 /***************************************************************************
227  
228    Type methods/struct
229  
230  ***************************************************************************/
231
232 static void keyb_dealloc(PyObject *self)
233 {
234     PyObject_Del(self);
235 }
236
237 static PyTypeObject KeyboardType = {
238     PyObject_HEAD_INIT(NULL)
239     0,
240     "Keyboard",
241     sizeof(Keyboard),
242     0,
243     (destructor) keyb_dealloc,      /*tp_dealloc*/
244     0,                              /*tp_print*/
245     0,                              /*tp_getattr*/
246     0,                              /*tp_setattr*/
247     0,                              /*tp_compare*/
248     0,                              /*tp_repr*/
249     0,                              /*tp_as_number*/
250     0,                              /*tp_as_sequence*/
251     0,                              /*tp_as_mapping*/
252     0,                              /*tp_hash */
253 };
254
255 /**************************************************************************/
256
257 void keyboard_startup()
258 {
259     PyObject *input, *inputdict, *ptr;
260     gboolean b;
261
262     curpos = firstnode = NULL;
263     grabbed = user_grabbed = FALSE;
264
265     b = translate("C-G", &reset_state, &reset_key);
266     g_assert(b);
267
268     KeyboardType.ob_type = &PyType_Type;
269     KeyboardType.tp_methods = KeyboardMethods;
270     PyType_Ready(&KeyboardType);
271     PyType_Ready(&KeyboardDataType);
272
273     /* get the input module/dict */
274     input = PyImport_ImportModule("input"); /* new */
275     g_assert(input != NULL);
276     inputdict = PyModule_GetDict(input); /* borrowed */
277     g_assert(inputdict != NULL);
278
279     /* add a Keyboard instance to the input module */
280     ptr = (PyObject*) PyObject_New(Keyboard, &KeyboardType);
281     PyDict_SetItemString(inputdict, "Keyboard", ptr);
282     Py_DECREF(ptr);
283
284     Py_DECREF(input);
285 }
286
287 void keyboard_shutdown()
288 {
289     if (grabbed || user_grabbed) {
290         grabbed = FALSE;
291         grab_keyboard(FALSE);
292     }
293     grab_keys(FALSE);
294     destroytree(firstnode);
295     firstnode = NULL;
296 }
297