]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/pointer.c
merge the C branch into HEAD
[mikachu/openbox.git] / openbox / pointer.c
1 #include "pointer.h"
2 #include "keyboard.h"
3 #include "frame.h"
4 #include "engine.h"
5 #include "openbox.h"
6 #include "hooks.h"
7
8 #include <glib.h>
9 #include <Python.h>
10 #include <structmember.h> /* for PyMemberDef stuff */
11 #ifdef HAVE_STDLIB_H
12 #  include <stdlib.h>
13 #endif
14
15 typedef enum {
16     Action_Press,
17     Action_Release,
18     Action_Click,
19     Action_DoubleClick,
20     Action_Motion,
21     NUM_ACTIONS
22 } Action;
23
24 /* GData of GSList*s of PointerBinding*s. */
25 static GData *bound_contexts;
26 static gboolean grabbed;
27 static int double_click_rate, drag_threshold;
28 PyObject *grab_func;
29
30 struct foreach_grab_temp {
31     Client *client;
32     gboolean grab;
33 };
34
35 typedef struct {
36     guint state;
37     guint button;
38     Action action;
39     char *name;
40     GSList *funcs[NUM_ACTIONS];
41 } PointerBinding;
42
43 /***************************************************************************
44  
45    Define the type 'ButtonData'
46
47  ***************************************************************************/
48
49 typedef struct PointerData {
50     PyObject_HEAD
51     Action action;
52     GQuark context;
53     char *button;
54     guint state;
55     guint buttonnum;
56     int posx, posy;
57     int pressposx, pressposy;
58     int pcareax, pcareay, pcareaw, pcareah;
59 } PointerData;
60
61 staticforward PyTypeObject PointerDataType;
62
63 /***************************************************************************
64  
65    Type methods/struct
66  
67  ***************************************************************************/
68
69 static PyObject *ptrdata_new(char *button, GQuark context, Action action,
70                              guint state, guint buttonnum, int posx, int posy,
71                              int pressposx, int pressposy, int pcareax,
72                              int pcareay, int pcareaw, int pcareah)
73 {
74     PointerData *self = PyObject_New(PointerData, &PointerDataType);
75     self->button = g_strdup(button);
76     self->context = context;
77     self->action = action;
78     self->state = state;
79     self->buttonnum = buttonnum;
80     self->posx = posx;
81     self->posy = posy;
82     self->pressposx = pressposx;
83     self->pressposy = pressposy;
84     self->pcareax = pcareax;
85     self->pcareay = pcareay;
86     self->pcareaw = pcareaw;
87     self->pcareah = pcareah;
88     return (PyObject*) self;
89 }
90
91 static void ptrdata_dealloc(PointerData *self)
92 {
93     g_free(self->button);
94     PyObject_Del((PyObject*)self);
95 }
96
97 static PyObject *ptrdata_getattr(PointerData *self, char *name)
98 {
99     if (!strcmp(name, "button"))
100         return PyString_FromString(self->button);
101     if (!strcmp(name, "action"))
102         return PyInt_FromLong(self->action);
103     if (!strcmp(name, "context"))
104         return PyString_FromString(g_quark_to_string(self->context));
105     if (!strcmp(name, "state"))
106         return PyInt_FromLong(self->state);
107     if (!strcmp(name, "buttonnum"))
108         return PyInt_FromLong(self->buttonnum);
109
110     if (self->action == Action_Motion) { /* the rest are only for motions */
111         if (!strcmp(name, "pos")) {
112             PyObject *pos = PyTuple_New(2);
113             PyTuple_SET_ITEM(pos, 0, PyInt_FromLong(self->posx));
114             PyTuple_SET_ITEM(pos, 1, PyInt_FromLong(self->posy));
115             return pos;
116         }
117         if (!strcmp(name, "presspos")) {
118             PyObject *presspos = PyTuple_New(2);
119             PyTuple_SET_ITEM(presspos, 0, PyInt_FromLong(self->pressposx));
120             PyTuple_SET_ITEM(presspos, 1, PyInt_FromLong(self->pressposy));
121             return presspos;
122         }
123         if (!strcmp(name, "pressclientarea")) {
124             if (self->pcareaw < 0) { /* < 0 indicates no client */
125                 Py_INCREF(Py_None);
126                 return Py_None;
127             } else {
128                 PyObject *ca = PyTuple_New(4);
129                 PyTuple_SET_ITEM(ca, 0, PyInt_FromLong(self->pcareax));
130                 PyTuple_SET_ITEM(ca, 1, PyInt_FromLong(self->pcareay));
131                 PyTuple_SET_ITEM(ca, 2, PyInt_FromLong(self->pcareaw));
132                 PyTuple_SET_ITEM(ca, 3, PyInt_FromLong(self->pcareah));
133                 return ca;
134             }
135         }
136     }
137
138     PyErr_Format(PyExc_AttributeError, "no such attribute '%s'", name);
139     return NULL;
140 }
141
142 static PyTypeObject PointerDataType = {
143     PyObject_HEAD_INIT(NULL)
144     0,
145     "PointerData",
146     sizeof(PointerData),
147     0,
148     (destructor) ptrdata_dealloc,   /*tp_dealloc*/
149     0,                              /*tp_print*/
150     (getattrfunc) ptrdata_getattr,  /*tp_getattr*/
151     0,                              /*tp_setattr*/
152     0,                              /*tp_compare*/
153     0,                              /*tp_repr*/
154     0,                              /*tp_as_number*/
155     0,                              /*tp_as_sequence*/
156     0,                              /*tp_as_mapping*/
157     0,                              /*tp_hash */
158 };
159
160 /***************************************************************************/
161
162 static gboolean translate(char *str, guint *state, guint *button)
163 {
164     char **parsed;
165     char *l;
166     int i;
167     gboolean ret = FALSE;
168
169     parsed = g_strsplit(str, "-", -1);
170     
171     /* first, find the button (last token) */
172     l = NULL;
173     for (i = 0; parsed[i] != NULL; ++i)
174         l = parsed[i];
175     if (l == NULL)
176         goto translation_fail;
177
178     /* figure out the mod mask */
179     *state = 0;
180     for (i = 0; parsed[i] != l; ++i) {
181         guint m = keyboard_translate_modifier(parsed[i]);
182         if (!m) goto translation_fail;
183         *state |= m;
184     }
185
186     /* figure out the button */
187     *button = atoi(l);
188     if (!*button) {
189         g_warning("Invalid button '%s' in pointer binding.", l);
190         goto translation_fail;
191     }
192
193     ret = TRUE;
194
195 translation_fail:
196     g_strfreev(parsed);
197     return ret;
198 }
199
200 static void grab_button(Client *client, guint state, guint button,
201                         GQuark context, gboolean grab)
202 {
203     Window win;
204     int mode = GrabModeAsync;
205     unsigned int mask;
206
207     if (context == g_quark_try_string("frame")) {
208         win = client->frame->window;
209         mask = ButtonPressMask | ButtonMotionMask | ButtonReleaseMask;
210     } else if (context == g_quark_try_string("client")) {
211         win = client->frame->plate;
212         mode = GrabModeSync; /* this is handled in pointer_event */
213         mask = ButtonPressMask; /* can't catch more than this with Sync mode
214                                    the release event is manufactured in
215                                    pointer_fire */
216     } else return;
217
218     if (grab)
219         XGrabButton(ob_display, button, state, win, FALSE, mask, mode,
220                     GrabModeAsync, None, None);
221     else
222         XUngrabButton(ob_display, button, state, win);
223 }
224
225 static void foreach_grab(GQuark key, gpointer data, gpointer user_data)
226 {
227     struct foreach_grab_temp *d = user_data;
228     GSList *it;
229     for (it = data; it != NULL; it = it->next) {
230         PointerBinding *b = it->data;
231         grab_button(d->client, b->state, b->button, key, d->grab);
232     }
233 }
234   
235 void pointer_grab_all(Client *client, gboolean grab)
236 {
237     struct foreach_grab_temp bt;
238     bt.client = client;
239     bt.grab = grab;
240     g_datalist_foreach(&bound_contexts, foreach_grab, &bt);
241 }
242
243 static void grab_all_clients(gboolean grab)
244 {
245     GSList *it;
246
247     for (it = client_list; it != NULL; it = it->next)
248         pointer_grab_all(it->data, grab);
249 }
250
251 static gboolean grab_pointer(gboolean grab)
252 {
253     gboolean ret = TRUE;
254     if (grab)
255         ret = XGrabPointer(ob_display, ob_root, FALSE, (ButtonPressMask |
256                                                         ButtonReleaseMask |
257                                                         ButtonMotionMask |
258                                                         PointerMotionMask),
259                            GrabModeAsync, GrabModeAsync, None, None,
260                            CurrentTime) == GrabSuccess;
261     else
262         XUngrabPointer(ob_display, CurrentTime);
263     if (ret) grabbed = grab;
264     return ret;
265 }
266
267 static void foreach_clear(GQuark key, gpointer data, gpointer user_data)
268 {
269     GSList *it;
270     user_data = user_data;
271     for (it = data; it != NULL; it = it->next) {
272         int i;
273
274         PointerBinding *b = it->data;
275         for (i = 0; i < NUM_ACTIONS; ++i)
276             while (b->funcs[i] != NULL) {
277                 Py_DECREF((PyObject*)b->funcs[i]->data);
278                 b->funcs[i] = g_slist_delete_link(b->funcs[i], b->funcs[i]);
279             }
280         g_free(b->name);
281         g_free(b);
282     }
283     g_slist_free(data);
284 }
285
286 static void clearall()
287 {
288     grab_all_clients(FALSE);
289     g_datalist_foreach(&bound_contexts, foreach_clear, NULL);
290 }
291
292 static void fire_event(char *button, GQuark context, Action action,
293                        guint state, guint buttonnum, int posx, int posy,
294                        int pressposx, int pressposy, int pcareax,
295                        int pcareay, int pcareaw, int pcareah,
296                        PyObject *client, GSList *functions)
297 {
298     PyObject *ptrdata, *args, *ret;
299     GSList *it;
300
301     ptrdata = ptrdata_new(button, context, action,
302                           state, buttonnum, posx, posy, pressposx, pressposy,
303                           pcareax, pcareay, pcareaw, pcareah);
304     args = Py_BuildValue("OO", ptrdata, client);
305
306     if (grabbed) {
307         ret = PyObject_CallObject(grab_func, args);
308         if (ret == NULL) PyErr_Print();
309         Py_XDECREF(ret);
310     } else {
311         for (it = functions; it != NULL; it = it->next) {
312             ret = PyObject_CallObject(it->data, args);
313             if (ret == NULL) PyErr_Print();
314             Py_XDECREF(ret);
315         }
316     }
317
318     Py_DECREF(args);
319     Py_DECREF(ptrdata);
320 }
321
322 void pointer_event(XEvent *e, Client *c)
323 {
324     static guint button = 0, lastbutton = 0;
325     static Time time = 0;
326     static Rect carea;
327     static guint pressx, pressy;
328     GQuark contextq;
329     gboolean click = FALSE, dblclick = FALSE;
330     PyObject *client;
331     GString *str = g_string_sized_new(0);
332     guint state;
333     GSList *it = NULL;
334     PointerBinding *b = NULL;
335
336     contextq = engine_get_context(c, e->xany.window);
337
338     /* pick a button, figure out clicks/double clicks */
339     switch (e->type) {
340     case ButtonPress:
341         if (!button) {
342             button = e->xbutton.button;
343             if (c != NULL) carea = c->frame->area;
344             else carea.width = -1; /* indicates no client */
345             pressx = e->xbutton.x_root;
346             pressy = e->xbutton.y_root;
347         }
348         state = e->xbutton.state;
349         break;
350     case ButtonRelease:
351         state = e->xbutton.state;
352         break;
353     case MotionNotify:
354         state = e->xmotion.state;
355         break;
356     default:
357         g_assert_not_reached();
358         return;
359     }
360
361     if (!grabbed) {
362         for (it = g_datalist_id_get_data(&bound_contexts, contextq);
363              it != NULL; it = it->next) {
364             b = it->data;
365             if (b->state == state && b->button == button)
366                 break;
367         }
368         /* if not grabbed and not bound, then nothing to do! */
369         if (it == NULL) return;
370     }
371
372     if (c) client = clientwrap_new(c);
373     else client = Py_None;
374
375     /* build the button string */
376     if (state & ControlMask) g_string_append(str, "C-");
377     if (state & ShiftMask)   g_string_append(str, "S-");
378     if (state & Mod1Mask)    g_string_append(str, "Mod1-");
379     if (state & Mod2Mask)    g_string_append(str, "Mod2-");
380     if (state & Mod3Mask)    g_string_append(str, "Mod3-");
381     if (state & Mod4Mask)    g_string_append(str, "Mod4-");
382     if (state & Mod5Mask)    g_string_append(str, "Mod5-");
383     g_string_append_printf(str, "%d", button);
384
385     /* figure out clicks/double clicks */
386     switch (e->type) {
387     case ButtonRelease:
388         if (button == e->xbutton.button) {
389             /* determine if this is a valid 'click'. Its not if the release is
390              not over the window, or if a drag occured. */
391             if (ABS(e->xbutton.x_root - pressx) < (unsigned)drag_threshold &&
392                 ABS(e->xbutton.y_root - pressy) < (unsigned)drag_threshold &&
393                 e->xbutton.x >= 0 && e->xbutton.y >= 0) {
394                 int junk;
395                 Window wjunk;
396                 guint ujunk, w, h;
397                 XGetGeometry(ob_display, e->xany.window, &wjunk, &junk, &junk,
398                              &w, &h, &ujunk, &ujunk);
399                 if (e->xbutton.x < (signed)w && e->xbutton.y < (signed)h)
400                     click =TRUE;
401             }
402
403             /* determine if this is a valid 'double-click' */
404             if (click) {
405                 if (lastbutton == button &&
406                     e->xbutton.time - double_click_rate < time) {
407                     dblclick = TRUE;
408                     lastbutton = 0;
409                 } else
410                     lastbutton = button;
411             } else
412                 lastbutton = 0;
413             time = e->xbutton.time;
414             pressx = pressy = 0;
415             button = 0;
416             carea.x = carea.y = carea.width = carea.height = 0;
417         }
418         break;
419     }
420
421     /* fire off the events */
422     switch (e->type) {
423     case ButtonPress:
424         fire_event(str->str, contextq, Action_Press,
425                    state, button, 0, 0, 0, 0, 0, 0, 0, 0,
426                    client, b == NULL ? NULL : b->funcs[Action_Press]);
427         break;
428     case ButtonRelease:
429         fire_event(str->str, contextq, Action_Release,
430                    state, button, 0, 0, 0, 0, 0, 0, 0, 0,
431                    client, b == NULL ? NULL : b->funcs[Action_Release]);
432         break;
433     case MotionNotify:
434         /* watch out for the drag threshold */
435         if (ABS(e->xmotion.x_root - pressx) < (unsigned)drag_threshold &&
436             ABS(e->xmotion.y_root - pressy) < (unsigned)drag_threshold)
437             break;
438         fire_event(str->str, contextq, Action_Motion,
439                    state, button, e->xmotion.x_root,
440                    e->xmotion.y_root, pressx, pressy,
441                    carea.x, carea.y, carea.width, carea.height,
442                    client, b == NULL ? NULL : b->funcs[Action_Motion]);
443         break;
444     }
445
446     if (click)
447         fire_event(str->str, contextq, Action_Click,
448                    state, button, 0, 0, 0, 0, 0, 0, 0, 0,
449                    client, b == NULL ? NULL : b->funcs[Action_Click]);
450     if (dblclick)
451         fire_event(str->str, contextq, Action_DoubleClick,
452                    state, button, 0, 0, 0, 0, 0, 0, 0, 0,
453                    client, b == NULL ? NULL : b->funcs[Action_DoubleClick]);
454
455     g_string_free(str, TRUE);
456     if (client != Py_None) { Py_DECREF(client); }
457
458     if (contextq == g_quark_try_string("client")) {
459         /* Replay the event, so it goes to the client*/
460         XAllowEvents(ob_display, ReplayPointer, CurrentTime);
461         /* generate a release event since we don't get real ones */
462         if (e->type == ButtonPress) {
463             e->type = ButtonRelease;
464             pointer_event(e, c);
465         }
466     }
467 }
468
469 /***************************************************************************
470  
471    Define the type 'Pointer'
472
473  ***************************************************************************/
474
475 #define IS_POINTER(v)  ((v)->ob_type == &PointerType)
476 #define CHECK_POINTER(self, funcname) { \
477     if (!IS_POINTER(self)) { \
478         PyErr_SetString(PyExc_TypeError, \
479                         "descriptor '" funcname "' requires a 'Pointer' " \
480                         "object"); \
481         return NULL; \
482     } \
483 }
484
485 typedef struct Pointer {
486     PyObject_HEAD
487     Action press;
488     Action release;
489     Action click;
490     Action doubleclick;
491     Action motion;
492 } Pointer;
493
494 staticforward PyTypeObject PointerType;
495
496 static PyObject *ptr_bind(Pointer *self, PyObject *args)
497 {
498     char *buttonstr;
499     char *contextstr;
500     guint state, button;
501     PointerBinding *b;
502     GSList *it;
503     GQuark context;
504     PyObject *func;
505     Action action;
506     int i;
507
508     CHECK_POINTER(self, "grab");
509     if (!PyArg_ParseTuple(args, "ssiO:grab",
510                           &buttonstr, &contextstr, &action, &func))
511         return NULL;
512
513     if (!translate(buttonstr, &state, &button)) {
514         PyErr_SetString(PyExc_ValueError, "invalid button");
515         return NULL;
516     }
517
518     context = g_quark_try_string(contextstr);
519     if (!context) {
520         PyErr_SetString(PyExc_ValueError, "invalid context");
521         return NULL;
522     }
523
524     if (action < 0 || action >= NUM_ACTIONS) {
525         PyErr_SetString(PyExc_ValueError, "invalid action");
526         return NULL;
527     }
528
529     if (!PyCallable_Check(func)) {
530         PyErr_SetString(PyExc_ValueError, "expected a callable object");
531         return NULL;
532     }
533
534     for (it = g_datalist_id_get_data(&bound_contexts, context);
535          it != NULL; it = it->next){
536         b = it->data;
537         if (b->state == state && b->button == button) {
538             /* already bound */
539             b->funcs[action] = g_slist_append(b->funcs[action], func);
540             Py_INCREF(Py_None);
541             return Py_None;
542         }
543     }
544
545     grab_all_clients(FALSE);
546
547     /* add the binding */
548     b = g_new(PointerBinding, 1);
549     b->state = state;
550     b->button = button;
551     b->name = g_strdup(buttonstr);
552     for (i = 0; i < NUM_ACTIONS; ++i)
553         if (i != (signed)action) b->funcs[i] = NULL;
554     b->funcs[action] = g_slist_append(NULL, func);
555     g_datalist_id_set_data(&bound_contexts, context, 
556         g_slist_append(g_datalist_id_get_data(&bound_contexts, context), b));
557     grab_all_clients(TRUE);
558
559     Py_INCREF(Py_None);
560     return Py_None;
561 }
562
563 static PyObject *ptr_clearBinds(Pointer *self, PyObject *args)
564 {
565     CHECK_POINTER(self, "clearBinds");
566     if (!PyArg_ParseTuple(args, ":clearBinds"))
567         return NULL;
568     clearall();
569     Py_INCREF(Py_None);
570     return Py_None;
571 }
572
573 static PyObject *ptr_grab(Pointer *self, PyObject *args)
574 {
575     PyObject *func;
576
577     CHECK_POINTER(self, "grab");
578     if (!PyArg_ParseTuple(args, "O:grab", &func))
579         return NULL;
580     if (!PyCallable_Check(func)) {
581         PyErr_SetString(PyExc_ValueError, "expected a callable object");
582         return NULL;
583     }
584     if (!grab_pointer(TRUE)) {
585         PyErr_SetString(PyExc_RuntimeError, "failed to grab pointer");
586         return NULL;
587     }
588     grab_func = func;
589     Py_INCREF(grab_func);
590     Py_INCREF(Py_None);
591     return Py_None;
592 }
593
594 static PyObject *ptr_ungrab(Pointer *self, PyObject *args)
595 {
596     CHECK_POINTER(self, "ungrab");
597     if (!PyArg_ParseTuple(args, ":ungrab"))
598         return NULL;
599     grab_pointer(FALSE);
600     Py_XDECREF(grab_func);
601     grab_func = NULL;
602     Py_INCREF(Py_None);
603     return Py_None;
604 }
605
606 #define METH(n, d) {#n, (PyCFunction)ptr_##n, METH_VARARGS, #d}
607
608 static PyMethodDef PointerMethods[] = {
609     METH(bind,
610          "bind(button, context, func)\n\n"
611          "Binds a pointer button for a context to a function. See the "
612          "Terminology section for a decription and list of common contexts. "
613          "The button is a string which defines a modifier and button "
614          "combination with the format [Modifier-]...[Button]. Modifiers can "
615          "be 'mod1', 'mod2', 'mod3', 'mod4', 'mod5', 'control', and 'shift'. "
616          "The keys on your keyboard that are bound to each of these modifiers "
617          "can be found by running 'xmodmap'. The button is the number of the "
618          "button. Button numbers can be found by running 'xev', pressing the "
619          "button with the pointer over its window, and watching its output. "
620          "Here are some examples of valid buttons: 'control-1', '2', "
621          "'mod1-shift-5'. The func must have a definition similar to "
622          "'def func(keydata, client)'. A button and context may be bound to "
623          "more than one function."),
624     METH(clearBinds,
625          "clearBinds()\n\n"
626          "Removes all bindings that were previously made by bind()."),
627     METH(grab,
628          "grab(func)\n\n"
629          "Grabs the pointer device, causing all possible pointer events to be "
630          "sent to the given function. CAUTION: Be sure when you grab() that "
631          "you also have an ungrab() that will execute, or you will not be "
632          "able to use the pointer device until you restart Openbox. The func "
633          "must have a definition similar to 'def func(keydata)'. The pointer "
634          "cannot be grabbed if it is already grabbed."),
635     METH(ungrab,
636          "ungrab()\n\n"
637          "Ungrabs the pointer. The pointer cannot be ungrabbed if it is not "
638          "grabbed."),
639     { NULL, NULL, 0, NULL }
640 };
641
642 static PyMemberDef PointerMembers[] = {
643     {"Action_Press", T_INT, offsetof(Pointer, press), READONLY,
644      "a pointer button press"},
645     {"Action_Release", T_INT, offsetof(Pointer, release), READONLY,
646      "a pointer button release"},
647     {"Action_Click", T_INT, offsetof(Pointer, click), READONLY,
648      "a pointer button click (press-release)"},
649     {"Action_DoubleClick", T_INT, offsetof(Pointer, doubleclick), READONLY,
650      "a pointer button double-click"},
651     {"Action_Motion", T_INT, offsetof(Pointer, motion), READONLY,
652      "a pointer drag"},
653     {NULL}
654 };
655
656 /***************************************************************************
657  
658    Type methods/struct
659  
660  ***************************************************************************/
661
662 static void ptr_dealloc(PyObject *self)
663 {
664     PyObject_Del(self);
665 }
666
667 static PyTypeObject PointerType = {
668     PyObject_HEAD_INIT(NULL)
669     0,
670     "Pointer",
671     sizeof(Pointer),
672     0,
673     (destructor) ptr_dealloc,       /*tp_dealloc*/
674     0,                              /*tp_print*/
675     0,                              /*tp_getattr*/
676     0,                              /*tp_setattr*/
677     0,                              /*tp_compare*/
678     0,                              /*tp_repr*/
679     0,                              /*tp_as_number*/
680     0,                              /*tp_as_sequence*/
681     0,                              /*tp_as_mapping*/
682     0,                              /*tp_hash */
683 };
684
685 /**************************************************************************/
686
687 void pointer_startup()
688 {
689     PyObject *input, *inputdict;
690     Pointer *ptr;
691
692     grabbed = FALSE;
693     double_click_rate = 300;
694     drag_threshold = 3;
695     g_datalist_init(&bound_contexts);
696
697     PointerType.ob_type = &PyType_Type;
698     PointerType.tp_methods = PointerMethods;
699     PointerType.tp_members = PointerMembers;
700     PyType_Ready(&PointerType);
701     PyType_Ready(&PointerDataType);
702
703     /* get the input module/dict */
704     input = PyImport_ImportModule("input"); /* new */
705     g_assert(input != NULL);
706     inputdict = PyModule_GetDict(input); /* borrowed */
707     g_assert(inputdict != NULL);
708
709     /* add a Pointer instance to the input module */
710     ptr = PyObject_New(Pointer, &PointerType);
711     ptr->press = Action_Press;
712     ptr->release = Action_Release;
713     ptr->click = Action_Click;
714     ptr->doubleclick = Action_DoubleClick;
715     ptr->motion = Action_Motion;
716     PyDict_SetItemString(inputdict, "Pointer", (PyObject*) ptr);
717     Py_DECREF(ptr);
718
719     Py_DECREF(input);
720 }
721
722 void pointer_shutdown()
723 {
724     if (grabbed)
725         grab_pointer(FALSE);
726     clearall();
727     g_datalist_clear(&bound_contexts);
728 }
729