]> Pileus Git - ~andy/gtk/blob - gdk/win32/gdkdevice-win32.c
win32: in window_at_pointer, ensure that we handle non-client areas correctly
[~andy/gtk] / gdk / win32 / gdkdevice-win32.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 <gdk/gdkwindow.h>
23
24 #include <windowsx.h>
25 #include <objbase.h>
26
27 #include "gdkdisplayprivate.h"
28 #include "gdkdevice-win32.h"
29 #include "gdkwin32.h"
30
31 static gboolean gdk_device_win32_get_history (GdkDevice      *device,
32                                               GdkWindow      *window,
33                                               guint32         start,
34                                               guint32         stop,
35                                               GdkTimeCoord ***events,
36                                               gint           *n_events);
37 static void gdk_device_win32_get_state (GdkDevice       *device,
38                                         GdkWindow       *window,
39                                         gdouble         *axes,
40                                         GdkModifierType *mask);
41 static void gdk_device_win32_set_window_cursor (GdkDevice *device,
42                                                 GdkWindow *window,
43                                                 GdkCursor *cursor);
44 static void gdk_device_win32_warp (GdkDevice *device,
45                                    GdkScreen *screen,
46                                    gint       x,
47                                    gint       y);
48 static gboolean gdk_device_win32_query_state (GdkDevice        *device,
49                                               GdkWindow        *window,
50                                               GdkWindow       **root_window,
51                                               GdkWindow       **child_window,
52                                               gint             *root_x,
53                                               gint             *root_y,
54                                               gint             *win_x,
55                                               gint             *win_y,
56                                               GdkModifierType  *mask);
57 static GdkGrabStatus gdk_device_win32_grab   (GdkDevice     *device,
58                                               GdkWindow     *window,
59                                               gboolean       owner_events,
60                                               GdkEventMask   event_mask,
61                                               GdkWindow     *confine_to,
62                                               GdkCursor     *cursor,
63                                               guint32        time_);
64 static void          gdk_device_win32_ungrab (GdkDevice     *device,
65                                               guint32        time_);
66 static GdkWindow * gdk_device_win32_window_at_position (GdkDevice       *device,
67                                                         gint            *win_x,
68                                                         gint            *win_y,
69                                                         GdkModifierType *mask,
70                                                         gboolean         get_toplevel);
71 static void      gdk_device_win32_select_window_events (GdkDevice       *device,
72                                                         GdkWindow       *window,
73                                                         GdkEventMask     event_mask);
74
75
76 G_DEFINE_TYPE (GdkDeviceWin32, gdk_device_win32, GDK_TYPE_DEVICE)
77
78 static void
79 gdk_device_win32_class_init (GdkDeviceWin32Class *klass)
80 {
81   GdkDeviceClass *device_class = GDK_DEVICE_CLASS (klass);
82
83   device_class->get_history = gdk_device_win32_get_history;
84   device_class->get_state = gdk_device_win32_get_state;
85   device_class->set_window_cursor = gdk_device_win32_set_window_cursor;
86   device_class->warp = gdk_device_win32_warp;
87   device_class->query_state = gdk_device_win32_query_state;
88   device_class->grab = gdk_device_win32_grab;
89   device_class->ungrab = gdk_device_win32_ungrab;
90   device_class->window_at_position = gdk_device_win32_window_at_position;
91   device_class->select_window_events = gdk_device_win32_select_window_events;
92 }
93
94 static void
95 gdk_device_win32_init (GdkDeviceWin32 *device_win32)
96 {
97   GdkDevice *device;
98
99   device = GDK_DEVICE (device_win32);
100
101   _gdk_device_add_axis (device, GDK_NONE, GDK_AXIS_X, 0, 0, 1);
102   _gdk_device_add_axis (device, GDK_NONE, GDK_AXIS_Y, 0, 0, 1);
103 }
104
105 static gboolean
106 gdk_device_win32_get_history (GdkDevice      *device,
107                               GdkWindow      *window,
108                               guint32         start,
109                               guint32         stop,
110                               GdkTimeCoord ***events,
111                               gint           *n_events)
112 {
113   return FALSE;
114 }
115
116 static void
117 gdk_device_win32_get_state (GdkDevice       *device,
118                             GdkWindow       *window,
119                             gdouble         *axes,
120                             GdkModifierType *mask)
121 {
122   gint x_int, y_int;
123
124   gdk_window_get_pointer (window, &x_int, &y_int, mask);
125
126   if (axes)
127     {
128       axes[0] = x_int;
129       axes[1] = y_int;
130     }
131 }
132
133 static void
134 gdk_device_win32_set_window_cursor (GdkDevice *device,
135                                     GdkWindow *window,
136                                     GdkCursor *cursor)
137 {
138   GdkWin32Cursor *cursor_private;
139   GdkWindow *parent_window;
140   GdkWindowImplWin32 *impl;
141   HCURSOR hcursor;
142   HCURSOR hprevcursor;
143
144   impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
145   cursor_private = (GdkWin32Cursor*) cursor;
146
147   hprevcursor = impl->hcursor;
148
149   if (!cursor)
150     hcursor = NULL;
151   else
152     hcursor = cursor_private->hcursor;
153
154   if (hcursor != NULL)
155     {
156       /* If the pointer is over our window, set new cursor */
157       GdkWindow *curr_window = gdk_window_get_pointer (window, NULL, NULL, NULL);
158
159       if (curr_window == window ||
160           (curr_window && window == gdk_window_get_toplevel (curr_window)))
161         SetCursor (hcursor);
162       else
163         {
164           /* Climb up the tree and find whether our window is the
165            * first ancestor that has cursor defined, and if so, set
166            * new cursor.
167            */
168           while (curr_window && curr_window->impl &&
169                  !GDK_WINDOW_IMPL_WIN32 (curr_window->impl)->hcursor)
170             {
171               curr_window = curr_window->parent;
172               if (curr_window == GDK_WINDOW (window))
173                 {
174                   SetCursor (hcursor);
175                   break;
176                 }
177             }
178         }
179     }
180
181   /* Unset the previous cursor: Need to make sure it's no longer in
182    * use before we destroy it, in case we're not over our window but
183    * the cursor is still set to our old one.
184    */
185   if (hprevcursor != NULL &&
186       GetCursor () == hprevcursor)
187     {
188       /* Look for a suitable cursor to use instead */
189       hcursor = NULL;
190       parent_window = GDK_WINDOW (window)->parent;
191
192       while (hcursor == NULL)
193         {
194           if (parent_window)
195             {
196               impl = GDK_WINDOW_IMPL_WIN32 (parent_window->impl);
197               hcursor = impl->hcursor;
198               parent_window = parent_window->parent;
199             }
200           else
201             hcursor = LoadCursor (NULL, IDC_ARROW);
202         }
203
204       SetCursor (hcursor);
205     }
206 }
207
208 static void
209 gdk_device_win32_warp (GdkDevice *device,
210                        GdkScreen *screen,
211                        gint       x,
212                        gint       y)
213 {
214   SetCursorPos (x - _gdk_offset_x, y - _gdk_offset_y);
215 }
216
217 static GdkModifierType
218 get_current_mask (void)
219 {
220   GdkModifierType mask;
221   BYTE kbd[256];
222
223   GetKeyboardState (kbd);
224   mask = 0;
225   if (kbd[VK_SHIFT] & 0x80)
226     mask |= GDK_SHIFT_MASK;
227   if (kbd[VK_CAPITAL] & 0x80)
228     mask |= GDK_LOCK_MASK;
229   if (kbd[VK_CONTROL] & 0x80)
230     mask |= GDK_CONTROL_MASK;
231   if (kbd[VK_MENU] & 0x80)
232     mask |= GDK_MOD1_MASK;
233   if (kbd[VK_LBUTTON] & 0x80)
234     mask |= GDK_BUTTON1_MASK;
235   if (kbd[VK_MBUTTON] & 0x80)
236     mask |= GDK_BUTTON2_MASK;
237   if (kbd[VK_RBUTTON] & 0x80)
238     mask |= GDK_BUTTON3_MASK;
239
240   return mask;
241 }
242
243 static gboolean
244 gdk_device_win32_query_state (GdkDevice        *device,
245                               GdkWindow        *window,
246                               GdkWindow       **root_window,
247                               GdkWindow       **child_window,
248                               gint             *root_x,
249                               gint             *root_y,
250                               gint             *win_x,
251                               gint             *win_y,
252                               GdkModifierType  *mask)
253 {
254   gboolean return_val;
255   POINT point;
256   HWND hwnd, hwndc;
257
258   g_return_val_if_fail (window == NULL || GDK_IS_WINDOW (window), FALSE);
259   
260   return_val = TRUE;
261
262   hwnd = GDK_WINDOW_HWND (window);
263   GetCursorPos (&point);
264
265   if (root_x)
266     *root_x = point.x;
267
268   if (root_y)
269     *root_y = point.y;
270
271   ScreenToClient (hwnd, &point);
272
273   if (win_x)
274     *win_x = point.x;
275
276   if (win_y)
277     *win_y = point.y;
278
279   if (window == _gdk_root)
280     {
281       if (win_x)
282         *win_x += _gdk_offset_x;
283
284       if (win_y)
285         *win_y += _gdk_offset_y;
286     }
287
288   if (child_window)
289     {
290       hwndc = ChildWindowFromPoint (hwnd, point);
291
292       if (hwndc && hwndc != hwnd)
293         *child_window = gdk_win32_handle_table_lookup (hwndc);
294       else
295         *child_window = NULL; /* Direct child unknown to gdk */
296     }
297
298   if (root_window)
299     {
300       GdkScreen *screen;
301
302       screen = gdk_window_get_screen (window);
303       *root_window = gdk_screen_get_root_window (screen);
304     }
305
306   if (mask)
307     *mask = get_current_mask ();
308
309   return TRUE;
310 }
311
312 static GdkGrabStatus
313 gdk_device_win32_grab (GdkDevice    *device,
314                        GdkWindow    *window,
315                        gboolean      owner_events,
316                        GdkEventMask  event_mask,
317                        GdkWindow    *confine_to,
318                        GdkCursor    *cursor,
319                        guint32       time_)
320 {
321   if (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD)
322     SetCapture (GDK_WINDOW_HWND (window));
323
324   return GDK_GRAB_SUCCESS;
325 }
326
327 static void
328 gdk_device_win32_ungrab (GdkDevice *device,
329                          guint32    time_)
330 {
331   GdkDeviceGrabInfo *info;
332   GdkDisplay *display;
333
334   display = gdk_device_get_display (device);
335   info = _gdk_display_get_last_device_grab (display, device);
336
337   if (info)
338     info->serial_end = 0;
339
340   if (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD)
341     ReleaseCapture ();
342
343   _gdk_display_device_grab_update (display, device, NULL, 0);
344 }
345
346 static void
347 screen_to_client (HWND hwnd, POINT screen_pt, POINT *client_pt)
348 {
349   *client_pt = screen_pt;
350   ScreenToClient (hwnd, client_pt);
351 }
352
353 static GdkWindow *
354 gdk_device_win32_window_at_position (GdkDevice       *device,
355                                      gint            *win_x,
356                                      gint            *win_y,
357                                      GdkModifierType *mask,
358                                      gboolean         get_toplevel)
359 {
360   GdkWindow *window = NULL;
361   POINT screen_pt, client_pt;
362   HWND hwnd, hwndc;
363   RECT rect;
364
365   GetCursorPos (&screen_pt);
366
367   if (get_toplevel)
368     {
369       /* Only consider visible children of the desktop to avoid the various
370        * non-visible windows you often find on a running Windows box. These
371        * might overlap our windows and cause our walk to fail. As we assume
372        * WindowFromPoint() can find our windows, we follow similar logic
373        * here, and ignore invisible and disabled windows.
374        */
375       hwnd = GetDesktopWindow ();
376       do {
377         window = gdk_win32_handle_table_lookup (hwnd);
378
379         if (window != NULL &&
380             GDK_WINDOW_TYPE (window) != GDK_WINDOW_ROOT &&
381             GDK_WINDOW_TYPE (window) != GDK_WINDOW_FOREIGN)
382           break;
383
384         screen_to_client (hwnd, screen_pt, &client_pt);
385         hwndc = ChildWindowFromPointEx (hwnd, client_pt, CWP_SKIPDISABLED  |
386                                                          CWP_SKIPINVISIBLE);
387
388         /* Verify that we're really inside the client area of the window */
389         if (hwndc != hwnd)
390           {
391             GetClientRect (hwndc, &rect);
392             screen_to_client (hwndc, screen_pt, &client_pt);
393             if (!PtInRect (&rect, client_pt))
394               hwndc = hwnd;
395           }
396
397       } while (hwndc != hwnd && (hwnd = hwndc, 1));
398
399     }
400   else
401     {
402       hwnd = WindowFromPoint (screen_pt);
403
404       /* Verify that we're really inside the client area of the window */
405       GetClientRect (hwnd, &rect);
406       screen_to_client (hwnd, screen_pt, &client_pt);
407       if (!PtInRect (&rect, client_pt))
408         hwnd = NULL;
409
410       /* If we didn't hit any window at that point, return the desktop */
411       if (hwnd == NULL)
412         {
413           if (win_x)
414             *win_x = screen_pt.x + _gdk_offset_x;
415           if (win_y)
416             *win_y = screen_pt.y + _gdk_offset_y;
417           return _gdk_root;
418         }
419
420       window = gdk_win32_handle_table_lookup (hwnd);
421     }
422
423   if (window && (win_x || win_y))
424     {
425       if (win_x)
426         *win_x = client_pt.x;
427       if (win_y)
428         *win_y = client_pt.y;
429     }
430
431   return window;
432 }
433
434 static void
435 gdk_device_win32_select_window_events (GdkDevice    *device,
436                                        GdkWindow    *window,
437                                        GdkEventMask  event_mask)
438 {
439 }