]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkdevice-core.c
a947fec73b26bb5dc5290f00127afa8091ac0f15
[~andy/gtk] / gdk / x11 / gdkdevice-core.c
1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
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
22 #include <gdk/gdkwindow.h>
23 #include "gdkdevice-core.h"
24 #include "gdkprivate-x11.h"
25 #include "gdkx.h"
26
27 static gboolean gdk_device_core_get_history (GdkDevice      *device,
28                                              GdkWindow      *window,
29                                              guint32         start,
30                                              guint32         stop,
31                                              GdkTimeCoord ***events,
32                                              guint          *n_events);
33 static void gdk_device_core_get_state (GdkDevice       *device,
34                                        GdkWindow       *window,
35                                        gdouble         *axes,
36                                        GdkModifierType *mask);
37 static void gdk_device_core_set_window_cursor (GdkDevice *device,
38                                                GdkWindow *window,
39                                                GdkCursor *cursor);
40 static void gdk_device_core_warp (GdkDevice *device,
41                                   GdkScreen *screen,
42                                   gint       x,
43                                   gint       y);
44 static gboolean gdk_device_core_query_state (GdkDevice        *device,
45                                              GdkWindow        *window,
46                                              GdkWindow       **root_window,
47                                              GdkWindow       **child_window,
48                                              gint             *root_x,
49                                              gint             *root_y,
50                                              gint             *win_x,
51                                              gint             *win_y,
52                                              GdkModifierType  *mask);
53 static GdkGrabStatus gdk_device_core_grab   (GdkDevice     *device,
54                                              GdkWindow     *window,
55                                              gboolean       owner_events,
56                                              GdkEventMask   event_mask,
57                                              GdkWindow     *confine_to,
58                                              GdkCursor     *cursor,
59                                              guint32        time_);
60 static void          gdk_device_core_ungrab (GdkDevice     *device,
61                                              guint32        time_);
62 static GdkWindow * gdk_device_core_window_at_position (GdkDevice       *device,
63                                                        gint            *win_x,
64                                                        gint            *win_y,
65                                                        GdkModifierType *mask,
66                                                        gboolean         get_toplevel);
67 static void      gdk_device_core_select_window_events (GdkDevice       *device,
68                                                        GdkWindow       *window,
69                                                        GdkEventMask     event_mask);
70
71
72 G_DEFINE_TYPE (GdkDeviceCore, gdk_device_core, GDK_TYPE_DEVICE)
73
74 static void
75 gdk_device_core_class_init (GdkDeviceCoreClass *klass)
76 {
77   GdkDeviceClass *device_class = GDK_DEVICE_CLASS (klass);
78
79   device_class->get_history = gdk_device_core_get_history;
80   device_class->get_state = gdk_device_core_get_state;
81   device_class->set_window_cursor = gdk_device_core_set_window_cursor;
82   device_class->warp = gdk_device_core_warp;
83   device_class->query_state = gdk_device_core_query_state;
84   device_class->grab = gdk_device_core_grab;
85   device_class->ungrab = gdk_device_core_ungrab;
86   device_class->window_at_position = gdk_device_core_window_at_position;
87   device_class->select_window_events = gdk_device_core_select_window_events;
88 }
89
90 static void
91 gdk_device_core_init (GdkDeviceCore *device_core)
92 {
93   GdkDevice *device;
94
95   device = GDK_DEVICE (device_core);
96
97   _gdk_device_add_axis (device, GDK_NONE, GDK_AXIS_X, 0, 0, 1);
98   _gdk_device_add_axis (device, GDK_NONE, GDK_AXIS_Y, 0, 0, 1);
99 }
100
101 static gboolean
102 impl_coord_in_window (GdkWindow *window,
103                       int        impl_x,
104                       int        impl_y)
105 {
106   GdkWindowObject *priv = (GdkWindowObject *) window;
107
108   if (impl_x < priv->abs_x ||
109       impl_x > priv->abs_x + priv->width)
110     return FALSE;
111
112   if (impl_y < priv->abs_y ||
113       impl_y > priv->abs_y + priv->height)
114     return FALSE;
115
116   return TRUE;
117 }
118
119 static gboolean
120 gdk_device_core_get_history (GdkDevice      *device,
121                              GdkWindow      *window,
122                              guint32         start,
123                              guint32         stop,
124                              GdkTimeCoord ***events,
125                              guint          *n_events)
126 {
127   GdkWindowObject *priv;
128   XTimeCoord *xcoords;
129   GdkTimeCoord **coords;
130   GdkWindow *impl_window;
131   int tmp_n_events;
132   int i, j;
133
134   impl_window = _gdk_window_get_impl_window (window);
135   xcoords = XGetMotionEvents (GDK_DRAWABLE_XDISPLAY (window),
136                               GDK_DRAWABLE_XID (impl_window),
137                               start, stop, &tmp_n_events);
138   if (!xcoords)
139     return FALSE;
140
141   priv = (GdkWindowObject *) window;
142   coords = _gdk_device_allocate_history (device, tmp_n_events);
143
144   for (i = 0, j = 0; i < tmp_n_events; i++)
145     {
146       if (impl_coord_in_window (window, xcoords[i].x, xcoords[i].y))
147         {
148           coords[j]->time = xcoords[i].time;
149           coords[j]->axes[0] = xcoords[i].x - priv->abs_x;
150           coords[j]->axes[1] = xcoords[i].y - priv->abs_y;
151           j++;
152         }
153     }
154
155   XFree (xcoords);
156
157   /* free the events we allocated too much */
158   for (i = j; i < tmp_n_events; i++)
159     {
160       g_free (coords[i]);
161       coords[i] = NULL;
162     }
163
164   tmp_n_events = j;
165
166   if (tmp_n_events == 0)
167     {
168       gdk_device_free_history (coords, tmp_n_events);
169       return FALSE;
170     }
171
172   if (n_events)
173     *n_events = tmp_n_events;
174
175   if (events)
176     *events = coords;
177   else if (coords)
178     gdk_device_free_history (coords, tmp_n_events);
179
180   return TRUE;
181 }
182
183 static void
184 gdk_device_core_get_state (GdkDevice       *device,
185                            GdkWindow       *window,
186                            gdouble         *axes,
187                            GdkModifierType *mask)
188 {
189   gint x_int, y_int;
190
191   gdk_window_get_pointer (window, &x_int, &y_int, mask);
192
193   if (axes)
194     {
195       axes[0] = x_int;
196       axes[1] = y_int;
197     }
198 }
199
200 static void
201 gdk_device_core_set_window_cursor (GdkDevice *device,
202                                    GdkWindow *window,
203                                    GdkCursor *cursor)
204 {
205   GdkCursorPrivate *cursor_private;
206   Cursor xcursor;
207
208   cursor_private = (GdkCursorPrivate*) cursor;
209
210   if (!cursor)
211     xcursor = None;
212   else
213     xcursor = cursor_private->xcursor;
214
215   XDefineCursor (GDK_WINDOW_XDISPLAY (window),
216                  GDK_WINDOW_XID (window),
217                  xcursor);
218 }
219
220 static void
221 gdk_device_core_warp (GdkDevice *device,
222                       GdkScreen *screen,
223                       gint       x,
224                       gint       y)
225 {
226   Display *xdisplay;
227   Window dest;
228
229   xdisplay = GDK_DISPLAY_XDISPLAY (gdk_device_get_display (device));
230   dest = GDK_WINDOW_XWINDOW (gdk_screen_get_root_window (screen));
231
232   XWarpPointer (xdisplay, None, dest, 0, 0, 0, 0, x, y);
233 }
234
235 static gboolean
236 gdk_device_core_query_state (GdkDevice        *device,
237                              GdkWindow        *window,
238                              GdkWindow       **root_window,
239                              GdkWindow       **child_window,
240                              gint             *root_x,
241                              gint             *root_y,
242                              gint             *win_x,
243                              gint             *win_y,
244                              GdkModifierType  *mask)
245 {
246   GdkDisplay *display;
247   Window xroot_window, xchild_window;
248   int xroot_x, xroot_y, xwin_x, xwin_y;
249   unsigned int xmask;
250
251   display = gdk_window_get_display (window);
252
253   if (!XQueryPointer (GDK_WINDOW_XDISPLAY (window),
254                       GDK_WINDOW_XID (window),
255                       &xroot_window,
256                       &xchild_window,
257                       &xroot_x,
258                       &xroot_y,
259                       &xwin_x,
260                       &xwin_y,
261                       &xmask))
262     {
263       return FALSE;
264     }
265
266   if (root_window)
267     *root_window = gdk_window_lookup_for_display (display, xroot_window);
268
269   if (child_window)
270     *child_window = gdk_window_lookup_for_display (display, xchild_window);
271
272   if (root_x)
273     *root_x = xroot_x;
274
275   if (root_y)
276     *root_y = xroot_y;
277
278   if (win_x)
279     *win_x = xwin_x;
280
281   if (win_y)
282     *win_y = xwin_y;
283
284   if (mask)
285     *mask = xmask;
286
287   return TRUE;
288 }
289
290 static GdkGrabStatus
291 gdk_device_core_grab (GdkDevice    *device,
292                       GdkWindow    *window,
293                       gboolean      owner_events,
294                       GdkEventMask  event_mask,
295                       GdkWindow    *confine_to,
296                       GdkCursor    *cursor,
297                       guint32       time_)
298 {
299   GdkDisplay *display;
300   Window xwindow, xconfine_to;
301   int status;
302
303   display = gdk_device_get_display (device);
304
305   xwindow = GDK_WINDOW_XID (window);
306
307   if (confine_to)
308     confine_to = _gdk_window_get_impl_window (confine_to);
309
310   if (!confine_to || GDK_WINDOW_DESTROYED (confine_to))
311     xconfine_to = None;
312   else
313     xconfine_to = GDK_WINDOW_XID (confine_to);
314
315   if (device->source == GDK_SOURCE_KEYBOARD)
316     {
317       /* Device is a keyboard */
318       status = XGrabKeyboard (GDK_DISPLAY_XDISPLAY (display),
319                               xwindow,
320                               owner_events,
321                               GrabModeAsync, GrabModeAsync,
322                               time_);
323     }
324   else
325     {
326       Cursor xcursor;
327       guint xevent_mask;
328       gint i;
329
330       /* Device is a pointer */
331       if (!cursor)
332         xcursor = None;
333       else
334         {
335           _gdk_x11_cursor_update_theme (cursor);
336           xcursor = ((GdkCursorPrivate *) cursor)->xcursor;
337         }
338
339       xevent_mask = 0;
340
341       for (i = 0; i < _gdk_nenvent_masks; i++)
342         {
343           if (event_mask & (1 << (i + 1)))
344             xevent_mask |= _gdk_event_mask_table[i];
345         }
346
347       /* We don't want to set a native motion hint mask, as we're emulating motion
348        * hints. If we set a native one we just wouldn't get any events.
349        */
350       xevent_mask &= ~PointerMotionHintMask;
351
352       status = XGrabPointer (GDK_DISPLAY_XDISPLAY (display),
353                              xwindow,
354                              owner_events,
355                              xevent_mask,
356                              GrabModeAsync, GrabModeAsync,
357                              xconfine_to,
358                              xcursor,
359                              time_);
360     }
361
362   return _gdk_x11_convert_grab_status (status);
363 }
364
365 static void
366 gdk_device_core_ungrab (GdkDevice *device,
367                         guint32    time_)
368 {
369   GdkDisplay *display;
370
371   display = gdk_device_get_display (device);
372
373   if (device->source == GDK_SOURCE_KEYBOARD)
374     XUngrabKeyboard (GDK_DISPLAY_XDISPLAY (display), time_);
375   else
376     XUngrabPointer (GDK_DISPLAY_XDISPLAY (display), time_);
377 }
378
379 static GdkWindow *
380 gdk_device_core_window_at_position (GdkDevice       *device,
381                                     gint            *win_x,
382                                     gint            *win_y,
383                                     GdkModifierType *mask,
384                                     gboolean         get_toplevel)
385 {
386   GdkDisplay *display;
387   GdkScreen *screen;
388   Display *xdisplay;
389   GdkWindow *window;
390   Window xwindow, root, child, last;
391   int xroot_x, xroot_y, xwin_x, xwin_y;
392   unsigned int xmask;
393
394   last = None;
395   display = gdk_device_get_display (device);
396   screen = gdk_display_get_default_screen (display);
397
398   /* This function really only works if the mouse pointer is held still
399    * during its operation. If it moves from one leaf window to another
400    * than we'll end up with inaccurate values for win_x, win_y
401    * and the result.
402    */
403   gdk_x11_display_grab (display);
404
405   xdisplay = GDK_SCREEN_XDISPLAY (screen);
406   xwindow = GDK_SCREEN_XROOTWIN (screen);
407
408   XQueryPointer (xdisplay, xwindow,
409                  &root, &child,
410                  &xroot_x, &xroot_y,
411                  &xwin_x, &xwin_y,
412                  &xmask);
413
414   if (root == xwindow)
415     xwindow = child;
416   else
417     xwindow = root;
418
419   while (xwindow)
420     {
421       last = xwindow;
422       XQueryPointer (xdisplay, xwindow,
423                      &root, &xwindow,
424                      &xroot_x, &xroot_y,
425                      &xwin_x, &xwin_y,
426                      &xmask);
427
428       if (get_toplevel && last != root &&
429           (window = gdk_window_lookup_for_display (display, last)) != NULL &&
430           GDK_WINDOW_TYPE (window) != GDK_WINDOW_FOREIGN)
431         {
432           xwindow = last;
433           break;
434         }
435     }
436
437   gdk_x11_display_ungrab (display);
438
439   window = gdk_window_lookup_for_display (display, last);
440
441   if (win_x)
442     *win_x = (window) ? xwin_x : -1;
443
444   if (win_y)
445     *win_y = (window) ? xwin_y : -1;
446
447   if (mask)
448     *mask = xmask;
449
450   return window;
451 }
452
453 static void
454 gdk_device_core_select_window_events (GdkDevice    *device,
455                                       GdkWindow    *window,
456                                       GdkEventMask  event_mask)
457 {
458   GdkEventMask filter_mask, window_mask;
459   guint xmask = 0;
460   gint i;
461
462   window_mask = gdk_window_get_events (window);
463   filter_mask = (GDK_POINTER_MOTION_MASK &
464                  GDK_POINTER_MOTION_HINT_MASK &
465                  GDK_BUTTON_MOTION_MASK &
466                  GDK_BUTTON1_MOTION_MASK &
467                  GDK_BUTTON2_MOTION_MASK &
468                  GDK_BUTTON3_MOTION_MASK &
469                  GDK_BUTTON_PRESS_MASK &
470                  GDK_BUTTON_RELEASE_MASK &
471                  GDK_KEY_PRESS_MASK &
472                  GDK_KEY_RELEASE_MASK &
473                  GDK_ENTER_NOTIFY_MASK &
474                  GDK_LEAVE_NOTIFY_MASK &
475                  GDK_FOCUS_CHANGE_MASK &
476                  GDK_PROXIMITY_IN_MASK &
477                  GDK_PROXIMITY_OUT_MASK &
478                  GDK_SCROLL_MASK);
479
480   /* Filter out non-device events */
481   event_mask &= filter_mask;
482
483   /* Unset device events on window mask */
484   window_mask &= ~(filter_mask);
485
486   /* Combine masks */
487   event_mask |= window_mask;
488
489   for (i = 0; i < _gdk_nenvent_masks; i++)
490     {
491       if (event_mask & (1 << (i + 1)))
492         xmask |= _gdk_event_mask_table[i];
493     }
494
495   if (GDK_WINDOW_XID (window) != GDK_WINDOW_XROOTWIN (window))
496     xmask |= StructureNotifyMask | PropertyChangeMask;
497
498   XSelectInput (GDK_WINDOW_XDISPLAY (window),
499                 GDK_WINDOW_XWINDOW (window),
500                 xmask);
501 }