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 Lesser 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  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser 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 #include "config.h"
21 #include <string.h>
22 #include "gdkinputprivate.h"
23 #include "gdkdisplay-x11.h"
24 #include "gdkalias.h"
25 
26 /*
27  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
28  * file for a list of people on the GTK+ Team.  See the ChangeLog
29  * files for a list of changes.  These files are distributed with
30  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
31  */
32 
33 /* forward declarations */
34 
35 static void gdk_input_check_proximity (GdkDisplay *display);
36 
37 void
_gdk_input_init(GdkDisplay * display)38 _gdk_input_init(GdkDisplay *display)
39 {
40   _gdk_init_input_core (display);
41   display->ignore_core_events = FALSE;
42   _gdk_input_common_init (display, FALSE);
43 }
44 
45 gboolean
gdk_device_set_mode(GdkDevice * device,GdkInputMode mode)46 gdk_device_set_mode (GdkDevice      *device,
47 		     GdkInputMode    mode)
48 {
49   GList *tmp_list;
50   GdkDevicePrivate *gdkdev;
51   GdkInputWindow *input_window;
52   GdkDisplayX11 *display_impl;
53 
54   if (GDK_IS_CORE (device))
55     return FALSE;
56 
57   gdkdev = (GdkDevicePrivate *)device;
58 
59   if (device->mode == mode)
60     return TRUE;
61 
62   device->mode = mode;
63 
64   if (mode == GDK_MODE_WINDOW)
65     device->has_cursor = FALSE;
66   else if (mode == GDK_MODE_SCREEN)
67     device->has_cursor = TRUE;
68 
69   display_impl = GDK_DISPLAY_X11 (gdkdev->display);
70   for (tmp_list = display_impl->input_windows; tmp_list; tmp_list = tmp_list->next)
71     {
72       input_window = (GdkInputWindow *)tmp_list->data;
73       _gdk_input_select_events (input_window->impl_window, gdkdev);
74     }
75 
76   return TRUE;
77 }
78 
79 static int
ignore_errors(Display * display,XErrorEvent * event)80 ignore_errors (Display *display, XErrorEvent *event)
81 {
82   return True;
83 }
84 
85 static void
gdk_input_check_proximity(GdkDisplay * display)86 gdk_input_check_proximity (GdkDisplay *display)
87 {
88   GdkDisplayX11 *display_impl = GDK_DISPLAY_X11 (display);
89   GList *tmp_list = display_impl->input_devices;
90   gint new_proximity = 0;
91 
92   while (tmp_list && !new_proximity)
93     {
94       GdkDevicePrivate *gdkdev = (GdkDevicePrivate *)(tmp_list->data);
95 
96       if (gdkdev->info.mode != GDK_MODE_DISABLED
97 	  && !GDK_IS_CORE (gdkdev)
98 	  && gdkdev->xdevice)
99 	{
100       int (*old_handler) (Display *, XErrorEvent *);
101       XDeviceState *state = NULL;
102       XInputClass *xic;
103       int i;
104 
105       /* From X11 doc: "XQueryDeviceState can generate a BadDevice error."
106        * This would occur in particular when a device is unplugged,
107        * which would cause the program to crash (see bug 575767).
108        *
109        * To handle this case gracefully, we simply ignore the device.
110        * GTK+ 3 handles this better with XInput 2's hotplugging support;
111        * but this is better than a crash in GTK+ 2.
112        */
113       old_handler = XSetErrorHandler (ignore_errors);
114       state = XQueryDeviceState(display_impl->xdisplay, gdkdev->xdevice);
115       XSetErrorHandler (old_handler);
116 
117       if (! state)
118         {
119           /* Broken device. It may have been disconnected.
120            * Ignore it.
121            */
122           tmp_list = tmp_list->next;
123           continue;
124         }
125 
126 	  xic = state->data;
127 	  for (i=0; i<state->num_classes; i++)
128 	    {
129 	      if (xic->class == ValuatorClass)
130 		{
131 		  XValuatorState *xvs = (XValuatorState *)xic;
132 		  if ((xvs->mode & ProximityState) == InProximity)
133 		    {
134 		      new_proximity = TRUE;
135 		    }
136 		  break;
137 		}
138 	      xic = (XInputClass *)((char *)xic + xic->length);
139 	    }
140 
141 	  XFreeDeviceState (state);
142 	}
143       tmp_list = tmp_list->next;
144     }
145 
146   display->ignore_core_events = new_proximity;
147 }
148 
149 void
_gdk_input_configure_event(XConfigureEvent * xevent,GdkWindow * window)150 _gdk_input_configure_event (XConfigureEvent *xevent,
151 			    GdkWindow       *window)
152 {
153   GdkWindowObject *priv = (GdkWindowObject *)window;
154   GdkInputWindow *input_window;
155   gint root_x, root_y;
156 
157   input_window = priv->input_window;
158   if (input_window != NULL)
159     {
160       _gdk_input_get_root_relative_geometry (window, &root_x, &root_y);
161       input_window->root_x = root_x;
162       input_window->root_y = root_y;
163     }
164 }
165 
166 void
_gdk_input_crossing_event(GdkWindow * window,gboolean enter)167 _gdk_input_crossing_event (GdkWindow *window,
168 			   gboolean enter)
169 {
170   GdkDisplay *display = GDK_WINDOW_DISPLAY (window);
171   GdkDisplayX11 *display_impl = GDK_DISPLAY_X11 (display);
172   GdkWindowObject *priv = (GdkWindowObject *)window;
173   GdkInputWindow *input_window;
174   gint root_x, root_y;
175 
176   if (enter)
177     {
178       gdk_input_check_proximity(display);
179 
180       input_window = priv->input_window;
181       if (input_window != NULL)
182 	{
183 	  _gdk_input_get_root_relative_geometry (window, &root_x, &root_y);
184 	  input_window->root_x = root_x;
185 	  input_window->root_y = root_y;
186 	}
187     }
188   else
189     display->ignore_core_events = FALSE;
190 }
191 
192 static GdkEventType
get_input_event_type(GdkDevicePrivate * gdkdev,XEvent * xevent,int * core_x,int * core_y)193 get_input_event_type (GdkDevicePrivate *gdkdev,
194 		      XEvent *xevent,
195 		      int *core_x, int *core_y)
196 {
197   if (xevent->type == gdkdev->buttonpress_type)
198     {
199       XDeviceButtonEvent *xie = (XDeviceButtonEvent *)(xevent);
200       *core_x = xie->x;
201       *core_y = xie->y;
202       return GDK_BUTTON_PRESS;
203     }
204 
205   if (xevent->type == gdkdev->buttonrelease_type)
206     {
207       XDeviceButtonEvent *xie = (XDeviceButtonEvent *)(xevent);
208       *core_x = xie->x;
209       *core_y = xie->y;
210       return GDK_BUTTON_RELEASE;
211     }
212 
213   if (xevent->type == gdkdev->keypress_type)
214     {
215       XDeviceKeyEvent *xie = (XDeviceKeyEvent *)(xevent);
216       *core_x = xie->x;
217       *core_y = xie->y;
218       return GDK_KEY_PRESS;
219     }
220 
221   if (xevent->type == gdkdev->keyrelease_type)
222     {
223       XDeviceKeyEvent *xie = (XDeviceKeyEvent *)(xevent);
224       *core_x = xie->x;
225       *core_y = xie->y;
226       return GDK_KEY_RELEASE;
227     }
228 
229   if (xevent->type == gdkdev->motionnotify_type)
230     {
231       XDeviceMotionEvent *xie = (XDeviceMotionEvent *)(xevent);
232       *core_x = xie->x;
233       *core_y = xie->y;
234       return GDK_MOTION_NOTIFY;
235     }
236 
237   if (xevent->type == gdkdev->proximityin_type)
238     {
239       XProximityNotifyEvent *xie = (XProximityNotifyEvent *)(xevent);
240       *core_x = xie->x;
241       *core_y = xie->y;
242       return GDK_PROXIMITY_IN;
243     }
244 
245   if (xevent->type == gdkdev->proximityout_type)
246     {
247       XProximityNotifyEvent *xie = (XProximityNotifyEvent *)(xevent);
248       *core_x = xie->x;
249       *core_y = xie->y;
250       return GDK_PROXIMITY_OUT;
251     }
252 
253   *core_x = 0;
254   *core_y = 0;
255   return GDK_NOTHING;
256 }
257 
258 
259 gboolean
_gdk_input_other_event(GdkEvent * event,XEvent * xevent,GdkWindow * event_window)260 _gdk_input_other_event (GdkEvent *event,
261 			XEvent *xevent,
262 			GdkWindow *event_window)
263 {
264   GdkWindow *window;
265   GdkWindowObject *priv;
266   GdkInputWindow *iw;
267   GdkDevicePrivate *gdkdev;
268   GdkEventType event_type;
269   int x, y;
270   GdkDisplay *display = GDK_WINDOW_DISPLAY (event_window);
271   GdkDisplayX11 *display_impl = GDK_DISPLAY_X11 (display);
272 
273   /* This is a sort of a hack, as there isn't any XDeviceAnyEvent -
274      but it's potentially faster than scanning through the types of
275      every device. If we were deceived, then it won't match any of
276      the types for the device anyways */
277   gdkdev = _gdk_input_find_device (display,
278 				   ((XDeviceButtonEvent *)xevent)->deviceid);
279   if (!gdkdev)
280     return FALSE;			/* we don't handle it - not an XInput event */
281 
282   event_type = get_input_event_type (gdkdev, xevent, &x, &y);
283   if (event_type == GDK_NOTHING)
284     return FALSE;
285 
286   /* If we're not getting any event window its likely because we're outside the
287      window and there is no grab. We should still report according to the
288      implicit grab though. */
289   iw = ((GdkWindowObject *)event_window)->input_window;
290 
291   if (iw->button_down_window)
292     window = iw->button_down_window;
293   else
294     window = _gdk_window_get_input_window_for_event (event_window,
295                                                      event_type,
296 						     /* TODO: Seems wrong, but the code used to ignore button motion handling here... */
297 						     0,
298                                                      x, y,
299                                                      xevent->xany.serial);
300   priv = (GdkWindowObject *)window;
301   if (window == NULL)
302     return FALSE;
303 
304   if (gdkdev->info.mode == GDK_MODE_DISABLED ||
305       priv->extension_events == 0 ||
306       !(gdkdev->info.has_cursor || (priv->extension_events & GDK_ALL_DEVICES_MASK)))
307     return FALSE;
308 
309   if (!display->ignore_core_events && priv->extension_events != 0)
310     gdk_input_check_proximity (GDK_WINDOW_DISPLAY (window));
311 
312   if (!_gdk_input_common_other_event (event, xevent, window, gdkdev))
313     return FALSE;
314 
315   if (event->type == GDK_BUTTON_PRESS)
316     iw->button_down_window = window;
317   if (event->type == GDK_BUTTON_RELEASE && !gdkdev->button_count)
318     iw->button_down_window = NULL;
319 
320   if (event->type == GDK_PROXIMITY_OUT &&
321       display->ignore_core_events)
322     gdk_input_check_proximity (GDK_WINDOW_DISPLAY (window));
323 
324   return _gdk_input_common_event_selected(event, window, gdkdev);
325 }
326 
327 gint
_gdk_input_grab_pointer(GdkWindow * window,GdkWindow * native_window,gint owner_events,GdkEventMask event_mask,GdkWindow * confine_to,guint32 time)328 _gdk_input_grab_pointer (GdkWindow      *window,
329 			 GdkWindow      *native_window, /* This is the toplevel */
330 			 gint            owner_events,
331 			 GdkEventMask    event_mask,
332 			 GdkWindow *     confine_to,
333 			 guint32         time)
334 {
335   GdkInputWindow *input_window;
336   GdkWindowObject *priv, *impl_window;
337   gboolean need_ungrab;
338   GdkDevicePrivate *gdkdev;
339   GList *tmp_list;
340   XEventClass event_classes[GDK_MAX_DEVICE_CLASSES];
341   gint num_classes;
342   gint result;
343   GdkDisplayX11 *display_impl  = GDK_DISPLAY_X11 (GDK_WINDOW_DISPLAY (window));
344 
345   tmp_list = display_impl->input_windows;
346   need_ungrab = FALSE;
347 
348   while (tmp_list)
349     {
350       input_window = (GdkInputWindow *)tmp_list->data;
351 
352       if (input_window->grabbed)
353 	{
354 	  input_window->grabbed = FALSE;
355 	  need_ungrab = TRUE;
356 	  break;
357 	}
358       tmp_list = tmp_list->next;
359     }
360 
361   priv = (GdkWindowObject *)window;
362   impl_window = (GdkWindowObject *)_gdk_window_get_impl_window (window);
363   input_window = impl_window->input_window;
364   if (priv->extension_events)
365     {
366       g_assert (input_window != NULL);
367       input_window->grabbed = TRUE;
368 
369       tmp_list = display_impl->input_devices;
370       while (tmp_list)
371 	{
372 	  gdkdev = (GdkDevicePrivate *)tmp_list->data;
373 	  if (!GDK_IS_CORE (gdkdev) && gdkdev->xdevice)
374 	    {
375 	      _gdk_input_common_find_events (gdkdev, event_mask,
376 					     event_classes, &num_classes);
377 #ifdef G_ENABLE_DEBUG
378 	      if (_gdk_debug_flags & GDK_DEBUG_NOGRABS)
379 		result = GrabSuccess;
380 	      else
381 #endif
382 		result = XGrabDevice (display_impl->xdisplay, gdkdev->xdevice,
383 				      GDK_WINDOW_XWINDOW (native_window),
384 				      owner_events, num_classes, event_classes,
385 				      GrabModeAsync, GrabModeAsync, time);
386 
387 	      /* FIXME: if failure occurs on something other than the first
388 		 device, things will be badly inconsistent */
389 	      if (result != Success)
390 		return result;
391 	    }
392 	  tmp_list = tmp_list->next;
393 	}
394     }
395   else
396     {
397       tmp_list = display_impl->input_devices;
398       while (tmp_list)
399 	{
400 	  gdkdev = (GdkDevicePrivate *)tmp_list->data;
401 	  if (!GDK_IS_CORE (gdkdev) && gdkdev->xdevice &&
402 	      ((gdkdev->button_count != 0) || need_ungrab))
403 	    {
404 	      XUngrabDevice (display_impl->xdisplay, gdkdev->xdevice, time);
405 	      memset (gdkdev->button_state, 0, sizeof (gdkdev->button_state));
406 	      gdkdev->button_count = 0;
407 	    }
408 
409 	  tmp_list = tmp_list->next;
410 	}
411     }
412 
413   return Success;
414 }
415 
416 void
_gdk_input_ungrab_pointer(GdkDisplay * display,guint32 time)417 _gdk_input_ungrab_pointer (GdkDisplay *display,
418 			   guint32 time)
419 {
420   GdkInputWindow *input_window = NULL; /* Quiet GCC */
421   GdkDevicePrivate *gdkdev;
422   GList *tmp_list;
423   GdkDisplayX11 *display_impl = GDK_DISPLAY_X11 (display);
424 
425   tmp_list = display_impl->input_windows;
426   while (tmp_list)
427     {
428       input_window = (GdkInputWindow *)tmp_list->data;
429       if (input_window->grabbed)
430 	break;
431       tmp_list = tmp_list->next;
432     }
433 
434   if (tmp_list)			/* we found a grabbed window */
435     {
436       input_window->grabbed = FALSE;
437 
438       tmp_list = display_impl->input_devices;
439       while (tmp_list)
440 	{
441 	  gdkdev = (GdkDevicePrivate *)tmp_list->data;
442 	  if (!GDK_IS_CORE (gdkdev) && gdkdev->xdevice)
443 	    XUngrabDevice( display_impl->xdisplay, gdkdev->xdevice, time);
444 
445 	  tmp_list = tmp_list->next;
446 	}
447     }
448 }
449 
450 #define __GDK_INPUT_XFREE_C__
451 #include "gdkaliasdef.c"
452