]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkinput-gxi.c
Initial revision
[~andy/gtk] / gdk / x11 / gdkinput-gxi.c
1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #ifdef XINPUT_GXI
20
21 /* #define DEBUG_SWITCHING */
22
23 #include <gxid_lib.h>
24
25 /* Forward declarations */
26 static void gdk_input_gxi_select_notify (GdkDevicePrivate *gdkdev);
27 static gint gdk_input_gxi_set_mode (guint32 deviceid, GdkInputMode mode);
28 static gint gdk_input_is_extension_device (guint32 deviceid);
29 static void gdk_input_gxi_configure_event (XConfigureEvent *xevent, 
30                                            GdkWindow *window);
31 static void gdk_input_gxi_enter_event (XCrossingEvent *xevent, 
32                                        GdkWindow *window);
33 static gint gdk_input_gxi_other_event (GdkEvent *event, 
34                                        XEvent *xevent, 
35                                        GdkWindow *window);
36 static void gdk_input_gxi_update_device (GdkDevicePrivate *gdkdev);
37
38 static gint gdk_input_gxi_window_none_event (GdkEvent *event, XEvent *xevent);
39 static gint gdk_input_gxi_enable_window (GdkWindow *window, 
40                                          GdkDevicePrivate *gdkdev);
41 static gint gdk_input_gxi_disable_window (GdkWindow *window, 
42                                           GdkDevicePrivate *gdkdev);
43 static Window gdk_input_find_root_child(Display *dpy, Window w);
44 static void gdk_input_compute_obscuring(GdkInputWindow *input_window);
45 static gint gdk_input_is_obscured(GdkInputWindow *input_window, gdouble x, 
46                                   gdouble y);
47 static GdkTimeCoord *gdk_input_gxi_motion_events (GdkWindow *window,
48                                                   guint32 deviceid,
49                                                   guint32 start,
50                                                   guint32 stop,
51                                                   gint *nevents_return);
52 static void gdk_input_gxi_get_pointer (GdkWindow       *window,
53                                        guint32     deviceid,
54                                        gdouble         *x,
55                                        gdouble         *y,
56                                        gdouble         *pressure,
57                                        gdouble         *xtilt,
58                                        gdouble         *ytilt,
59                                        GdkModifierType *mask);
60 static gint gdk_input_gxi_grab_pointer (GdkWindow *     window,
61                                         gint            owner_events,
62                                         GdkEventMask    event_mask,
63                                         GdkWindow *     confine_to,
64                                         guint32         time);
65 static void gdk_input_gxi_ungrab_pointer (guint32 time);
66
67 /* Local variables */
68
69 static GdkDevicePrivate *gdk_input_current_device;
70 static GdkDevicePrivate *gdk_input_core_pointer;
71
72 void
73 gdk_input_init(void)
74 {
75   GList *tmp_list;
76   
77   gdk_input_vtable.set_mode           = gdk_input_gxi_set_mode;
78   gdk_input_vtable.set_axes        = gdk_input_common_set_axes;
79   gdk_input_vtable.motion_events      = gdk_input_gxi_motion_events;
80   gdk_input_vtable.get_pointer        = gdk_input_gxi_get_pointer;
81   gdk_input_vtable.grab_pointer       = gdk_input_gxi_grab_pointer;
82   gdk_input_vtable.ungrab_pointer     = gdk_input_gxi_ungrab_pointer;
83   gdk_input_vtable.configure_event    = gdk_input_gxi_configure_event;
84   gdk_input_vtable.enter_event        = gdk_input_gxi_enter_event;
85   gdk_input_vtable.other_event        = gdk_input_gxi_other_event;
86   gdk_input_vtable.window_none_event  = gdk_input_gxi_window_none_event;
87   gdk_input_vtable.enable_window      = gdk_input_gxi_enable_window;
88   gdk_input_vtable.disable_window     = gdk_input_gxi_disable_window;
89
90   gdk_input_ignore_core = FALSE;
91   gdk_input_core_pointer = NULL;
92
93   if (!gdk_input_gxid_host) 
94     {
95       gdk_input_gxid_host = getenv("GXID_HOST");
96     }
97   if (!gdk_input_gxid_port) 
98     {
99       char *t = getenv("GXID_PORT");
100       if (t)
101         gdk_input_gxid_port = atoi(t);
102     }
103   
104   gdk_input_common_init(TRUE);
105
106   /* find initial core pointer */
107   
108   for (tmp_list = gdk_input_devices; tmp_list; tmp_list = tmp_list->next) 
109     {
110       GdkDevicePrivate *gdkdev = (GdkDevicePrivate *)tmp_list->data;
111       if (gdk_input_is_extension_device(gdkdev->info.deviceid))
112         {
113           gdk_input_gxi_select_notify (gdkdev);
114         }
115       else
116         {
117           if (gdkdev->info.deviceid != GDK_CORE_POINTER)
118             gdk_input_core_pointer = gdkdev;
119         }
120     }
121 }
122
123 static void
124 gdk_input_gxi_select_notify (GdkDevicePrivate *gdkdev)
125 {
126   XEventClass class;
127
128   ChangeDeviceNotify  (gdkdev->xdevice, gdkdev->changenotify_type, class);
129
130   XSelectExtensionEvent (gdk_display, gdk_root_window, &class, 1);
131 }
132
133 /* Set the core pointer. Device should already be enabled. */
134 static gint
135 gdk_input_gxi_set_core_pointer(GdkDevicePrivate *gdkdev)
136 {
137   int x_axis,y_axis;
138
139   g_return_val_if_fail(gdkdev->xdevice,FALSE);
140
141   x_axis = gdkdev->axis_for_use[GDK_AXIS_X];
142   y_axis = gdkdev->axis_for_use[GDK_AXIS_Y];
143
144   g_return_val_if_fail(x_axis != -1 && y_axis != -1,FALSE);
145
146   /* core_pointer might not be up to date so we check with the server
147      before change the pointer */
148
149   if ( !gdk_input_is_extension_device(gdkdev->info.deviceid) )
150     {
151 #if 0
152       if (gdkdev != gdk_input_core_pointer)
153         g_warning("core pointer inconsistency");
154 #endif      
155       return TRUE;
156     }
157
158   if ( XChangePointerDevice(gdk_display,gdkdev->xdevice, x_axis, y_axis) 
159        != Success )
160     {
161       return FALSE;
162     }
163   else
164     {
165       gdk_input_gxi_update_device (gdk_input_core_pointer);
166       gdk_input_core_pointer = gdkdev;
167       return TRUE;
168     }
169 }
170
171
172 /* FIXME, merge with gdk_input_xfree_set_mode */
173
174 static gint
175 gdk_input_gxi_set_mode (guint32 deviceid, GdkInputMode mode)
176 {
177   GList *tmp_list;
178   GdkDevicePrivate *gdkdev;
179   GdkInputMode old_mode;
180   GdkInputWindow *input_window;
181
182   gdkdev = gdk_input_find_device(deviceid);
183   g_return_val_if_fail (gdkdev != NULL,FALSE);
184   old_mode = gdkdev->info.mode;
185
186   if (gdkdev->info.mode == mode)
187     return TRUE;
188   
189   gdkdev->info.mode = mode;
190
191   if (old_mode != GDK_MODE_DISABLED)
192     {
193       for (tmp_list = gdk_input_windows; tmp_list; tmp_list = tmp_list->next)
194         {
195           input_window = (GdkInputWindow *)tmp_list->data;
196           if (input_window->mode != GDK_EXTENSION_EVENTS_CURSOR)
197             gdk_input_disable_window (input_window->window, gdkdev);
198         }
199     }
200   
201   if (mode != GDK_MODE_DISABLED)
202     {
203       for (tmp_list = gdk_input_windows; tmp_list; tmp_list = tmp_list->next)
204         {
205           input_window = (GdkInputWindow *)tmp_list->data;
206           if (input_window->mode != GDK_EXTENSION_EVENTS_CURSOR)
207             if (!gdk_input_enable_window(input_window->window, gdkdev))
208               {
209                 gdk_input_set_mode(deviceid, old_mode);
210                 return FALSE;
211               }
212         }
213     }
214
215   return TRUE;
216
217 }
218
219 gint
220 gdk_input_is_extension_device (guint32 deviceid)
221 {
222   XDeviceInfo   *devices;
223   int num_devices, loop;
224
225   if (deviceid == GDK_CORE_POINTER)
226     return FALSE;
227   
228   devices = XListInputDevices(gdk_display, &num_devices);
229   for(loop=0; loop<num_devices; loop++)
230     {
231       if ((devices[loop].id == deviceid) && (devices[loop].use == IsXExtensionDevice)) 
232         {
233           XFreeDeviceList(devices);
234           return TRUE;
235         }
236     }
237
238   XFreeDeviceList(devices);
239   return FALSE;
240 }
241
242 static void
243 gdk_input_gxi_configure_event (XConfigureEvent *xevent, GdkWindow *window)
244 {
245   GdkInputWindow *input_window;
246   gint root_x, root_y;
247
248   input_window = gdk_input_window_find(window);
249   g_return_if_fail (input_window != NULL);
250
251   gdk_input_get_root_relative_geometry(gdk_display,GDK_WINDOW_XWINDOW(window),
252                                  &root_x, &root_y, NULL, NULL);
253   input_window->root_x = root_x;
254   input_window->root_y = root_y;
255   gdk_input_compute_obscuring(input_window);
256 }
257
258 static void
259 gdk_input_gxi_enter_event (XCrossingEvent *xevent, GdkWindow *window)
260 {
261   GdkInputWindow *input_window;
262
263   input_window = gdk_input_window_find(window);
264   g_return_if_fail (input_window != NULL);
265
266   gdk_input_compute_obscuring(input_window);
267 }
268
269 static gint 
270 gdk_input_gxi_other_event (GdkEvent *event, 
271                            XEvent *xevent, 
272                            GdkWindow *window)
273 {
274   GdkInputWindow *input_window;
275
276   GdkDevicePrivate *gdkdev;
277   gint return_val;
278
279   input_window = gdk_input_window_find(window);
280   g_return_val_if_fail (window != NULL, -1);
281
282   /* This is a sort of a hack, as there isn't any XDeviceAnyEvent -
283      but it's potentially faster than scanning through the types of
284      every device. If we were deceived, then it won't match any of
285      the types for the device anyways */
286   gdkdev = gdk_input_find_device(((XDeviceButtonEvent *)xevent)->deviceid);
287
288   if (!gdkdev) {
289     return -1;                  /* we don't handle it - not an XInput event */
290   }
291
292   if (gdkdev->info.mode == GDK_MODE_DISABLED ||
293       input_window->mode == GDK_EXTENSION_EVENTS_CURSOR)
294     return FALSE;
295   
296   if (gdkdev != gdk_input_current_device &&
297       xevent->type != gdkdev->changenotify_type)
298     {
299       gdk_input_current_device = gdkdev;
300     }
301
302   return_val = gdk_input_common_other_event (event, xevent, 
303                                              input_window, gdkdev);
304
305   if (return_val > 0 && event->type == GDK_MOTION_NOTIFY &&
306       (!gdkdev->button_state) && (!input_window->grabbed) &&
307       ((event->motion.x < 0) || (event->motion.y < 0) ||
308        (event->motion.x > ((GdkWindowPrivate *)window)->width) || 
309        (event->motion.y > ((GdkWindowPrivate *)window)->height) ||
310        gdk_input_is_obscured(input_window,event->motion.x,event->motion.y)))
311     {
312 #ifdef DEBUG_SWITCHING
313       g_print("gdkinput: Setting core pointer to %d on motion at (%f,%f)\n",
314               gdkdev->info.deviceid,event->motion.x,event->motion.y);
315       g_print("   window geometry is: %dx%d\n",
316               ((GdkWindowPrivate *)window)->width,
317               ((GdkWindowPrivate *)window)->height);
318 #endif      
319       gdk_input_gxi_set_core_pointer(gdkdev);
320       return FALSE;
321     }
322   else
323     return return_val;
324
325 }
326
327 static void
328 gdk_input_gxi_update_device (GdkDevicePrivate *gdkdev)
329 {
330   GList *t;
331
332   if (gdk_input_is_extension_device (gdkdev->info.deviceid))
333     {
334       if (!gdkdev->xdevice)
335         {
336           gdkdev->xdevice = XOpenDevice(gdk_display, gdkdev->info.deviceid);
337           gdk_input_gxi_select_notify (gdkdev);
338           gdkdev->needs_update = 1;
339         }
340       if (gdkdev->needs_update && gdkdev->xdevice)
341         {
342           for (t = gdk_input_windows; t; t = t->next)
343             gdk_input_common_select_events (((GdkInputWindow *)t->data)->window,
344                                          gdkdev);
345           gdkdev->needs_update = 0;
346         }
347     }
348 }
349
350 static gint 
351 gdk_input_gxi_window_none_event (GdkEvent *event, XEvent *xevent)
352 {
353   GdkDevicePrivate *gdkdev = 
354     gdk_input_find_device(((XDeviceButtonEvent *)xevent)->deviceid);
355
356   if (!gdkdev) {
357     return -1;                  /* we don't handle it - not an XInput event */
358   }
359
360   if (xevent->type == gdkdev->changenotify_type)
361     {
362       if (gdk_input_core_pointer != gdkdev)
363         {
364 #ifdef DEBUG_SWITCHING
365           g_print("ChangeNotify from %d to %d:\n",
366                   gdk_input_core_pointer->info.deviceid,
367                   gdkdev->info.deviceid);
368 #endif
369           gdk_input_gxi_update_device (gdk_input_core_pointer);
370           gdk_input_core_pointer = gdkdev;
371         }
372     }
373                 
374   return FALSE;
375 }
376
377 static gint
378 gdk_input_gxi_enable_window (GdkWindow *window, GdkDevicePrivate *gdkdev)
379 {
380   GdkInputWindow *input_window;
381
382   input_window = gdk_input_window_find (window);
383   g_return_val_if_fail (input_window != NULL, FALSE);
384
385   if (!gdkdev->claimed)
386     {
387       if (gxid_claim_device(gdk_input_gxid_host, gdk_input_gxid_port,
388                             gdkdev->info.deviceid,
389                             GDK_WINDOW_XWINDOW(window), FALSE) !=
390           GXID_RETURN_OK)
391         {
392           g_warning("Could not get device (is gxid running?)\n");
393           return FALSE;
394         }
395       gdkdev->claimed = TRUE;
396     }
397
398   if (gdkdev->xdevice && gdkdev != gdk_input_core_pointer)
399     gdk_input_common_select_events(window, gdkdev);
400   else
401     gdkdev->needs_update = TRUE;
402   
403   return TRUE;
404 }
405
406 static gint
407 gdk_input_gxi_disable_window(GdkWindow *window, GdkDevicePrivate *gdkdev)
408 {
409   GdkInputWindow *input_window;
410
411   input_window = gdk_input_window_find (window);
412   g_return_val_if_fail (input_window != NULL, FALSE);
413
414   if (gdkdev->claimed)
415     {
416       gxid_release_device(gdk_input_gxid_host, gdk_input_gxid_port,
417                           gdkdev->info.deviceid,
418                           GDK_WINDOW_XWINDOW(window));
419
420       gdkdev->claimed = FALSE;
421     }
422
423   if (gdkdev->xdevice && gdkdev != gdk_input_core_pointer)
424     gdk_input_common_select_events(window, gdkdev);
425   else
426     gdkdev->needs_update = TRUE;
427   
428   return TRUE;
429 }
430
431 static gint
432 gdk_input_is_obscured(GdkInputWindow *input_window, gdouble x, gdouble y)
433 {
434   int i;
435   for (i=0;i<input_window->num_obscuring;i++)
436     {
437       GdkRectangle *rect = &input_window->obscuring[i];
438       if ((x >= rect->x) &&
439           (y >= rect->y) &&
440           (x < rect->x + rect->width) &&
441           (y < rect->y + rect->height))
442         return TRUE;
443     }
444   return FALSE;
445 }
446
447 /* If this routine needs fixing, the corresponding routine
448    in gxid.c will need it too. */
449
450 static Window 
451 gdk_input_find_root_child(Display *dpy, Window w)
452 {
453   Window root,parent;
454   Window *children;
455   int nchildren;
456
457   parent = w;
458   do 
459     {
460       w = parent;
461       XQueryTree(dpy,w,&root,&parent,&children,&nchildren);
462       if (children) XFree(children);
463     } 
464   while (parent != root);
465   
466   return w;
467 }
468
469 void
470 gdk_input_compute_obscuring(GdkInputWindow *input_window)
471 {
472   int i;
473   int x,y,width,height;
474   int xc,yc,widthc,heightc,border_widthc,depthc;
475
476   Window root,parent;
477   Window *children;
478   int nchildren;
479
480   Window w = GDK_WINDOW_XWINDOW(input_window->window);
481   Window root_child = gdk_input_find_root_child(gdk_display,w);
482   gdk_input_get_root_relative_geometry(gdk_display,w,&x,&y,&width,&height);
483
484   input_window->root_x = x;
485   input_window->root_y = y;
486
487   XQueryTree(gdk_display,GDK_ROOT_WINDOW(),
488              &root,&parent,&children,&nchildren);
489
490
491   if (input_window->obscuring)
492     g_free(input_window->obscuring);
493   input_window->obscuring = 0;
494   input_window->num_obscuring = 0;
495
496   for (i=0;i<nchildren;i++)
497     if (children[i] == root_child)
498       break;
499
500   if (i>=nchildren-1)
501     {
502       if (nchildren)
503         XFree(children);
504       return;
505     }
506
507   input_window->obscuring = g_new(GdkRectangle,(nchildren-i-1));
508
509   for (i=i+1;i<nchildren;i++)
510     {
511       int xmin, xmax, ymin, ymax;
512       XGetGeometry(gdk_display,children[i],&root,&xc,&yc,&widthc,&heightc,
513                    &border_widthc, &depthc);
514       xmin = xc>x ? xc : x;
515       xmax = (xc+widthc)<(x+width) ? xc+widthc : x+width;
516       ymin = yc>y ? yc : y;
517       ymax = (yc+heightc)<(y+height) ? yc+heightc : y+height;
518       if ((xmin < xmax) && (ymin < ymax))
519         {
520           XWindowAttributes attributes;
521           XGetWindowAttributes(gdk_display,children[i],&attributes);
522           if (attributes.map_state == IsViewable)
523             {
524               GdkRectangle *rect = &input_window->obscuring[input_window->num_obscuring];
525               
526               /* we store the whole window, not just the obscuring part */
527               rect->x = xc - x;
528               rect->y = yc - y;
529               rect->width = widthc;
530               rect->height = heightc;
531               input_window->num_obscuring++;
532             }
533         }
534     }
535
536   if (nchildren)
537     XFree(children);
538 }
539
540 static void 
541 gdk_input_gxi_get_pointer     (GdkWindow       *window,
542                                guint32     deviceid,
543                                gdouble         *x,
544                                gdouble         *y,
545                                gdouble         *pressure,
546                                gdouble         *xtilt,
547                                gdouble         *ytilt,
548                                GdkModifierType *mask)
549 {
550   GdkDevicePrivate *gdkdev;
551
552   gdkdev = gdk_input_find_device (deviceid);
553   g_return_if_fail (gdkdev != NULL);
554
555   if (gdkdev == gdk_input_core_pointer)
556     gdk_input_common_get_pointer (window, GDK_CORE_POINTER, x, y,
557                                   pressure, xtilt, ytilt, mask);
558   else
559     gdk_input_common_get_pointer (window, deviceid, x, y,
560                                   pressure, xtilt, ytilt, mask);
561 }
562
563 static GdkTimeCoord *
564 gdk_input_gxi_motion_events (GdkWindow *window,
565                              guint32 deviceid,
566                              guint32 start,
567                              guint32 stop,
568                              gint *nevents_return)
569 {
570   GdkDevicePrivate *gdkdev;
571
572   gdkdev = gdk_input_find_device (deviceid);
573   g_return_val_if_fail (gdkdev != NULL, NULL);
574   
575
576   if (gdkdev == gdk_input_core_pointer)
577     return gdk_input_motion_events (window, GDK_CORE_POINTER, start, stop,
578                                     nevents_return);
579   else
580     return gdk_input_common_motion_events (window, deviceid, start, stop,
581                                            nevents_return);
582   
583 }
584
585 static gint 
586 gdk_input_gxi_grab_pointer (GdkWindow *     window,
587                             gint            owner_events,
588                             GdkEventMask    event_mask,
589                             GdkWindow *     confine_to,
590                             guint32         time)
591 {
592   GdkInputWindow *input_window, *new_window;
593   GList *tmp_list;
594
595   tmp_list = gdk_input_windows;
596   while (tmp_list)
597     {
598       input_window = (GdkInputWindow *)tmp_list->data;
599       if (input_window->grabbed)
600         return AlreadyGrabbed;
601
602       if (input_window->window == window)
603         new_window = input_window;
604       
605       tmp_list = tmp_list->next;
606     }
607
608   new_window->grabbed = TRUE;
609   return Success;
610 }
611
612 static void 
613 gdk_input_gxi_ungrab_pointer (guint32 time)
614 {
615   GdkInputWindow *input_window;
616   GList *tmp_list;
617
618   tmp_list = gdk_input_windows;
619   while (tmp_list)
620     {
621       input_window = (GdkInputWindow *)tmp_list->data;
622       if (input_window->grabbed)
623         input_window->grabbed = FALSE;
624       tmp_list = tmp_list->next;
625     }
626 }
627
628 #endif /* XINPUT_GXI */