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