]> Dogcows Code - chaz/openbox/blob - openbox/pointer.c
make the ~/.openbox dir on startup
[chaz/openbox] / 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 #include "configwrap.h"
8
9 #include <glib.h>
10 #include <Python.h>
11 #include <structmember.h> /* for PyMemberDef stuff */
12 #ifdef HAVE_STDLIB_H
13 # include <stdlib.h>
14 #endif
15
16 typedef enum {
17 Action_Press,
18 Action_Release,
19 Action_Click,
20 Action_DoubleClick,
21 Action_Motion,
22 NUM_ACTIONS
23 } Action;
24
25 /* GData of GSList*s of PointerBinding*s. */
26 static GData *bound_contexts;
27 static gboolean grabbed;
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 guint drag_threshold;
336
337 drag_threshold = configwrap_get_int("input", "drag_threshold");
338
339 contextq = engine_get_context(c, e->xany.window);
340
341 /* pick a button, figure out clicks/double clicks */
342 switch (e->type) {
343 case ButtonPress:
344 if (!button) {
345 button = e->xbutton.button;
346 if (c != NULL) carea = c->frame->area;
347 else carea.width = -1; /* indicates no client */
348 pressx = e->xbutton.x_root;
349 pressy = e->xbutton.y_root;
350 }
351 state = e->xbutton.state;
352 break;
353 case ButtonRelease:
354 state = e->xbutton.state;
355 break;
356 case MotionNotify:
357 state = e->xmotion.state;
358 break;
359 default:
360 g_assert_not_reached();
361 return;
362 }
363
364 if (!grabbed) {
365 for (it = g_datalist_id_get_data(&bound_contexts, contextq);
366 it != NULL; it = it->next) {
367 b = it->data;
368 if (b->state == state && b->button == button)
369 break;
370 }
371 /* if not grabbed and not bound, then nothing to do! */
372 if (it == NULL) return;
373 }
374
375 if (c) client = clientwrap_new(c);
376 else client = Py_None;
377
378 /* build the button string */
379 if (state & ControlMask) g_string_append(str, "C-");
380 if (state & ShiftMask) g_string_append(str, "S-");
381 if (state & Mod1Mask) g_string_append(str, "Mod1-");
382 if (state & Mod2Mask) g_string_append(str, "Mod2-");
383 if (state & Mod3Mask) g_string_append(str, "Mod3-");
384 if (state & Mod4Mask) g_string_append(str, "Mod4-");
385 if (state & Mod5Mask) g_string_append(str, "Mod5-");
386 g_string_append_printf(str, "%d", button);
387
388 /* figure out clicks/double clicks */
389 switch (e->type) {
390 case ButtonRelease:
391 if (button == e->xbutton.button) {
392 /* determine if this is a valid 'click'. Its not if the release is
393 not over the window, or if a drag occured. */
394 if (ABS(e->xbutton.x_root - pressx) < drag_threshold &&
395 ABS(e->xbutton.y_root - pressy) < drag_threshold &&
396 e->xbutton.x >= 0 && e->xbutton.y >= 0) {
397 int junk;
398 Window wjunk;
399 guint ujunk, w, h;
400 XGetGeometry(ob_display, e->xany.window, &wjunk, &junk, &junk,
401 &w, &h, &ujunk, &ujunk);
402 if (e->xbutton.x < (signed)w && e->xbutton.y < (signed)h)
403 click =TRUE;
404 }
405
406 /* determine if this is a valid 'double-click' */
407 if (click) {
408 if (lastbutton == button &&
409 e->xbutton.time -
410 configwrap_get_int("input", "double_click_rate") < time) {
411 dblclick = TRUE;
412 lastbutton = 0;
413 } else
414 lastbutton = button;
415 } else
416 lastbutton = 0;
417 time = e->xbutton.time;
418 pressx = pressy = 0;
419 button = 0;
420 carea.x = carea.y = carea.width = carea.height = 0;
421 }
422 break;
423 }
424
425 /* fire off the events */
426 switch (e->type) {
427 case ButtonPress:
428 fire_event(str->str, contextq, Action_Press,
429 state, button, 0, 0, 0, 0, 0, 0, 0, 0,
430 client, b == NULL ? NULL : b->funcs[Action_Press]);
431 break;
432 case ButtonRelease:
433 fire_event(str->str, contextq, Action_Release,
434 state, button, 0, 0, 0, 0, 0, 0, 0, 0,
435 client, b == NULL ? NULL : b->funcs[Action_Release]);
436 break;
437 case MotionNotify:
438 /* watch out for the drag threshold */
439 if (ABS(e->xmotion.x_root - pressx) < drag_threshold &&
440 ABS(e->xmotion.y_root - pressy) < drag_threshold)
441 break;
442 fire_event(str->str, contextq, Action_Motion,
443 state, button, e->xmotion.x_root,
444 e->xmotion.y_root, pressx, pressy,
445 carea.x, carea.y, carea.width, carea.height,
446 client, b == NULL ? NULL : b->funcs[Action_Motion]);
447 break;
448 }
449
450 if (click)
451 fire_event(str->str, contextq, Action_Click,
452 state, button, 0, 0, 0, 0, 0, 0, 0, 0,
453 client, b == NULL ? NULL : b->funcs[Action_Click]);
454 if (dblclick)
455 fire_event(str->str, contextq, Action_DoubleClick,
456 state, button, 0, 0, 0, 0, 0, 0, 0, 0,
457 client, b == NULL ? NULL : b->funcs[Action_DoubleClick]);
458
459 g_string_free(str, TRUE);
460 if (client != Py_None) { Py_DECREF(client); }
461
462 if (contextq == g_quark_try_string("client")) {
463 /* Replay the event, so it goes to the client*/
464 XAllowEvents(ob_display, ReplayPointer, CurrentTime);
465 /* generate a release event since we don't get real ones */
466 if (e->type == ButtonPress) {
467 e->type = ButtonRelease;
468 pointer_event(e, c);
469 }
470 }
471 }
472
473 /***************************************************************************
474
475 Define the type 'Pointer'
476
477 ***************************************************************************/
478
479 #define IS_POINTER(v) ((v)->ob_type == &PointerType)
480 #define CHECK_POINTER(self, funcname) { \
481 if (!IS_POINTER(self)) { \
482 PyErr_SetString(PyExc_TypeError, \
483 "descriptor '" funcname "' requires a 'Pointer' " \
484 "object"); \
485 return NULL; \
486 } \
487 }
488
489 typedef struct Pointer {
490 PyObject_HEAD
491 Action press;
492 Action release;
493 Action click;
494 Action doubleclick;
495 Action motion;
496 } Pointer;
497
498 staticforward PyTypeObject PointerType;
499
500 static PyObject *ptr_bind(Pointer *self, PyObject *args)
501 {
502 char *buttonstr;
503 char *contextstr;
504 guint state, button;
505 PointerBinding *b;
506 GSList *it;
507 GQuark context;
508 PyObject *func;
509 Action action;
510 int i;
511
512 CHECK_POINTER(self, "grab");
513 if (!PyArg_ParseTuple(args, "ssiO:grab",
514 &buttonstr, &contextstr, &action, &func))
515 return NULL;
516
517 if (!translate(buttonstr, &state, &button)) {
518 PyErr_SetString(PyExc_ValueError, "invalid button");
519 return NULL;
520 }
521
522 context = g_quark_try_string(contextstr);
523 if (!context) {
524 PyErr_SetString(PyExc_ValueError, "invalid context");
525 return NULL;
526 }
527
528 if (action < 0 || action >= NUM_ACTIONS) {
529 PyErr_SetString(PyExc_ValueError, "invalid action");
530 return NULL;
531 }
532
533 if (!PyCallable_Check(func)) {
534 PyErr_SetString(PyExc_ValueError, "expected a callable object");
535 return NULL;
536 }
537
538 for (it = g_datalist_id_get_data(&bound_contexts, context);
539 it != NULL; it = it->next){
540 b = it->data;
541 if (b->state == state && b->button == button) {
542 /* already bound */
543 b->funcs[action] = g_slist_append(b->funcs[action], func);
544 Py_INCREF(Py_None);
545 return Py_None;
546 }
547 }
548
549 grab_all_clients(FALSE);
550
551 /* add the binding */
552 b = g_new(PointerBinding, 1);
553 b->state = state;
554 b->button = button;
555 b->name = g_strdup(buttonstr);
556 for (i = 0; i < NUM_ACTIONS; ++i)
557 if (i != (signed)action) b->funcs[i] = NULL;
558 b->funcs[action] = g_slist_append(NULL, func);
559 g_datalist_id_set_data(&bound_contexts, context,
560 g_slist_append(g_datalist_id_get_data(&bound_contexts, context), b));
561 grab_all_clients(TRUE);
562
563 Py_INCREF(Py_None);
564 return Py_None;
565 }
566
567 static PyObject *ptr_clearBinds(Pointer *self, PyObject *args)
568 {
569 CHECK_POINTER(self, "clearBinds");
570 if (!PyArg_ParseTuple(args, ":clearBinds"))
571 return NULL;
572 clearall();
573 Py_INCREF(Py_None);
574 return Py_None;
575 }
576
577 static PyObject *ptr_grab(Pointer *self, PyObject *args)
578 {
579 PyObject *func;
580
581 CHECK_POINTER(self, "grab");
582 if (!PyArg_ParseTuple(args, "O:grab", &func))
583 return NULL;
584 if (!PyCallable_Check(func)) {
585 PyErr_SetString(PyExc_ValueError, "expected a callable object");
586 return NULL;
587 }
588 if (!grab_pointer(TRUE)) {
589 PyErr_SetString(PyExc_RuntimeError, "failed to grab pointer");
590 return NULL;
591 }
592 grab_func = func;
593 Py_INCREF(grab_func);
594 Py_INCREF(Py_None);
595 return Py_None;
596 }
597
598 static PyObject *ptr_ungrab(Pointer *self, PyObject *args)
599 {
600 CHECK_POINTER(self, "ungrab");
601 if (!PyArg_ParseTuple(args, ":ungrab"))
602 return NULL;
603 grab_pointer(FALSE);
604 Py_XDECREF(grab_func);
605 grab_func = NULL;
606 Py_INCREF(Py_None);
607 return Py_None;
608 }
609
610 #define METH(n, d) {#n, (PyCFunction)ptr_##n, METH_VARARGS, #d}
611
612 static PyMethodDef PointerMethods[] = {
613 METH(bind,
614 "bind(button, context, func)\n\n"
615 "Binds a pointer button for a context to a function. See the "
616 "Terminology section for a decription and list of common contexts. "
617 "The button is a string which defines a modifier and button "
618 "combination with the format [Modifier-]...[Button]. Modifiers can "
619 "be 'mod1', 'mod2', 'mod3', 'mod4', 'mod5', 'control', and 'shift'. "
620 "The keys on your keyboard that are bound to each of these modifiers "
621 "can be found by running 'xmodmap'. The button is the number of the "
622 "button. Button numbers can be found by running 'xev', pressing the "
623 "button with the pointer over its window, and watching its output. "
624 "Here are some examples of valid buttons: 'control-1', '2', "
625 "'mod1-shift-5'. The func must have a definition similar to "
626 "'def func(keydata, client)'. A button and context may be bound to "
627 "more than one function."),
628 METH(clearBinds,
629 "clearBinds()\n\n"
630 "Removes all bindings that were previously made by bind()."),
631 METH(grab,
632 "grab(func)\n\n"
633 "Grabs the pointer device, causing all possible pointer events to be "
634 "sent to the given function. CAUTION: Be sure when you grab() that "
635 "you also have an ungrab() that will execute, or you will not be "
636 "able to use the pointer device until you restart Openbox. The func "
637 "must have a definition similar to 'def func(keydata)'. The pointer "
638 "cannot be grabbed if it is already grabbed."),
639 METH(ungrab,
640 "ungrab()\n\n"
641 "Ungrabs the pointer. The pointer cannot be ungrabbed if it is not "
642 "grabbed."),
643 { NULL, NULL, 0, NULL }
644 };
645
646 static PyMemberDef PointerMembers[] = {
647 {"Action_Press", T_INT, offsetof(Pointer, press), READONLY,
648 "a pointer button press"},
649 {"Action_Release", T_INT, offsetof(Pointer, release), READONLY,
650 "a pointer button release"},
651 {"Action_Click", T_INT, offsetof(Pointer, click), READONLY,
652 "a pointer button click (press-release)"},
653 {"Action_DoubleClick", T_INT, offsetof(Pointer, doubleclick), READONLY,
654 "a pointer button double-click"},
655 {"Action_Motion", T_INT, offsetof(Pointer, motion), READONLY,
656 "a pointer drag"},
657 {NULL}
658 };
659
660 /***************************************************************************
661
662 Type methods/struct
663
664 ***************************************************************************/
665
666 static void ptr_dealloc(PyObject *self)
667 {
668 PyObject_Del(self);
669 }
670
671 static PyTypeObject PointerType = {
672 PyObject_HEAD_INIT(NULL)
673 0,
674 "Pointer",
675 sizeof(Pointer),
676 0,
677 (destructor) ptr_dealloc, /*tp_dealloc*/
678 0, /*tp_print*/
679 0, /*tp_getattr*/
680 0, /*tp_setattr*/
681 0, /*tp_compare*/
682 0, /*tp_repr*/
683 0, /*tp_as_number*/
684 0, /*tp_as_sequence*/
685 0, /*tp_as_mapping*/
686 0, /*tp_hash */
687 };
688
689 /**************************************************************************/
690
691 void pointer_startup()
692 {
693 PyObject *input, *inputdict;
694 Pointer *ptr;
695
696 grabbed = FALSE;
697 configwrap_add_int("input", "double_click_rate", "Double-Click Rate",
698 "An integer containing the number of milliseconds in "
699 "which 2 clicks must be received to cause a "
700 "double-click event.", 300);
701 configwrap_add_int("input", "drag_threshold", "Drag Threshold",
702 "An integer containing the number of pixels a drag "
703 "must go before motion events start getting generated. "
704 "Once a drag has begun, the button release will not "
705 "count as a click event.", 3);
706 g_datalist_init(&bound_contexts);
707
708 PointerType.ob_type = &PyType_Type;
709 PointerType.tp_methods = PointerMethods;
710 PointerType.tp_members = PointerMembers;
711 PyType_Ready(&PointerType);
712 PyType_Ready(&PointerDataType);
713
714 /* get the input module/dict */
715 input = PyImport_ImportModule("input"); /* new */
716 g_assert(input != NULL);
717 inputdict = PyModule_GetDict(input); /* borrowed */
718 g_assert(inputdict != NULL);
719
720 /* add a Pointer instance to the input module */
721 ptr = PyObject_New(Pointer, &PointerType);
722 ptr->press = Action_Press;
723 ptr->release = Action_Release;
724 ptr->click = Action_Click;
725 ptr->doubleclick = Action_DoubleClick;
726 ptr->motion = Action_Motion;
727 PyDict_SetItemString(inputdict, "Pointer", (PyObject*) ptr);
728 Py_DECREF(ptr);
729
730 Py_DECREF(input);
731 }
732
733 void pointer_shutdown()
734 {
735 if (grabbed)
736 grab_pointer(FALSE);
737 clearall();
738 g_datalist_clear(&bound_contexts);
739 }
740
This page took 0.066682 seconds and 4 git commands to generate.