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