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