]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkdevice-core.c
x11: Move remaining APIs into gdkx11utils.h
[~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 "gdkasync.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   Cursor xcursor;
204
205   if (!cursor)
206     xcursor = None;
207   else
208     xcursor = gdk_x11_cursor_get_xcursor (cursor);
209
210   XDefineCursor (GDK_WINDOW_XDISPLAY (window),
211                  GDK_WINDOW_XID (window),
212                  xcursor);
213 }
214
215 static void
216 gdk_device_core_warp (GdkDevice *device,
217                       GdkScreen *screen,
218                       gint       x,
219                       gint       y)
220 {
221   Display *xdisplay;
222   Window dest;
223
224   xdisplay = GDK_DISPLAY_XDISPLAY (gdk_device_get_display (device));
225   dest = GDK_WINDOW_XID (gdk_screen_get_root_window (screen));
226
227   XWarpPointer (xdisplay, None, dest, 0, 0, 0, 0, x, y);
228 }
229
230 static gboolean
231 gdk_device_core_query_state (GdkDevice        *device,
232                              GdkWindow        *window,
233                              GdkWindow       **root_window,
234                              GdkWindow       **child_window,
235                              gint             *root_x,
236                              gint             *root_y,
237                              gint             *win_x,
238                              gint             *win_y,
239                              GdkModifierType  *mask)
240 {
241   GdkDisplay *display;
242   GdkScreen *default_screen;
243   Window xroot_window, xchild_window;
244   int xroot_x, xroot_y, xwin_x, xwin_y;
245   unsigned int xmask;
246
247   display = gdk_window_get_display (window);
248   default_screen = gdk_display_get_default_screen (display);
249
250   if (G_LIKELY (GDK_DISPLAY_X11 (display)->trusted_client))
251     {
252       if (!XQueryPointer (GDK_WINDOW_XDISPLAY (window),
253                           GDK_WINDOW_XID (window),
254                           &xroot_window,
255                           &xchild_window,
256                           &xroot_x,
257                           &xroot_y,
258                           &xwin_x,
259                           &xwin_y,
260                           &xmask))
261         return FALSE;
262     }
263   else
264     {
265       XSetWindowAttributes attributes;
266       Display *xdisplay;
267       Window xwindow, w;
268
269       /* FIXME: untrusted clients not multidevice-safe */
270       xdisplay = GDK_SCREEN_XDISPLAY (default_screen);
271       xwindow = GDK_SCREEN_XROOTWIN (default_screen);
272
273       w = XCreateWindow (xdisplay, xwindow, 0, 0, 1, 1, 0,
274                          CopyFromParent, InputOnly, CopyFromParent,
275                          0, &attributes);
276       XQueryPointer (xdisplay, w,
277                      &xroot_window,
278                      &xchild_window,
279                      &xroot_x,
280                      &xroot_y,
281                      &xwin_x,
282                      &xwin_y,
283                      &xmask);
284       XDestroyWindow (xdisplay, w);
285     }
286
287   if (root_window)
288     *root_window = gdk_x11_window_lookup_for_display (display, xroot_window);
289
290   if (child_window)
291     *child_window = gdk_x11_window_lookup_for_display (display, xchild_window);
292
293   if (root_x)
294     *root_x = xroot_x;
295
296   if (root_y)
297     *root_y = xroot_y;
298
299   if (win_x)
300     *win_x = xwin_x;
301
302   if (win_y)
303     *win_y = xwin_y;
304
305   if (mask)
306     *mask = xmask;
307
308   return TRUE;
309 }
310
311 static GdkGrabStatus
312 gdk_device_core_grab (GdkDevice    *device,
313                       GdkWindow    *window,
314                       gboolean      owner_events,
315                       GdkEventMask  event_mask,
316                       GdkWindow    *confine_to,
317                       GdkCursor    *cursor,
318                       guint32       time_)
319 {
320   GdkDisplay *display;
321   Window xwindow, xconfine_to;
322   gint status;
323
324   display = gdk_device_get_display (device);
325
326   xwindow = GDK_WINDOW_XID (window);
327
328   if (confine_to)
329     confine_to = _gdk_window_get_impl_window (confine_to);
330
331   if (!confine_to || GDK_WINDOW_DESTROYED (confine_to))
332     xconfine_to = None;
333   else
334     xconfine_to = GDK_WINDOW_XID (confine_to);
335
336 #ifdef G_ENABLE_DEBUG
337   if (_gdk_debug_flags & GDK_DEBUG_NOGRABS)
338     status = GrabSuccess;
339   else
340 #endif
341   if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
342     {
343       /* Device is a keyboard */
344       status = XGrabKeyboard (GDK_DISPLAY_XDISPLAY (display),
345                               xwindow,
346                               owner_events,
347                               GrabModeAsync, GrabModeAsync,
348                               time_);
349     }
350   else
351     {
352       Cursor xcursor;
353       guint xevent_mask;
354       gint i;
355
356       /* Device is a pointer */
357       if (!cursor)
358         xcursor = None;
359       else
360         {
361           _gdk_x11_cursor_update_theme (cursor);
362           xcursor = gdk_x11_cursor_get_xcursor (cursor);
363         }
364
365       xevent_mask = 0;
366
367       for (i = 0; i < _gdk_x11_event_mask_table_size; i++)
368         {
369           if (event_mask & (1 << (i + 1)))
370             xevent_mask |= _gdk_x11_event_mask_table[i];
371         }
372
373       /* We don't want to set a native motion hint mask, as we're emulating motion
374        * hints. If we set a native one we just wouldn't get any events.
375        */
376       xevent_mask &= ~PointerMotionHintMask;
377
378       status = XGrabPointer (GDK_DISPLAY_XDISPLAY (display),
379                              xwindow,
380                              owner_events,
381                              xevent_mask,
382                              GrabModeAsync, GrabModeAsync,
383                              xconfine_to,
384                              xcursor,
385                              time_);
386     }
387
388   _gdk_x11_display_update_grab_info (display, device, status);
389
390   return _gdk_x11_convert_grab_status (status);
391 }
392
393 static void
394 gdk_device_core_ungrab (GdkDevice *device,
395                         guint32    time_)
396 {
397   GdkDisplay *display;
398   gulong serial;
399
400   display = gdk_device_get_display (device);
401   serial = NextRequest (GDK_DISPLAY_XDISPLAY (display));
402
403   if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
404     XUngrabKeyboard (GDK_DISPLAY_XDISPLAY (display), time_);
405   else
406     XUngrabPointer (GDK_DISPLAY_XDISPLAY (display), time_);
407
408   _gdk_x11_display_update_grab_info_ungrab (display, device, time_, serial);
409 }
410
411 static GdkWindow *
412 gdk_device_core_window_at_position (GdkDevice       *device,
413                                     gint            *win_x,
414                                     gint            *win_y,
415                                     GdkModifierType *mask,
416                                     gboolean         get_toplevel)
417 {
418   GdkDisplay *display;
419   GdkScreen *screen;
420   Display *xdisplay;
421   GdkWindow *window;
422   Window xwindow, root, child, last;
423   int xroot_x, xroot_y, xwin_x, xwin_y;
424   unsigned int xmask;
425
426   last = None;
427   display = gdk_device_get_display (device);
428   screen = gdk_display_get_default_screen (display);
429
430   /* This function really only works if the mouse pointer is held still
431    * during its operation. If it moves from one leaf window to another
432    * than we'll end up with inaccurate values for win_x, win_y
433    * and the result.
434    */
435   gdk_x11_display_grab (display);
436
437   xdisplay = GDK_SCREEN_XDISPLAY (screen);
438   xwindow = GDK_SCREEN_XROOTWIN (screen);
439
440   if (G_LIKELY (GDK_DISPLAY_X11 (display)->trusted_client))
441     {
442       XQueryPointer (xdisplay, xwindow,
443                      &root, &child,
444                      &xroot_x, &xroot_y,
445                      &xwin_x, &xwin_y,
446                      &xmask);
447
448       if (root == xwindow)
449         xwindow = child;
450       else
451        xwindow = root;
452     }
453   else
454     {
455       gint i, screens, width, height;
456       GList *toplevels, *list;
457       Window pointer_window, root, child;
458       int rootx = -1, rooty = -1;
459       int winx, winy;
460       unsigned int xmask;
461
462       /* FIXME: untrusted clients case not multidevice-safe */
463       pointer_window = None;
464       screens = gdk_display_get_n_screens (display);
465
466       for (i = 0; i < screens; ++i)
467         {
468           screen = gdk_display_get_screen (display, i);
469           toplevels = gdk_screen_get_toplevel_windows (screen);
470           for (list = toplevels; list != NULL; list = g_list_next (list))
471             {
472               window = GDK_WINDOW (list->data);
473               xwindow = GDK_WINDOW_XID (window);
474               gdk_x11_display_error_trap_push (display);
475               XQueryPointer (xdisplay, xwindow,
476                              &root, &child, &rootx, &rooty, &winx, &winy, &xmask);
477               if (gdk_x11_display_error_trap_pop (display))
478                 continue;
479               if (child != None)
480                 {
481                   pointer_window = child;
482                   break;
483                 }
484               gdk_window_get_geometry (window, NULL, NULL, &width, &height);
485               if (winx >= 0 && winy >= 0 && winx < width && winy < height)
486                 {
487                   /* A childless toplevel, or below another window? */
488                   XSetWindowAttributes attributes;
489                   Window w;
490
491                   w = XCreateWindow (xdisplay, xwindow, winx, winy, 1, 1, 0,
492                                      CopyFromParent, InputOnly, CopyFromParent,
493                                      0, &attributes);
494                   XMapWindow (xdisplay, w);
495                   XQueryPointer (xdisplay, xwindow,
496                                  &root, &child,
497                                  &rootx, &rooty, &winx, &winy, &xmask);
498                   XDestroyWindow (xdisplay, w);
499                   if (child == w)
500                     {
501                       pointer_window = xwindow;
502                       break;
503                     }
504                 }
505             }
506
507           g_list_free (toplevels);
508           if (pointer_window != None)
509             break;
510         }
511
512       xwindow = pointer_window;
513     }
514
515   while (xwindow)
516     {
517       last = xwindow;
518       gdk_x11_display_error_trap_push (display);
519       XQueryPointer (xdisplay, xwindow,
520                      &root, &xwindow,
521                      &xroot_x, &xroot_y,
522                      &xwin_x, &xwin_y,
523                      &xmask);
524       if (gdk_x11_display_error_trap_pop (display))
525         break;
526
527       if (get_toplevel && last != root &&
528           (window = gdk_x11_window_lookup_for_display (display, last)) != NULL &&
529           window->window_type != GDK_WINDOW_FOREIGN)
530         {
531           xwindow = last;
532           break;
533         }
534     }
535
536   gdk_x11_display_ungrab (display);
537
538   window = gdk_x11_window_lookup_for_display (display, last);
539
540   if (win_x)
541     *win_x = (window) ? xwin_x : -1;
542
543   if (win_y)
544     *win_y = (window) ? xwin_y : -1;
545
546   if (mask)
547     *mask = xmask;
548
549   return window;
550 }
551
552 static void
553 gdk_device_core_select_window_events (GdkDevice    *device,
554                                       GdkWindow    *window,
555                                       GdkEventMask  event_mask)
556 {
557   GdkEventMask filter_mask, window_mask;
558   guint xmask = 0;
559   gint i;
560
561   window_mask = gdk_window_get_events (window);
562   filter_mask = (GDK_POINTER_MOTION_MASK &
563                  GDK_POINTER_MOTION_HINT_MASK &
564                  GDK_BUTTON_MOTION_MASK &
565                  GDK_BUTTON1_MOTION_MASK &
566                  GDK_BUTTON2_MOTION_MASK &
567                  GDK_BUTTON3_MOTION_MASK &
568                  GDK_BUTTON_PRESS_MASK &
569                  GDK_BUTTON_RELEASE_MASK &
570                  GDK_KEY_PRESS_MASK &
571                  GDK_KEY_RELEASE_MASK &
572                  GDK_ENTER_NOTIFY_MASK &
573                  GDK_LEAVE_NOTIFY_MASK &
574                  GDK_FOCUS_CHANGE_MASK &
575                  GDK_PROXIMITY_IN_MASK &
576                  GDK_PROXIMITY_OUT_MASK &
577                  GDK_SCROLL_MASK);
578
579   /* Filter out non-device events */
580   event_mask &= filter_mask;
581
582   /* Unset device events on window mask */
583   window_mask &= ~(filter_mask);
584
585   /* Combine masks */
586   event_mask |= window_mask;
587
588   for (i = 0; i < _gdk_x11_event_mask_table_size; i++)
589     {
590       if (event_mask & (1 << (i + 1)))
591         xmask |= _gdk_x11_event_mask_table[i];
592     }
593
594   if (GDK_WINDOW_XID (window) != GDK_WINDOW_XROOTWIN (window))
595     xmask |= StructureNotifyMask | PropertyChangeMask;
596
597   XSelectInput (GDK_WINDOW_XDISPLAY (window),
598                 GDK_WINDOW_XID (window),
599                 xmask);
600 }