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