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