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