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