]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkdevice-core-x11.c
Change GdkFrameClock from an interface to a class
[~andy/gtk] / gdk / x11 / gdkdevice-core-x11.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, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include "config.h"
19
20 #include "gdkx11device-core.h"
21 #include "gdkdeviceprivate.h"
22
23 #include "gdkinternals.h"
24 #include "gdkwindow.h"
25 #include "gdkprivate-x11.h"
26 #include "gdkasync.h"
27
28 struct _GdkX11DeviceCore
29 {
30   GdkDevice parent_instance;
31 };
32
33 struct _GdkX11DeviceCoreClass
34 {
35   GdkDeviceClass parent_class;
36 };
37
38 static gboolean gdk_x11_device_core_get_history (GdkDevice       *device,
39                                                  GdkWindow       *window,
40                                                  guint32          start,
41                                                  guint32          stop,
42                                                  GdkTimeCoord  ***events,
43                                                  gint            *n_events);
44 static void     gdk_x11_device_core_get_state   (GdkDevice       *device,
45                                                  GdkWindow       *window,
46                                                  gdouble         *axes,
47                                                  GdkModifierType *mask);
48 static void     gdk_x11_device_core_set_window_cursor (GdkDevice *device,
49                                                        GdkWindow *window,
50                                                        GdkCursor *cursor);
51 static void     gdk_x11_device_core_warp (GdkDevice *device,
52                                           GdkScreen *screen,
53                                           gint       x,
54                                           gint       y);
55 static void gdk_x11_device_core_query_state (GdkDevice        *device,
56                                              GdkWindow        *window,
57                                              GdkWindow       **root_window,
58                                              GdkWindow       **child_window,
59                                              gint             *root_x,
60                                              gint             *root_y,
61                                              gint             *win_x,
62                                              gint             *win_y,
63                                              GdkModifierType  *mask);
64 static GdkGrabStatus gdk_x11_device_core_grab   (GdkDevice     *device,
65                                                  GdkWindow     *window,
66                                                  gboolean       owner_events,
67                                                  GdkEventMask   event_mask,
68                                                  GdkWindow     *confine_to,
69                                                  GdkCursor     *cursor,
70                                                  guint32        time_);
71 static void          gdk_x11_device_core_ungrab (GdkDevice     *device,
72                                                  guint32        time_);
73 static GdkWindow * gdk_x11_device_core_window_at_position (GdkDevice       *device,
74                                                            gint            *win_x,
75                                                            gint            *win_y,
76                                                            GdkModifierType *mask,
77                                                            gboolean         get_toplevel);
78 static void      gdk_x11_device_core_select_window_events (GdkDevice       *device,
79                                                            GdkWindow       *window,
80                                                            GdkEventMask     event_mask);
81
82 G_DEFINE_TYPE (GdkX11DeviceCore, gdk_x11_device_core, GDK_TYPE_DEVICE)
83
84 static void
85 gdk_x11_device_core_class_init (GdkX11DeviceCoreClass *klass)
86 {
87   GdkDeviceClass *device_class = GDK_DEVICE_CLASS (klass);
88
89   device_class->get_history = gdk_x11_device_core_get_history;
90   device_class->get_state = gdk_x11_device_core_get_state;
91   device_class->set_window_cursor = gdk_x11_device_core_set_window_cursor;
92   device_class->warp = gdk_x11_device_core_warp;
93   device_class->query_state = gdk_x11_device_core_query_state;
94   device_class->grab = gdk_x11_device_core_grab;
95   device_class->ungrab = gdk_x11_device_core_ungrab;
96   device_class->window_at_position = gdk_x11_device_core_window_at_position;
97   device_class->select_window_events = gdk_x11_device_core_select_window_events;
98 }
99
100 static void
101 gdk_x11_device_core_init (GdkX11DeviceCore *device_core)
102 {
103   GdkDevice *device;
104
105   device = GDK_DEVICE (device_core);
106
107   _gdk_device_add_axis (device, GDK_NONE, GDK_AXIS_X, 0, 0, 1);
108   _gdk_device_add_axis (device, GDK_NONE, GDK_AXIS_Y, 0, 0, 1);
109 }
110
111 static gboolean
112 impl_coord_in_window (GdkWindow *window,
113                       int        impl_x,
114                       int        impl_y)
115 {
116   if (impl_x < window->abs_x ||
117       impl_x >= window->abs_x + window->width)
118     return FALSE;
119
120   if (impl_y < window->abs_y ||
121       impl_y >= window->abs_y + window->height)
122     return FALSE;
123
124   return TRUE;
125 }
126
127 static gboolean
128 gdk_x11_device_core_get_history (GdkDevice      *device,
129                                  GdkWindow      *window,
130                                  guint32         start,
131                                  guint32         stop,
132                                  GdkTimeCoord ***events,
133                                  gint           *n_events)
134 {
135   XTimeCoord *xcoords;
136   GdkTimeCoord **coords;
137   GdkWindow *impl_window;
138   int tmp_n_events;
139   int i, j;
140
141   impl_window = _gdk_window_get_impl_window (window);
142   xcoords = XGetMotionEvents (GDK_WINDOW_XDISPLAY (window),
143                               GDK_WINDOW_XID (impl_window),
144                               start, stop, &tmp_n_events);
145   if (!xcoords)
146     return FALSE;
147
148   coords = _gdk_device_allocate_history (device, tmp_n_events);
149
150   for (i = 0, j = 0; i < tmp_n_events; i++)
151     {
152       if (impl_coord_in_window (window, xcoords[i].x, xcoords[i].y))
153         {
154           coords[j]->time = xcoords[i].time;
155           coords[j]->axes[0] = xcoords[i].x - window->abs_x;
156           coords[j]->axes[1] = xcoords[i].y - window->abs_y;
157           j++;
158         }
159     }
160
161   XFree (xcoords);
162
163   /* free the events we allocated too much */
164   for (i = j; i < tmp_n_events; i++)
165     {
166       g_free (coords[i]);
167       coords[i] = NULL;
168     }
169
170   tmp_n_events = j;
171
172   if (tmp_n_events == 0)
173     {
174       gdk_device_free_history (coords, tmp_n_events);
175       return FALSE;
176     }
177
178   if (n_events)
179     *n_events = tmp_n_events;
180
181   if (events)
182     *events = coords;
183   else if (coords)
184     gdk_device_free_history (coords, tmp_n_events);
185
186   return TRUE;
187 }
188
189 static void
190 gdk_x11_device_core_get_state (GdkDevice       *device,
191                                GdkWindow       *window,
192                                gdouble         *axes,
193                                GdkModifierType *mask)
194 {
195   gint x_int, y_int;
196
197   gdk_window_get_device_position (window, device, &x_int, &y_int, mask);
198
199   if (axes)
200     {
201       axes[0] = x_int;
202       axes[1] = y_int;
203     }
204 }
205
206 static void
207 gdk_x11_device_core_set_window_cursor (GdkDevice *device,
208                                        GdkWindow *window,
209                                        GdkCursor *cursor)
210 {
211   Cursor xcursor;
212
213   if (!cursor)
214     xcursor = None;
215   else
216     xcursor = gdk_x11_cursor_get_xcursor (cursor);
217
218   XDefineCursor (GDK_WINDOW_XDISPLAY (window),
219                  GDK_WINDOW_XID (window),
220                  xcursor);
221 }
222
223 static void
224 gdk_x11_device_core_warp (GdkDevice *device,
225                           GdkScreen *screen,
226                           gint       x,
227                           gint       y)
228 {
229   Display *xdisplay;
230   Window dest;
231
232   xdisplay = GDK_DISPLAY_XDISPLAY (gdk_device_get_display (device));
233   dest = GDK_WINDOW_XID (gdk_screen_get_root_window (screen));
234
235   XWarpPointer (xdisplay, None, dest, 0, 0, 0, 0, x, y);
236 }
237
238 static void
239 gdk_x11_device_core_query_state (GdkDevice        *device,
240                                  GdkWindow        *window,
241                                  GdkWindow       **root_window,
242                                  GdkWindow       **child_window,
243                                  gint             *root_x,
244                                  gint             *root_y,
245                                  gint             *win_x,
246                                  gint             *win_y,
247                                  GdkModifierType  *mask)
248 {
249   GdkDisplay *display;
250   GdkScreen *default_screen;
251   Window xroot_window, xchild_window;
252   int xroot_x, xroot_y, xwin_x, xwin_y;
253   unsigned int xmask;
254
255   display = gdk_window_get_display (window);
256   default_screen = gdk_display_get_default_screen (display);
257
258   if (!GDK_X11_DISPLAY (display)->trusted_client ||
259       !XQueryPointer (GDK_WINDOW_XDISPLAY (window),
260                       GDK_WINDOW_XID (window),
261                       &xroot_window,
262                       &xchild_window,
263                       &xroot_x, &xroot_y,
264                       &xwin_x, &xwin_y,
265                       &xmask))
266     {
267       XSetWindowAttributes attributes;
268       Display *xdisplay;
269       Window xwindow, w;
270
271       /* FIXME: untrusted clients not multidevice-safe */
272       xdisplay = GDK_SCREEN_XDISPLAY (default_screen);
273       xwindow = GDK_SCREEN_XROOTWIN (default_screen);
274
275       w = XCreateWindow (xdisplay, xwindow, 0, 0, 1, 1, 0,
276                          CopyFromParent, InputOnly, CopyFromParent,
277                          0, &attributes);
278       XQueryPointer (xdisplay, w,
279                      &xroot_window,
280                      &xchild_window,
281                      &xroot_x, &xroot_y,
282                      &xwin_x, &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
309 static GdkGrabStatus
310 gdk_x11_device_core_grab (GdkDevice    *device,
311                           GdkWindow    *window,
312                           gboolean      owner_events,
313                           GdkEventMask  event_mask,
314                           GdkWindow    *confine_to,
315                           GdkCursor    *cursor,
316                           guint32       time_)
317 {
318   GdkDisplay *display;
319   Window xwindow, xconfine_to;
320   gint status;
321
322   display = gdk_device_get_display (device);
323
324   xwindow = GDK_WINDOW_XID (window);
325
326   if (confine_to)
327     confine_to = _gdk_window_get_impl_window (confine_to);
328
329   if (!confine_to || GDK_WINDOW_DESTROYED (confine_to))
330     xconfine_to = None;
331   else
332     xconfine_to = GDK_WINDOW_XID (confine_to);
333
334 #ifdef G_ENABLE_DEBUG
335   if (_gdk_debug_flags & GDK_DEBUG_NOGRABS)
336     status = GrabSuccess;
337   else
338 #endif
339   if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
340     {
341       /* Device is a keyboard */
342       status = XGrabKeyboard (GDK_DISPLAY_XDISPLAY (display),
343                               xwindow,
344                               owner_events,
345                               GrabModeAsync, GrabModeAsync,
346                               time_);
347     }
348   else
349     {
350       Cursor xcursor;
351       guint xevent_mask;
352       gint i;
353
354       /* Device is a pointer */
355       if (!cursor)
356         xcursor = None;
357       else
358         {
359           _gdk_x11_cursor_update_theme (cursor);
360           xcursor = gdk_x11_cursor_get_xcursor (cursor);
361         }
362
363       xevent_mask = 0;
364
365       for (i = 0; i < _gdk_x11_event_mask_table_size; i++)
366         {
367           if (event_mask & (1 << (i + 1)))
368             xevent_mask |= _gdk_x11_event_mask_table[i];
369         }
370
371       /* We don't want to set a native motion hint mask, as we're emulating motion
372        * hints. If we set a native one we just wouldn't get any events.
373        */
374       xevent_mask &= ~PointerMotionHintMask;
375
376       status = XGrabPointer (GDK_DISPLAY_XDISPLAY (display),
377                              xwindow,
378                              owner_events,
379                              xevent_mask,
380                              GrabModeAsync, GrabModeAsync,
381                              xconfine_to,
382                              xcursor,
383                              time_);
384     }
385
386   _gdk_x11_display_update_grab_info (display, device, status);
387
388   return _gdk_x11_convert_grab_status (status);
389 }
390
391 static void
392 gdk_x11_device_core_ungrab (GdkDevice *device,
393                             guint32    time_)
394 {
395   GdkDisplay *display;
396   gulong serial;
397
398   display = gdk_device_get_display (device);
399   serial = NextRequest (GDK_DISPLAY_XDISPLAY (display));
400
401   if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
402     XUngrabKeyboard (GDK_DISPLAY_XDISPLAY (display), time_);
403   else
404     XUngrabPointer (GDK_DISPLAY_XDISPLAY (display), time_);
405
406   _gdk_x11_display_update_grab_info_ungrab (display, device, time_, serial);
407 }
408
409 static GdkWindow *
410 gdk_x11_device_core_window_at_position (GdkDevice       *device,
411                                         gint            *win_x,
412                                         gint            *win_y,
413                                         GdkModifierType *mask,
414                                         gboolean         get_toplevel)
415 {
416   GdkDisplay *display;
417   GdkScreen *screen;
418   Display *xdisplay;
419   GdkWindow *window;
420   Window xwindow, root, child, last;
421   int xroot_x, xroot_y, xwin_x, xwin_y;
422   unsigned int xmask;
423
424   last = None;
425   display = gdk_device_get_display (device);
426   screen = gdk_display_get_default_screen (display);
427
428   /* This function really only works if the mouse pointer is held still
429    * during its operation. If it moves from one leaf window to another
430    * than we'll end up with inaccurate values for win_x, win_y
431    * and the result.
432    */
433   gdk_x11_display_grab (display);
434
435   xdisplay = GDK_SCREEN_XDISPLAY (screen);
436   xwindow = GDK_SCREEN_XROOTWIN (screen);
437
438   if (G_LIKELY (GDK_X11_DISPLAY (display)->trusted_client))
439     {
440       XQueryPointer (xdisplay, xwindow,
441                      &root, &child,
442                      &xroot_x, &xroot_y,
443                      &xwin_x, &xwin_y,
444                      &xmask);
445
446       if (root == xwindow)
447         xwindow = child;
448       else
449        xwindow = root;
450     }
451   else
452     {
453       gint i, screens, width, height;
454       GList *toplevels, *list;
455       Window pointer_window, root, child;
456       int rootx = -1, rooty = -1;
457       int winx, winy;
458       unsigned int xmask;
459
460       /* FIXME: untrusted clients case not multidevice-safe */
461       pointer_window = None;
462       screens = gdk_display_get_n_screens (display);
463
464       for (i = 0; i < screens; ++i)
465         {
466           screen = gdk_display_get_screen (display, i);
467           toplevels = gdk_screen_get_toplevel_windows (screen);
468           for (list = toplevels; list != NULL; list = g_list_next (list))
469             {
470               window = GDK_WINDOW (list->data);
471               xwindow = GDK_WINDOW_XID (window);
472               gdk_x11_display_error_trap_push (display);
473               XQueryPointer (xdisplay, xwindow,
474                              &root, &child,
475                              &rootx, &rooty,
476                              &winx, &winy,
477                              &xmask);
478               if (gdk_x11_display_error_trap_pop (display))
479                 continue;
480               if (child != None)
481                 {
482                   pointer_window = child;
483                   break;
484                 }
485               gdk_window_get_geometry (window, NULL, NULL, &width, &height);
486               if (winx >= 0 && winy >= 0 && winx < width && winy < height)
487                 {
488                   /* A childless toplevel, or below another window? */
489                   XSetWindowAttributes attributes;
490                   Window w;
491
492                   w = XCreateWindow (xdisplay, xwindow, winx, winy, 1, 1, 0,
493                                      CopyFromParent, InputOnly, CopyFromParent,
494                                      0, &attributes);
495                   XMapWindow (xdisplay, w);
496                   XQueryPointer (xdisplay, xwindow,
497                                  &root, &child,
498                                  &rootx, &rooty,
499                                  &winx, &winy,
500                                  &xmask);
501                   XDestroyWindow (xdisplay, w);
502                   if (child == w)
503                     {
504                       pointer_window = xwindow;
505                       break;
506                     }
507                 }
508             }
509
510           g_list_free (toplevels);
511           if (pointer_window != None)
512             break;
513         }
514
515       xwindow = pointer_window;
516     }
517
518   while (xwindow)
519     {
520       last = xwindow;
521       gdk_x11_display_error_trap_push (display);
522       XQueryPointer (xdisplay, xwindow,
523                      &root, &xwindow,
524                      &xroot_x, &xroot_y,
525                      &xwin_x, &xwin_y,
526                      &xmask);
527       if (gdk_x11_display_error_trap_pop (display))
528         break;
529
530       if (get_toplevel && last != root &&
531           (window = gdk_x11_window_lookup_for_display (display, last)) != NULL &&
532           window->window_type != GDK_WINDOW_FOREIGN)
533         {
534           xwindow = last;
535           break;
536         }
537     }
538
539   gdk_x11_display_ungrab (display);
540
541   window = gdk_x11_window_lookup_for_display (display, last);
542
543   if (win_x)
544     *win_x = (window) ? xwin_x : -1;
545
546   if (win_y)
547     *win_y = (window) ? xwin_y : -1;
548
549   if (mask)
550     *mask = xmask;
551
552   return window;
553 }
554
555 static void
556 gdk_x11_device_core_select_window_events (GdkDevice    *device,
557                                          GdkWindow    *window,
558                                          GdkEventMask  event_mask)
559 {
560   GdkEventMask filter_mask, window_mask;
561   guint xmask = 0;
562   gint i;
563
564   window_mask = gdk_window_get_events (window);
565   filter_mask = (GDK_POINTER_MOTION_MASK &
566                  GDK_POINTER_MOTION_HINT_MASK &
567                  GDK_BUTTON_MOTION_MASK &
568                  GDK_BUTTON1_MOTION_MASK &
569                  GDK_BUTTON2_MOTION_MASK &
570                  GDK_BUTTON3_MOTION_MASK &
571                  GDK_BUTTON_PRESS_MASK &
572                  GDK_BUTTON_RELEASE_MASK &
573                  GDK_KEY_PRESS_MASK &
574                  GDK_KEY_RELEASE_MASK &
575                  GDK_ENTER_NOTIFY_MASK &
576                  GDK_LEAVE_NOTIFY_MASK &
577                  GDK_FOCUS_CHANGE_MASK &
578                  GDK_PROXIMITY_IN_MASK &
579                  GDK_PROXIMITY_OUT_MASK &
580                  GDK_SCROLL_MASK);
581
582   /* Filter out non-device events */
583   event_mask &= filter_mask;
584
585   /* Unset device events on window mask */
586   window_mask &= ~(filter_mask);
587
588   /* Combine masks */
589   event_mask |= window_mask;
590
591   for (i = 0; i < _gdk_x11_event_mask_table_size; i++)
592     {
593       if (event_mask & (1 << (i + 1)))
594         xmask |= _gdk_x11_event_mask_table[i];
595     }
596
597   if (GDK_WINDOW_XID (window) != GDK_WINDOW_XROOTWIN (window))
598     xmask |= StructureNotifyMask | PropertyChangeMask;
599
600   XSelectInput (GDK_WINDOW_XDISPLAY (window),
601                 GDK_WINDOW_XID (window),
602                 xmask);
603 }