1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 2021 the GTK team
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 <gdk/gdk.h>
21 #include "gdkwin32.h"
22 #include "gdkprivate-win32.h"
23 #include "gdkdevicemanager-win32.h"
24 #include "gdkdevice-virtual.h"
25 #include "gdkdevice-winpointer.h"
26 #include "gdkdeviceprivate.h"
27 #include "gdkdisplayprivate.h"
28 #include "gdkseatdefaultprivate.h"
29 #include "gdkdevicetoolprivate.h"
30 #include "gdkinput-winpointer.h"
31 
32 #include <windows.h>
33 
34 #include <tchar.h>
35 #include <tpcshrd.h>
36 #include <hidsdi.h>
37 
38 #define HID_STRING_BYTES_LIMIT 200
39 #define VID_PID_CHARS 4
40 
41 typedef BOOL
42 (WINAPI *registerPointerDeviceNotifications_t)(HWND window, BOOL notifyRange);
43 typedef BOOL
44 (WINAPI *getPointerDevices_t)(UINT32 *deviceCount, POINTER_DEVICE_INFO *pointerDevices);
45 typedef BOOL
46 (WINAPI *getPointerDeviceCursors_t)(HANDLE device, UINT32 *cursorCount, POINTER_DEVICE_CURSOR_INFO *deviceCursors);
47 typedef BOOL
48 (WINAPI *getPointerDeviceRects_t)(HANDLE device, RECT *pointerDeviceRect, RECT *displayRect);
49 typedef BOOL
50 (WINAPI *getPointerType_t)(UINT32 pointerId, POINTER_INPUT_TYPE *pointerType);
51 typedef BOOL
52 (WINAPI *getPointerCursorId_t)(UINT32 pointerId, UINT32 *cursorId);
53 typedef BOOL
54 (WINAPI *getPointerPenInfo_t)(UINT32 pointerId, POINTER_PEN_INFO *penInfo);
55 typedef BOOL
56 (WINAPI *getPointerTouchInfo_t)(UINT32 pointerId, POINTER_TOUCH_INFO *touchInfo);
57 typedef BOOL
58 (WINAPI *getPointerPenInfoHistory_t)(UINT32 pointerId, UINT32 *entriesCount, POINTER_PEN_INFO *penInfo);
59 typedef BOOL
60 (WINAPI *getPointerTouchInfoHistory_t)(UINT32 pointerId, UINT32 *entriesCount, POINTER_TOUCH_INFO *touchInfo);
61 typedef BOOL
62 (WINAPI *setGestureConfig_t)(HWND hwnd, DWORD dwReserved, UINT cIDs, PGESTURECONFIG pGestureConfig, UINT cbSize);
63 typedef BOOL
64 (WINAPI *setWindowFeedbackSetting_t)(HWND hwnd, FEEDBACK_TYPE feedback, DWORD dwFlags, UINT32 size, const VOID *configuration);
65 
66 static registerPointerDeviceNotifications_t registerPointerDeviceNotifications;
67 static getPointerDevices_t getPointerDevices;
68 static getPointerDeviceCursors_t getPointerDeviceCursors;
69 static getPointerDeviceRects_t getPointerDeviceRects;
70 static getPointerType_t getPointerType;
71 static getPointerCursorId_t getPointerCursorId;
72 static getPointerPenInfo_t getPointerPenInfo;
73 static getPointerTouchInfo_t getPointerTouchInfo;
74 static getPointerPenInfoHistory_t getPointerPenInfoHistory;
75 static getPointerTouchInfoHistory_t getPointerTouchInfoHistory;
76 static setGestureConfig_t setGestureConfig;
77 static setWindowFeedbackSetting_t setWindowFeedbackSetting;
78 
79 static ATOM notifications_window_class;
80 static HWND notifications_window_handle;
81 
82 static GPtrArray *ignored_interactions;
83 
84 static inline void
winpointer_ignore_interaction(UINT32 pointer_id)85 winpointer_ignore_interaction (UINT32 pointer_id)
86 {
87   g_ptr_array_add (ignored_interactions, GUINT_TO_POINTER (pointer_id));
88 }
89 
90 static inline void
winpointer_remove_ignored_interaction(UINT32 pointer_id)91 winpointer_remove_ignored_interaction (UINT32 pointer_id)
92 {
93   g_ptr_array_remove_fast (ignored_interactions, GUINT_TO_POINTER (pointer_id));
94 }
95 
96 static inline gboolean
winpointer_should_ignore_interaction(UINT32 pointer_id)97 winpointer_should_ignore_interaction (UINT32 pointer_id)
98 {
99   return g_ptr_array_find (ignored_interactions, GUINT_TO_POINTER (pointer_id), NULL);
100 }
101 
102 static inline guint32
winpointer_get_time(MSG * msg,POINTER_INFO * info)103 winpointer_get_time (MSG *msg,
104                      POINTER_INFO *info)
105 {
106   return info->dwTime != 0 ? info->dwTime : msg->time;
107 }
108 
109 static inline gboolean
winpointer_is_eraser(POINTER_PEN_INFO * pen_info)110 winpointer_is_eraser (POINTER_PEN_INFO *pen_info)
111 {
112   return (pen_info->penFlags & (PEN_FLAG_INVERTED | PEN_FLAG_ERASER)) != 0;
113 }
114 
115 static inline gboolean
winpointer_should_filter_message(MSG * msg,POINTER_INPUT_TYPE type)116 winpointer_should_filter_message (MSG *msg,
117                                   POINTER_INPUT_TYPE type)
118 {
119   switch (type)
120     {
121     case PT_TOUCH:
122       return msg->message == WM_POINTERENTER ||
123              msg->message == WM_POINTERLEAVE;
124     break;
125     }
126 
127   return FALSE;
128 }
129 
130 static inline double*
copy_axes(double * axes)131 copy_axes (double *axes)
132 {
133   return g_memdup2 (axes, sizeof (double) * GDK_AXIS_LAST);
134 }
135 
136 static GdkDeviceWinpointer*
winpointer_find_device_with_source(HANDLE device_handle,UINT32 cursor_id,GdkInputSource input_source)137 winpointer_find_device_with_source (HANDLE device_handle,
138                                     UINT32 cursor_id,
139                                     GdkInputSource input_source)
140 {
141   for (GList *l = _gdk_device_manager->winpointer_devices; l != NULL; l = l->next)
142     {
143       GdkDeviceWinpointer *device = (GdkDeviceWinpointer*) l->data;
144 
145       if (device->device_handle == device_handle &&
146           device->start_cursor_id <= cursor_id &&
147           device->end_cursor_id >= cursor_id &&
148           gdk_device_get_source ((GdkDevice*) device) == input_source)
149         {
150           return device;
151         }
152     }
153 
154   return NULL;
155 }
156 
157 static gboolean
winpointer_get_event_type(MSG * msg,POINTER_INFO * info,GdkEventType * evt_type)158 winpointer_get_event_type (MSG *msg,
159                            POINTER_INFO *info,
160                            GdkEventType *evt_type)
161 {
162   switch (info->pointerType)
163     {
164     case PT_PEN:
165       switch (msg->message)
166         {
167         case WM_POINTERENTER:
168           g_return_val_if_fail (IS_POINTER_NEW_WPARAM (msg->wParam), FALSE);
169           *evt_type = GDK_PROXIMITY_IN;
170         return TRUE;
171         case WM_POINTERLEAVE:
172           g_return_val_if_fail (!IS_POINTER_INRANGE_WPARAM (msg->wParam), FALSE);
173           *evt_type = GDK_PROXIMITY_OUT;
174         return TRUE;
175         case WM_POINTERDOWN:
176           *evt_type = GDK_BUTTON_PRESS;
177         return TRUE;
178         case WM_POINTERUP:
179           *evt_type = GDK_BUTTON_RELEASE;
180         return TRUE;
181         case WM_POINTERUPDATE:
182           *evt_type = GDK_MOTION_NOTIFY;
183         return TRUE;
184         }
185     break;
186     case PT_TOUCH:
187       if (IS_POINTER_CANCELED_WPARAM (msg->wParam) ||
188           !HAS_POINTER_CONFIDENCE_WPARAM (msg->wParam))
189         {
190           winpointer_ignore_interaction (GET_POINTERID_WPARAM (msg->wParam));
191 
192           if (((info->pointerFlags & POINTER_FLAG_INCONTACT) &&
193                (info->pointerFlags & POINTER_FLAG_UPDATE)) ||
194               (info->pointerFlags & POINTER_FLAG_UP))
195             {
196               *evt_type = GDK_TOUCH_CANCEL;
197               return TRUE;
198             }
199           else
200             return FALSE;
201         }
202 
203       g_return_val_if_fail (msg->message != WM_POINTERENTER &&
204                             msg->message != WM_POINTERLEAVE, FALSE);
205 
206       switch (msg->message)
207         {
208           case WM_POINTERDOWN:
209             *evt_type = GDK_TOUCH_BEGIN;
210           return TRUE;
211           case WM_POINTERUP:
212             *evt_type = GDK_TOUCH_END;
213           return TRUE;
214           case WM_POINTERUPDATE:
215             if (!IS_POINTER_INCONTACT_WPARAM (msg->wParam))
216               return FALSE;
217             *evt_type = GDK_TOUCH_UPDATE;
218           return TRUE;
219         }
220     break;
221     }
222 
223   g_warn_if_reached ();
224 
225   return FALSE;
226 }
227 
228 static void
winpointer_make_event(GdkDeviceWinpointer * device,GdkDeviceTool * tool,GdkSurface * surface,MSG * msg,POINTER_INFO * info)229 winpointer_make_event (GdkDeviceWinpointer *device,
230                        GdkDeviceTool *tool,
231                        GdkSurface *surface,
232                        MSG *msg,
233                        POINTER_INFO *info)
234 {
235   guint32 time = 0;
236   double screen_x = 0.0;
237   double screen_y = 0.0;
238   double x = 0.0;
239   double y = 0.0;
240   unsigned int state = 0;
241   unsigned int button = 0;
242   double axes[GDK_AXIS_LAST];
243   GdkEventSequence *sequence = NULL;
244   gboolean emulating_pointer = FALSE;
245   POINT client_area_coordinates;
246   GdkWin32Surface *impl = NULL;
247   GdkEventType evt_type;
248   GdkEvent *evt = NULL;
249   GdkDevice *core_device = NULL;
250 
251   core_device = _gdk_device_manager->core_pointer;
252 
253   if (!winpointer_get_event_type (msg, info, &evt_type))
254     return;
255 
256   time = winpointer_get_time (msg, info);
257 
258   screen_x = device->origin_x + info->ptHimetricLocation.x * device->scale_x;
259   screen_y = device->origin_y + info->ptHimetricLocation.y * device->scale_y;
260 
261   client_area_coordinates.x = 0;
262   client_area_coordinates.y = 0;
263   ClientToScreen (GDK_SURFACE_HWND (surface), &client_area_coordinates);
264   x = screen_x - client_area_coordinates.x;
265   y = screen_y - client_area_coordinates.y;
266 
267   impl = GDK_WIN32_SURFACE (surface);
268   x /= impl->surface_scale;
269   y /= impl->surface_scale;
270 
271   state = 0;
272   if (info->dwKeyStates & POINTER_MOD_CTRL)
273     state |= GDK_CONTROL_MASK;
274   if (info->dwKeyStates & POINTER_MOD_SHIFT)
275     state |= GDK_SHIFT_MASK;
276   if (GetKeyState (VK_MENU) < 0)
277     state |= GDK_ALT_MASK;
278   if (GetKeyState (VK_CAPITAL) & 0x1)
279     state |= GDK_LOCK_MASK;
280 
281   device->last_button_mask = 0;
282   if (((info->pointerFlags & POINTER_FLAG_FIRSTBUTTON) &&
283        (info->ButtonChangeType != POINTER_CHANGE_FIRSTBUTTON_DOWN))
284       || info->ButtonChangeType == POINTER_CHANGE_FIRSTBUTTON_UP)
285     device->last_button_mask |= GDK_BUTTON1_MASK;
286   if (((info->pointerFlags & POINTER_FLAG_SECONDBUTTON) &&
287        (info->ButtonChangeType != POINTER_CHANGE_SECONDBUTTON_DOWN))
288       || info->ButtonChangeType == POINTER_CHANGE_SECONDBUTTON_UP)
289     device->last_button_mask |= GDK_BUTTON3_MASK;
290   state |= device->last_button_mask;
291 
292   memset (axes, 0, sizeof (axes));
293   switch (info->pointerType)
294     {
295     case PT_PEN:
296       {
297         POINTER_PEN_INFO *pen_info = (POINTER_PEN_INFO*) info;
298 
299         axes[GDK_AXIS_PRESSURE] = (pen_info->penMask & PEN_MASK_PRESSURE) ?
300                                    pen_info->pressure / 1024.0 :
301                                   (pen_info->pointerInfo.pointerFlags & POINTER_FLAG_INCONTACT) ?
302                                    1.0 : 0.0;
303         axes[GDK_AXIS_XTILT] = (pen_info->penMask & PEN_MASK_TILT_X) ?
304                                 pen_info->tiltX / 90.0 : 0.0;
305         axes[GDK_AXIS_YTILT] = (pen_info->penMask & PEN_MASK_TILT_Y) ?
306                                 pen_info->tiltY / 90.0 : 0.0;
307         axes[GDK_AXIS_ROTATION] = (pen_info->penMask & PEN_MASK_ROTATION) ?
308                                    pen_info->rotation / 360.0 : 0.0;
309       }
310     break;
311     case PT_TOUCH:
312       {
313         POINTER_TOUCH_INFO *touch_info = (POINTER_TOUCH_INFO*) info;
314 
315         axes[GDK_AXIS_PRESSURE] = (touch_info->touchMask & TOUCH_MASK_PRESSURE) ?
316                                    touch_info->pressure / 1024.0 :
317                                   (touch_info->pointerInfo.pointerFlags & POINTER_FLAG_INCONTACT) ?
318                                    1.0 : 0.0;
319       }
320     break;
321     }
322 
323   sequence = (GdkEventSequence*) GUINT_TO_POINTER (info->pointerId);
324   emulating_pointer = (info->pointerFlags & POINTER_FLAG_PRIMARY) != 0;
325   button = (info->pointerFlags & POINTER_FLAG_FIRSTBUTTON) ||
326            (info->ButtonChangeType == POINTER_CHANGE_FIRSTBUTTON_UP) ? 1 : 3;
327 
328   switch (evt_type)
329     {
330     case GDK_PROXIMITY_IN:
331     case GDK_PROXIMITY_OUT:
332       evt = gdk_proximity_event_new (evt_type,
333                                      surface,
334                                      core_device,
335                                      tool,
336                                      time);
337     break;
338     case GDK_BUTTON_PRESS:
339     case GDK_BUTTON_RELEASE:
340       evt = gdk_button_event_new (evt_type,
341                                   surface,
342                                   core_device,
343                                   tool,
344                                   time,
345                                   state,
346                                   button,
347                                   x,
348                                   y,
349                                   copy_axes (axes));
350     break;
351     case GDK_MOTION_NOTIFY:
352       evt = gdk_motion_event_new (surface,
353                                   core_device,
354                                   tool,
355                                   time,
356                                   state,
357                                   x,
358                                   y,
359                                   copy_axes (axes));
360     break;
361     case GDK_TOUCH_BEGIN:
362     case GDK_TOUCH_UPDATE:
363     case GDK_TOUCH_CANCEL:
364     case GDK_TOUCH_END:
365       evt = gdk_touch_event_new (evt_type,
366                                  sequence,
367                                  surface,
368                                  core_device,
369                                  time,
370                                  state,
371                                  x,
372                                  y,
373                                  copy_axes (axes),
374                                  emulating_pointer);
375     break;
376     default:
377       g_warn_if_reached ();
378     break;
379     }
380 
381   if (evt_type == GDK_PROXIMITY_OUT)
382     gdk_device_update_tool ((GdkDevice*) device, NULL);
383 
384   if (G_LIKELY (evt))
385     {
386       _gdk_device_virtual_set_active (core_device, (GdkDevice*) device);
387       _gdk_win32_append_event (evt);
388     }
389 }
390 
391 void
gdk_winpointer_input_events(GdkSurface * surface,crossing_cb_t crossing_cb,MSG * msg)392 gdk_winpointer_input_events (GdkSurface *surface,
393                              crossing_cb_t crossing_cb,
394                              MSG *msg)
395 {
396   UINT32 pointer_id = GET_POINTERID_WPARAM (msg->wParam);
397   POINTER_INPUT_TYPE type = PT_POINTER;
398   UINT32 cursor_id = 0;
399 
400   if (!getPointerType (pointer_id, &type))
401     {
402       WIN32_API_FAILED_LOG_ONCE ("GetPointerType");
403       return;
404     }
405 
406   if (!getPointerCursorId (pointer_id, &cursor_id))
407     {
408       WIN32_API_FAILED_LOG_ONCE ("GetPointerCursorId");
409       return;
410     }
411 
412   if (winpointer_should_filter_message (msg, type))
413     return;
414 
415   if (winpointer_should_ignore_interaction (pointer_id))
416     return;
417 
418   switch (type)
419     {
420     case PT_PEN:
421       {
422         POINTER_PEN_INFO *infos = NULL;
423         UINT32 history_count = 0;
424         GdkDeviceWinpointer *device = NULL;
425         GdkDeviceTool *tool = NULL;
426         UINT32 h = 0;
427 
428         do
429           {
430             infos = g_new0 (POINTER_PEN_INFO, history_count);
431             if (!getPointerPenInfoHistory (pointer_id, &history_count, infos))
432               {
433                 WIN32_API_FAILED_LOG_ONCE ("GetPointerPenInfoHistory");
434                 g_free (infos);
435                 return;
436               }
437           }
438         while (!infos && history_count > 0);
439 
440         if (G_UNLIKELY (history_count == 0))
441           return;
442 
443         device = winpointer_find_device_with_source (infos->pointerInfo.sourceDevice, cursor_id, GDK_SOURCE_PEN);
444         if (G_UNLIKELY (!device))
445           {
446             g_free (infos);
447             return;
448           }
449 
450         if (!winpointer_is_eraser (infos))
451           tool = device->tool_pen;
452         else
453           tool = device->tool_eraser;
454 
455         gdk_device_update_tool ((GdkDevice*) device, tool);
456 
457         h = history_count - 1;
458 
459         if (crossing_cb)
460           {
461             POINT screen_pt = infos[h].pointerInfo.ptPixelLocation;
462             guint32 event_time = winpointer_get_time (msg, &infos[h].pointerInfo);
463 
464             crossing_cb(GDK_DEVICE (device), surface, &screen_pt, event_time);
465           }
466 
467         do
468           winpointer_make_event (device, tool, surface, msg, (POINTER_INFO*) &infos[h]);
469         while (h-- > 0);
470 
471         g_free (infos);
472       }
473     break;
474     case PT_TOUCH:
475       {
476         POINTER_TOUCH_INFO *infos = NULL;
477         UINT32 history_count = 0;
478         GdkDeviceWinpointer *device = NULL;
479         UINT32 h = 0;
480 
481         do
482           {
483             infos = g_new0 (POINTER_TOUCH_INFO, history_count);
484             if (!getPointerTouchInfoHistory (pointer_id, &history_count, infos))
485               {
486                 WIN32_API_FAILED_LOG_ONCE ("GetPointerTouchInfoHistory");
487                 g_free (infos);
488                 return;
489               }
490           }
491         while (!infos && history_count > 0);
492 
493         if (G_UNLIKELY (history_count == 0))
494           return;
495 
496         device = winpointer_find_device_with_source (infos->pointerInfo.sourceDevice, cursor_id, GDK_SOURCE_TOUCHSCREEN);
497         if (G_UNLIKELY (!device))
498           {
499             g_free (infos);
500             return;
501           }
502 
503         h = history_count - 1;
504 
505         if (crossing_cb)
506           {
507             POINT screen_pt = infos[h].pointerInfo.ptPixelLocation;
508             guint32 event_time = winpointer_get_time (msg, &infos[h].pointerInfo);
509 
510             crossing_cb(GDK_DEVICE (device), surface, &screen_pt, event_time);
511           }
512 
513         do
514           winpointer_make_event (device, NULL, surface, msg, (POINTER_INFO*) &infos[h]);
515         while (h-- > 0);
516 
517         g_free (infos);
518       }
519     break;
520     }
521 }
522 
523 gboolean
gdk_winpointer_get_message_info(MSG * msg,GdkDevice ** device,guint32 * time)524 gdk_winpointer_get_message_info (MSG *msg,
525                                  GdkDevice **device,
526                                  guint32 *time)
527 {
528   UINT32 pointer_id = GET_POINTERID_WPARAM (msg->wParam);
529   POINTER_INPUT_TYPE type = PT_POINTER;
530   UINT32 cursor_id = 0;
531 
532   if (!getPointerType (pointer_id, &type))
533     {
534       WIN32_API_FAILED_LOG_ONCE ("GetPointerType");
535       return FALSE;
536     }
537 
538   if (!getPointerCursorId (pointer_id, &cursor_id))
539     {
540       WIN32_API_FAILED_LOG_ONCE ("GetPointerCursorId");
541       return FALSE;
542     }
543 
544   switch (type)
545     {
546     case PT_PEN:
547       {
548         POINTER_PEN_INFO pen_info;
549 
550         if (!getPointerPenInfo (pointer_id, &pen_info))
551           {
552             WIN32_API_FAILED_LOG_ONCE ("GetPointerPenInfo");
553             return FALSE;
554           }
555 
556         *device = (GdkDevice*) winpointer_find_device_with_source (pen_info.pointerInfo.sourceDevice, cursor_id, GDK_SOURCE_PEN);
557         *time = winpointer_get_time (msg, &pen_info.pointerInfo);
558       }
559     break;
560     case PT_TOUCH:
561       {
562         POINTER_TOUCH_INFO touch_info;
563 
564         if (!getPointerTouchInfo (pointer_id, &touch_info))
565             {
566               WIN32_API_FAILED_LOG_ONCE ("GetPointerTouchInfo");
567               return FALSE;
568             }
569 
570         *device = GDK_DEVICE (winpointer_find_device_with_source (touch_info.pointerInfo.sourceDevice,
571                                                                   cursor_id,
572                                                                   GDK_SOURCE_TOUCHSCREEN));
573 
574         *time = winpointer_get_time (msg, &touch_info.pointerInfo);
575       }
576     break;
577     default:
578       g_warn_if_reached ();
579       return FALSE;
580     break;
581     }
582 
583   return *device ? TRUE : FALSE;
584 }
585 
586 gboolean
gdk_winpointer_should_forward_message(MSG * msg)587 gdk_winpointer_should_forward_message (MSG *msg)
588 {
589   UINT32 pointer_id = GET_POINTERID_WPARAM (msg->wParam);
590   POINTER_INPUT_TYPE type = PT_POINTER;
591 
592   if (!getPointerType (pointer_id, &type))
593     {
594       WIN32_API_FAILED_LOG_ONCE ("GetPointerType");
595       return TRUE;
596     }
597 
598   return !(type == PT_PEN || type == PT_TOUCH);
599 }
600 
601 void
gdk_winpointer_interaction_ended(MSG * msg)602 gdk_winpointer_interaction_ended (MSG *msg)
603 {
604   winpointer_remove_ignored_interaction (GET_POINTERID_WPARAM (msg->wParam));
605 }
606 
607 static inline double
utils_rect_width(RECT * rect)608 utils_rect_width (RECT *rect)
609 {
610   return rect->right - rect->left;
611 }
612 
613 static inline double
utils_rect_height(RECT * rect)614 utils_rect_height (RECT *rect)
615 {
616   return rect->bottom - rect->top;
617 }
618 
619 static inline gboolean
utils_rect_is_degenerate(RECT * rect)620 utils_rect_is_degenerate (RECT *rect)
621 {
622   return utils_rect_width (rect) == 0 || utils_rect_height (rect) == 0;
623 }
624 
625 static gboolean
winpointer_device_update_scale_factors(GdkDeviceWinpointer * device)626 winpointer_device_update_scale_factors (GdkDeviceWinpointer *device)
627 {
628   RECT device_rect;
629   RECT display_rect;
630 
631   if (!getPointerDeviceRects (device->device_handle, &device_rect, &display_rect))
632     {
633       WIN32_API_FAILED ("GetPointerDeviceRects");
634       return FALSE;
635     }
636 
637   if (utils_rect_is_degenerate (&device_rect))
638     {
639       g_warning ("Invalid coordinates from GetPointerDeviceRects");
640       return FALSE;
641     }
642 
643   device->origin_x = display_rect.left;
644   device->origin_y = display_rect.top;
645   device->scale_x = utils_rect_width (&display_rect) / utils_rect_width (&device_rect);
646   device->scale_y = utils_rect_height (&display_rect) / utils_rect_height (&device_rect);
647 
648   return TRUE;
649 }
650 
651 static void
winpointer_get_device_details(HANDLE device,char * vid,char * pid,char ** manufacturer,char ** product)652 winpointer_get_device_details (HANDLE device,
653                                char *vid,
654                                char *pid,
655                                char **manufacturer,
656                                char **product)
657 {
658   RID_DEVICE_INFO info;
659   UINT wchars_count = 0;
660   UINT size = 0;
661 
662   memset (&info, 0, sizeof (info));
663 
664   info.cbSize = sizeof (info);
665   size = sizeof (info);
666 
667   if (GetRawInputDeviceInfoW (device, RIDI_DEVICEINFO, &info, &size) > 0 &&
668       info.dwType == RIM_TYPEHID &&
669       info.hid.dwVendorId > 0 &&
670       info.hid.dwProductId > 0)
671     {
672       const char *format_string = "%0" G_STRINGIFY (VID_PID_CHARS) "x";
673 
674       g_snprintf (vid, VID_PID_CHARS + 1, format_string, (unsigned) info.hid.dwVendorId);
675       g_snprintf (pid, VID_PID_CHARS + 1, format_string, (unsigned) info.hid.dwProductId);
676     }
677 
678   if (GetRawInputDeviceInfoW (device, RIDI_DEVICENAME, NULL, &wchars_count) == 0)
679     {
680       gunichar2 *device_path = g_new0 (gunichar2, wchars_count);
681 
682       if (GetRawInputDeviceInfoW (device, RIDI_DEVICENAME, device_path, &wchars_count) > 0)
683         {
684           HANDLE device_file = CreateFileW (device_path,
685                                             0,
686                                             FILE_SHARE_READ |
687                                             FILE_SHARE_WRITE |
688                                             FILE_SHARE_DELETE,
689                                             NULL,
690                                             OPEN_EXISTING,
691                                             FILE_FLAG_SESSION_AWARE,
692                                             NULL);
693 
694           if (device_file != INVALID_HANDLE_VALUE)
695             {
696               gunichar2 *buffer = g_malloc0 (HID_STRING_BYTES_LIMIT);
697 
698               if (HidD_GetManufacturerString (device_file, buffer, HID_STRING_BYTES_LIMIT))
699                 if (buffer[0])
700                   *manufacturer = g_utf16_to_utf8 (buffer, -1, NULL, NULL, NULL);
701 
702               if (HidD_GetProductString (device_file, buffer, HID_STRING_BYTES_LIMIT))
703                 if (buffer[0])
704                   *product = g_utf16_to_utf8 (buffer, -1, NULL, NULL, NULL);
705 
706               g_free (buffer);
707               CloseHandle (device_file);
708             }
709         }
710 
711       g_free (device_path);
712     }
713 }
714 
715 static void
winpointer_create_device(POINTER_DEVICE_INFO * info,GdkInputSource source)716 winpointer_create_device (POINTER_DEVICE_INFO *info,
717                           GdkInputSource source)
718 {
719   GdkDeviceWinpointer *device = NULL;
720   GdkSeat *seat = NULL;
721   unsigned num_touches = 0;
722   char vid[VID_PID_CHARS + 1];
723   char pid[VID_PID_CHARS + 1];
724   char *manufacturer = NULL;
725   char *product = NULL;
726   char *base_name = NULL;
727   char *name = NULL;
728   UINT32 num_cursors = 0;
729   GdkAxisFlags axes_flags = 0;
730 
731   seat = gdk_display_get_default_seat (_gdk_display);
732 
733   memset (pid, 0, VID_PID_CHARS + 1);
734   memset (vid, 0, VID_PID_CHARS + 1);
735 
736   if (!getPointerDeviceCursors (info->device, &num_cursors, NULL))
737     {
738       WIN32_API_FAILED ("GetPointerDeviceCursors");
739       return;
740     }
741 
742   if (num_cursors == 0)
743     return;
744 
745   winpointer_get_device_details (info->device, vid, pid, &manufacturer, &product);
746 
747   /* build up the name */
748   if (!manufacturer && vid[0])
749     manufacturer = g_strdup (vid);
750 
751   if (!product && pid[0])
752     product = g_strdup (pid);
753 
754   if (manufacturer && product)
755     base_name = g_strconcat (manufacturer, " ", product, NULL);
756 
757   if (!base_name && info->productString[0])
758     base_name = g_utf16_to_utf8 (info->productString, -1, NULL, NULL, NULL);
759 
760   if (!base_name)
761     base_name = g_strdup ("Unnamed");
762 
763   switch (source)
764     {
765     case GDK_SOURCE_PEN:
766       name = g_strconcat (base_name, " Pen", NULL);
767     break;
768 
769     case GDK_SOURCE_TOUCHSCREEN:
770       num_touches = info->maxActiveContacts;
771       name = g_strconcat (base_name, " Finger touch", NULL);
772     break;
773 
774     default:
775       name = g_strdup (base_name);
776     break;
777     }
778 
779   device = g_object_new (GDK_TYPE_DEVICE_WINPOINTER,
780                          "display", _gdk_display,
781                          "seat", seat,
782                          "has-cursor", TRUE,
783                          "source", source,
784                          "name", name,
785                          "num-touches", num_touches,
786                          "vendor-id", vid[0] ? vid : NULL,
787                          "product-id", pid[0] ? pid : NULL,
788                          NULL);
789 
790   switch (source)
791     {
792     case GDK_SOURCE_PEN:
793       _gdk_device_add_axis (GDK_DEVICE (device), GDK_AXIS_PRESSURE, 0.0, 1.0, 1.0 / 1024.0);
794       axes_flags |= GDK_AXIS_FLAG_PRESSURE;
795 
796       _gdk_device_add_axis (GDK_DEVICE (device), GDK_AXIS_XTILT, -1.0, 1.0, 1.0 / 90.0);
797       axes_flags |= GDK_AXIS_FLAG_XTILT;
798 
799       _gdk_device_add_axis (GDK_DEVICE (device), GDK_AXIS_YTILT, -1.0, 1.0, 1.0 / 90.0);
800       axes_flags |= GDK_AXIS_FLAG_YTILT;
801 
802       _gdk_device_add_axis (GDK_DEVICE (device), GDK_AXIS_ROTATION, 0.0, 1.0, 1.0 / 360.0);
803       axes_flags |= GDK_AXIS_FLAG_ROTATION;
804     break;
805 
806     case GDK_SOURCE_TOUCHSCREEN:
807       _gdk_device_add_axis (GDK_DEVICE (device), GDK_AXIS_PRESSURE, 0.0, 1.0, 1.0 / 1024.0);
808       axes_flags |= GDK_AXIS_FLAG_PRESSURE;
809     break;
810 
811     default:
812       g_warn_if_reached ();
813     break;
814     }
815 
816   device->device_handle = info->device;
817   device->start_cursor_id = info->startingCursorId;
818   device->end_cursor_id = info->startingCursorId + num_cursors - 1;
819 
820   if (!winpointer_device_update_scale_factors (device))
821     {
822       g_set_object (&device, NULL);
823       goto cleanup;
824     }
825 
826   switch (source)
827     {
828     case GDK_SOURCE_PEN:
829       {
830         device->tool_pen = gdk_device_tool_new (0, 0, GDK_DEVICE_TOOL_TYPE_PEN, axes_flags);
831         gdk_seat_default_add_tool (GDK_SEAT_DEFAULT (seat), device->tool_pen);
832 
833         device->tool_eraser = gdk_device_tool_new (0, 0, GDK_DEVICE_TOOL_TYPE_ERASER, axes_flags);
834         gdk_seat_default_add_tool (GDK_SEAT_DEFAULT (seat), device->tool_eraser);
835       }
836     break;
837     case GDK_SOURCE_TOUCHSCREEN:
838     break;
839     default:
840       g_warn_if_reached ();
841     break;
842     }
843 
844   _gdk_device_manager->winpointer_devices = g_list_append (_gdk_device_manager->winpointer_devices, device);
845 
846   _gdk_device_set_associated_device (GDK_DEVICE (device), _gdk_device_manager->core_pointer);
847   _gdk_device_add_physical_device (_gdk_device_manager->core_pointer, GDK_DEVICE (device));
848 
849   gdk_seat_default_add_physical_device (GDK_SEAT_DEFAULT (seat), GDK_DEVICE (device));
850 
851 cleanup:
852   g_free (name);
853   g_free (base_name);
854   g_free (product);
855   g_free (manufacturer);
856 }
857 
858 static void
winpointer_create_devices(POINTER_DEVICE_INFO * info)859 winpointer_create_devices (POINTER_DEVICE_INFO *info)
860 {
861   switch (info->pointerDeviceType)
862     {
863     case POINTER_DEVICE_TYPE_INTEGRATED_PEN:
864     case POINTER_DEVICE_TYPE_EXTERNAL_PEN:
865       winpointer_create_device (info, GDK_SOURCE_PEN);
866     break;
867     case POINTER_DEVICE_TYPE_TOUCH:
868       winpointer_create_device (info, GDK_SOURCE_TOUCHSCREEN);
869     break;
870     default:
871       g_warn_if_reached ();
872     break;
873     }
874 }
875 
876 static gboolean
winpointer_find_device_in_system_list(GdkDeviceWinpointer * device,POINTER_DEVICE_INFO * infos,UINT32 infos_count)877 winpointer_find_device_in_system_list (GdkDeviceWinpointer *device,
878                                        POINTER_DEVICE_INFO *infos,
879                                        UINT32 infos_count)
880 {
881   for (UINT32 i = 0; i < infos_count; i++)
882     {
883       if (device->device_handle == infos[i].device &&
884           device->start_cursor_id == infos[i].startingCursorId)
885         {
886           return TRUE;
887         }
888     }
889 
890   return FALSE;
891 }
892 
893 static gboolean
winpointer_find_system_device_in_device_manager(POINTER_DEVICE_INFO * info)894 winpointer_find_system_device_in_device_manager (POINTER_DEVICE_INFO *info)
895 {
896   for (GList *l = _gdk_device_manager->winpointer_devices; l != NULL; l = l->next)
897     {
898       GdkDeviceWinpointer *device = GDK_DEVICE_WINPOINTER (l->data);
899 
900       if (device->device_handle == info->device &&
901           device->start_cursor_id == info->startingCursorId)
902         {
903           return TRUE;
904         }
905     }
906 
907   return FALSE;
908 }
909 
910 static void
winpointer_enumerate_devices(void)911 winpointer_enumerate_devices (void)
912 {
913   POINTER_DEVICE_INFO *infos = NULL;
914   UINT32 infos_count = 0;
915   UINT32 i = 0;
916   GList *current = NULL;
917 
918   do
919     {
920       infos = g_new0 (POINTER_DEVICE_INFO, infos_count);
921       if (!getPointerDevices (&infos_count, infos))
922         {
923           WIN32_API_FAILED ("GetPointerDevices");
924           g_free (infos);
925           return;
926         }
927     }
928   while (infos_count > 0 && !infos);
929 
930   current = _gdk_device_manager->winpointer_devices;
931 
932   while (current != NULL)
933     {
934       GdkDeviceWinpointer *device = GDK_DEVICE_WINPOINTER (current->data);
935       GList *next = current->next;
936 
937       if (!winpointer_find_device_in_system_list (device, infos, infos_count))
938         {
939           GdkSeat *seat = gdk_device_get_seat (GDK_DEVICE (device));
940 
941           _gdk_device_manager->winpointer_devices = g_list_delete_link (_gdk_device_manager->winpointer_devices,
942                                                                         current);
943 
944           gdk_device_update_tool (GDK_DEVICE (device), NULL);
945 
946           if (device->tool_pen)
947             gdk_seat_default_remove_tool (GDK_SEAT_DEFAULT (seat), device->tool_pen);
948 
949           if (device->tool_eraser)
950             gdk_seat_default_remove_tool (GDK_SEAT_DEFAULT (seat), device->tool_eraser);
951 
952           _gdk_device_set_associated_device (GDK_DEVICE (device), NULL);
953           _gdk_device_remove_physical_device (_gdk_device_manager->core_pointer, GDK_DEVICE (device));
954 
955           gdk_seat_default_remove_physical_device (GDK_SEAT_DEFAULT (seat), GDK_DEVICE (device));
956 
957           g_object_unref (device);
958         }
959       else
960         {
961           winpointer_device_update_scale_factors (device);
962         }
963 
964       current = next;
965     }
966 
967   /* create new gdk devices */
968   for (i = 0; i < infos_count; i++)
969     {
970       if (!winpointer_find_system_device_in_device_manager (&infos[i]))
971         {
972           winpointer_create_devices (&infos[i]);
973         }
974     }
975 
976   g_free (infos);
977 }
978 
979 static LRESULT CALLBACK
winpointer_notifications_window_procedure(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)980 winpointer_notifications_window_procedure (HWND hWnd,
981                                            UINT uMsg,
982                                            WPARAM wParam,
983                                            LPARAM lParam)
984 {
985   switch (uMsg)
986     {
987     case WM_POINTERDEVICECHANGE:
988       winpointer_enumerate_devices ();
989       return 0;
990     }
991 
992   return DefWindowProcW (hWnd, uMsg, wParam, lParam);
993 }
994 
995 static gboolean
winpointer_notif_window_create(void)996 winpointer_notif_window_create (void)
997 {
998   WNDCLASSEXW wndclassex;
999 
1000   memset (&wndclassex, 0, sizeof (wndclassex));
1001   wndclassex.cbSize = sizeof (wndclassex);
1002   wndclassex.lpszClassName = L"GdkWin32WinpointerNotificationsWindowClass";
1003   wndclassex.lpfnWndProc = winpointer_notifications_window_procedure;
1004   wndclassex.hInstance = _gdk_dll_hinstance;
1005 
1006   if ((notifications_window_class = RegisterClassExW (&wndclassex)) == 0)
1007     {
1008       WIN32_API_FAILED ("RegisterClassExW");
1009       return FALSE;
1010     }
1011 
1012   if (!(notifications_window_handle = CreateWindowExW (0,
1013                                                        (LPCWSTR)(guintptr)notifications_window_class,
1014                                                        L"GdkWin32 Winpointer Notifications",
1015                                                        0,
1016                                                        0, 0, 0, 0,
1017                                                        HWND_MESSAGE,
1018                                                        NULL,
1019                                                        _gdk_dll_hinstance,
1020                                                        NULL)))
1021     {
1022       WIN32_API_FAILED ("CreateWindowExW");
1023       return FALSE;
1024     }
1025 
1026   return TRUE;
1027 }
1028 
1029 static gboolean
winpointer_ensure_procedures(void)1030 winpointer_ensure_procedures (void)
1031 {
1032   static HMODULE user32_dll = NULL;
1033 
1034   if (!user32_dll)
1035     {
1036       user32_dll = LoadLibraryW (L"user32.dll");
1037       if (!user32_dll)
1038         {
1039           WIN32_API_FAILED ("LoadLibraryW");
1040           return FALSE;
1041         }
1042 
1043       registerPointerDeviceNotifications = (registerPointerDeviceNotifications_t)
1044         GetProcAddress (user32_dll, "RegisterPointerDeviceNotifications");
1045       getPointerDevices = (getPointerDevices_t)
1046         GetProcAddress (user32_dll, "GetPointerDevices");
1047       getPointerDeviceCursors = (getPointerDeviceCursors_t)
1048         GetProcAddress (user32_dll, "GetPointerDeviceCursors");
1049       getPointerDeviceRects = (getPointerDeviceRects_t)
1050         GetProcAddress (user32_dll, "GetPointerDeviceRects");
1051       getPointerType = (getPointerType_t)
1052         GetProcAddress (user32_dll, "GetPointerType");
1053       getPointerCursorId = (getPointerCursorId_t)
1054         GetProcAddress (user32_dll, "GetPointerCursorId");
1055       getPointerPenInfo = (getPointerPenInfo_t)
1056         GetProcAddress (user32_dll, "GetPointerPenInfo");
1057       getPointerTouchInfo = (getPointerTouchInfo_t)
1058         GetProcAddress (user32_dll, "GetPointerTouchInfo");
1059       getPointerPenInfoHistory = (getPointerPenInfoHistory_t)
1060         GetProcAddress (user32_dll, "GetPointerPenInfoHistory");
1061       getPointerTouchInfoHistory = (getPointerTouchInfoHistory_t)
1062         GetProcAddress (user32_dll, "GetPointerTouchInfoHistory");
1063       setGestureConfig = (setGestureConfig_t)
1064         GetProcAddress (user32_dll, "SetGestureConfig");
1065       setWindowFeedbackSetting = (setWindowFeedbackSetting_t)
1066         GetProcAddress (user32_dll, "SetWindowFeedbackSetting");
1067     }
1068 
1069   return registerPointerDeviceNotifications &&
1070          getPointerDevices &&
1071          getPointerDeviceCursors &&
1072          getPointerDeviceRects &&
1073          getPointerType &&
1074          getPointerCursorId &&
1075          getPointerPenInfo &&
1076          getPointerTouchInfo &&
1077          getPointerPenInfoHistory &&
1078          getPointerTouchInfoHistory &&
1079          setGestureConfig;
1080 }
1081 
1082 gboolean
gdk_winpointer_initialize(void)1083 gdk_winpointer_initialize (void)
1084 {
1085   if (!winpointer_ensure_procedures ())
1086     return FALSE;
1087 
1088   if (!winpointer_notif_window_create ())
1089     return FALSE;
1090 
1091   if (!registerPointerDeviceNotifications (notifications_window_handle, FALSE))
1092     {
1093       WIN32_API_FAILED ("RegisterPointerDeviceNotifications");
1094       return FALSE;
1095     }
1096 
1097   ignored_interactions = g_ptr_array_new ();
1098 
1099   winpointer_enumerate_devices ();
1100 
1101   return TRUE;
1102 }
1103 
1104 #ifndef MICROSOFT_TABLETPENSERVICE_PROPERTY
1105 #define MICROSOFT_TABLETPENSERVICE_PROPERTY \
1106 _T("MicrosoftTabletPenServiceProperty")
1107 #endif
1108 
1109 void
gdk_winpointer_initialize_surface(GdkSurface * surface)1110 gdk_winpointer_initialize_surface (GdkSurface *surface)
1111 {
1112   HWND hwnd = GDK_SURFACE_HWND (surface);
1113   ATOM key = 0;
1114   HANDLE val = (HANDLE)(TABLET_DISABLE_PRESSANDHOLD |
1115                         TABLET_DISABLE_PENTAPFEEDBACK |
1116                         TABLET_DISABLE_PENBARRELFEEDBACK |
1117                         TABLET_DISABLE_FLICKS |
1118                         TABLET_DISABLE_FLICKFALLBACKKEYS);
1119 
1120   winpointer_ensure_procedures ();
1121 
1122   key = GlobalAddAtom (MICROSOFT_TABLETPENSERVICE_PROPERTY);
1123   API_CALL (SetPropW, (hwnd, (LPCWSTR)(guintptr)key, val));
1124   GlobalDeleteAtom (key);
1125 
1126   if (setGestureConfig != NULL)
1127     {
1128       GESTURECONFIG gesture_config;
1129       memset (&gesture_config, 0, sizeof (gesture_config));
1130 
1131       gesture_config.dwID = 0;
1132       gesture_config.dwWant = 0;
1133       gesture_config.dwBlock = GC_ALLGESTURES;
1134 
1135       API_CALL (setGestureConfig, (hwnd, 0, 1, &gesture_config, sizeof (gesture_config)));
1136     }
1137 
1138   if (setWindowFeedbackSetting != NULL)
1139     {
1140       FEEDBACK_TYPE feedbacks[] = {
1141         FEEDBACK_TOUCH_CONTACTVISUALIZATION,
1142         FEEDBACK_PEN_BARRELVISUALIZATION,
1143         FEEDBACK_PEN_TAP,
1144         FEEDBACK_PEN_DOUBLETAP,
1145         FEEDBACK_PEN_PRESSANDHOLD,
1146         FEEDBACK_PEN_RIGHTTAP,
1147         FEEDBACK_TOUCH_TAP,
1148         FEEDBACK_TOUCH_DOUBLETAP,
1149         FEEDBACK_TOUCH_PRESSANDHOLD,
1150         FEEDBACK_TOUCH_RIGHTTAP,
1151         FEEDBACK_GESTURE_PRESSANDTAP
1152       };
1153       gsize i = 0;
1154 
1155       for (i = 0; i < G_N_ELEMENTS (feedbacks); i++)
1156         {
1157           BOOL setting = FALSE;
1158 
1159           API_CALL (setWindowFeedbackSetting, (hwnd, feedbacks[i], 0, sizeof (BOOL), &setting));
1160         }
1161     }
1162 }
1163 
1164 void
gdk_winpointer_finalize_surface(GdkSurface * surface)1165 gdk_winpointer_finalize_surface (GdkSurface *surface)
1166 {
1167   HWND hwnd = GDK_SURFACE_HWND (surface);
1168   ATOM key = 0;
1169 
1170   key = GlobalAddAtom (MICROSOFT_TABLETPENSERVICE_PROPERTY);
1171   RemovePropW (hwnd, (LPCWSTR)(guintptr)key);
1172   GlobalDeleteAtom (key);
1173 }
1174