2 * Wayland pointer handling
4 * Copyright (c) 2020 Alexandros Frantzis for Collabora Ltd
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include <linux/input.h>
28 #undef SW_MAX /* Also defined in winuser.rh */
32 #include "waylanddrv.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv
);
37 static HWND
wayland_pointer_get_focused_hwnd(void)
39 struct wayland_pointer
*pointer
= &process_wayland
.pointer
;
42 pthread_mutex_lock(&pointer
->mutex
);
43 hwnd
= pointer
->focused_hwnd
;
44 pthread_mutex_unlock(&pointer
->mutex
);
49 static void pointer_handle_motion_internal(wl_fixed_t sx
, wl_fixed_t sy
)
55 struct wayland_surface
*surface
;
57 if (!(hwnd
= wayland_pointer_get_focused_hwnd())) return;
58 if (!(surface
= wayland_surface_lock_hwnd(hwnd
))) return;
60 window_rect
= &surface
->window
.rect
;
62 wayland_surface_coords_to_window(surface
,
63 wl_fixed_to_double(sx
),
64 wl_fixed_to_double(sy
),
65 (int *)&screen
.x
, (int *)&screen
.y
);
66 screen
.x
+= window_rect
->left
;
67 screen
.y
+= window_rect
->top
;
68 /* Sometimes, due to rounding, we may end up with pointer coordinates
69 * slightly outside the target window, so bring them within bounds. */
70 if (screen
.x
>= window_rect
->right
) screen
.x
= window_rect
->right
- 1;
71 else if (screen
.x
< window_rect
->left
) screen
.x
= window_rect
->left
;
72 if (screen
.y
>= window_rect
->bottom
) screen
.y
= window_rect
->bottom
- 1;
73 else if (screen
.y
< window_rect
->top
) screen
.y
= window_rect
->top
;
75 pthread_mutex_unlock(&surface
->mutex
);
77 /* Hardware input events are in physical coordinates. */
78 if (!NtUserLogicalToPerMonitorDPIPhysicalPoint(hwnd
, &screen
)) return;
80 input
.type
= INPUT_MOUSE
;
81 input
.mi
.dx
= screen
.x
;
82 input
.mi
.dy
= screen
.y
;
83 input
.mi
.dwFlags
= MOUSEEVENTF_MOVE
| MOUSEEVENTF_ABSOLUTE
;
85 TRACE("hwnd=%p wayland_xy=%.2f,%.2f screen_xy=%d,%d\n",
86 hwnd
, wl_fixed_to_double(sx
), wl_fixed_to_double(sy
),
87 (int)screen
.x
, (int)screen
.y
);
89 NtUserSendHardwareInput(hwnd
, 0, &input
, 0);
92 static void pointer_handle_motion(void *data
, struct wl_pointer
*wl_pointer
,
93 uint32_t time
, wl_fixed_t sx
, wl_fixed_t sy
)
95 struct wayland_pointer
*pointer
= &process_wayland
.pointer
;
97 /* Ignore absolute motion events if in relative mode. */
98 if (pointer
->zwp_relative_pointer_v1
) return;
100 pointer_handle_motion_internal(sx
, sy
);
103 static void wayland_set_cursor(HWND hwnd
, HCURSOR hcursor
, BOOL use_hcursor
);
105 static void pointer_handle_enter(void *data
, struct wl_pointer
*wl_pointer
,
106 uint32_t serial
, struct wl_surface
*wl_surface
,
107 wl_fixed_t sx
, wl_fixed_t sy
)
109 struct wayland_pointer
*pointer
= &process_wayland
.pointer
;
112 if (!wl_surface
) return;
113 /* The wl_surface user data remains valid and immutable for the whole
114 * lifetime of the object, so it's safe to access without locking. */
115 hwnd
= wl_surface_get_user_data(wl_surface
);
117 TRACE("hwnd=%p\n", hwnd
);
119 pthread_mutex_lock(&pointer
->mutex
);
120 pointer
->focused_hwnd
= hwnd
;
121 pointer
->enter_serial
= serial
;
122 pthread_mutex_unlock(&pointer
->mutex
);
124 /* The cursor is undefined at every enter, so we set it again with
125 * the latest information we have. */
126 wayland_set_cursor(hwnd
, NULL
, FALSE
);
128 /* Handle the enter as a motion, to account for cases where the
129 * window first appears beneath the pointer and won't get a separate
131 pointer_handle_motion_internal(sx
, sy
);
134 static void pointer_handle_leave(void *data
, struct wl_pointer
*wl_pointer
,
135 uint32_t serial
, struct wl_surface
*wl_surface
)
137 struct wayland_pointer
*pointer
= &process_wayland
.pointer
;
139 if (!wl_surface
) return;
141 TRACE("hwnd=%p\n", wl_surface_get_user_data(wl_surface
));
143 pthread_mutex_lock(&pointer
->mutex
);
144 pointer
->focused_hwnd
= NULL
;
145 pointer
->enter_serial
= 0;
146 pthread_mutex_unlock(&pointer
->mutex
);
149 static void pointer_handle_button(void *data
, struct wl_pointer
*wl_pointer
,
150 uint32_t serial
, uint32_t time
, uint32_t button
,
153 struct wayland_pointer
*pointer
= &process_wayland
.pointer
;
157 if (!(hwnd
= wayland_pointer_get_focused_hwnd())) return;
159 input
.type
= INPUT_MOUSE
;
163 case BTN_LEFT
: input
.mi
.dwFlags
= MOUSEEVENTF_LEFTDOWN
; break;
164 case BTN_RIGHT
: input
.mi
.dwFlags
= MOUSEEVENTF_RIGHTDOWN
; break;
165 case BTN_MIDDLE
: input
.mi
.dwFlags
= MOUSEEVENTF_MIDDLEDOWN
; break;
168 input
.mi
.dwFlags
= MOUSEEVENTF_XDOWN
;
169 input
.mi
.mouseData
= XBUTTON1
;
173 input
.mi
.dwFlags
= MOUSEEVENTF_XDOWN
;
174 input
.mi
.mouseData
= XBUTTON2
;
179 if (state
== WL_POINTER_BUTTON_STATE_RELEASED
) input
.mi
.dwFlags
<<= 1;
181 pthread_mutex_lock(&pointer
->mutex
);
182 pointer
->button_serial
= state
== WL_POINTER_BUTTON_STATE_PRESSED
?
184 pthread_mutex_unlock(&pointer
->mutex
);
186 TRACE("hwnd=%p button=%#x state=%u\n", hwnd
, button
, state
);
188 NtUserSendHardwareInput(hwnd
, 0, &input
, 0);
191 static void pointer_handle_axis(void *data
, struct wl_pointer
*wl_pointer
,
192 uint32_t time
, uint32_t axis
, wl_fixed_t value
)
196 static void pointer_handle_frame(void *data
, struct wl_pointer
*wl_pointer
)
200 static void pointer_handle_axis_source(void *data
, struct wl_pointer
*wl_pointer
,
201 uint32_t axis_source
)
205 static void pointer_handle_axis_stop(void *data
, struct wl_pointer
*wl_pointer
,
206 uint32_t time
, uint32_t axis
)
210 static void pointer_handle_axis_discrete(void *data
, struct wl_pointer
*wl_pointer
,
211 uint32_t axis
, int32_t discrete
)
216 if (!(hwnd
= wayland_pointer_get_focused_hwnd())) return;
218 input
.type
= INPUT_MOUSE
;
222 case WL_POINTER_AXIS_VERTICAL_SCROLL
:
223 input
.mi
.dwFlags
= MOUSEEVENTF_WHEEL
;
224 input
.mi
.mouseData
= -WHEEL_DELTA
* discrete
;
226 case WL_POINTER_AXIS_HORIZONTAL_SCROLL
:
227 input
.mi
.dwFlags
= MOUSEEVENTF_HWHEEL
;
228 input
.mi
.mouseData
= WHEEL_DELTA
* discrete
;
233 TRACE("hwnd=%p axis=%u discrete=%d\n", hwnd
, axis
, discrete
);
235 NtUserSendHardwareInput(hwnd
, 0, &input
, 0);
238 static const struct wl_pointer_listener pointer_listener
=
240 pointer_handle_enter
,
241 pointer_handle_leave
,
242 pointer_handle_motion
,
243 pointer_handle_button
,
245 pointer_handle_frame
,
246 pointer_handle_axis_source
,
247 pointer_handle_axis_stop
,
248 pointer_handle_axis_discrete
251 static void relative_pointer_v1_relative_motion(void *data
,
252 struct zwp_relative_pointer_v1
*zwp_relative_pointer_v1
,
253 uint32_t utime_hi
, uint32_t utime_lo
,
254 wl_fixed_t dx
, wl_fixed_t dy
,
255 wl_fixed_t dx_unaccel
, wl_fixed_t dy_unaccel
)
259 POINT screen
, origin
;
260 struct wayland_surface
*surface
;
263 if (!(hwnd
= wayland_pointer_get_focused_hwnd())) return;
264 if (!(surface
= wayland_surface_lock_hwnd(hwnd
))) return;
266 window_rect
= surface
->window
.rect
;
268 wayland_surface_coords_to_window(surface
,
269 wl_fixed_to_double(dx
),
270 wl_fixed_to_double(dy
),
271 (int *)&screen
.x
, (int *)&screen
.y
);
273 pthread_mutex_unlock(&surface
->mutex
);
275 /* We clip the relative motion within the window rectangle so that
276 * the NtUserLogicalToPerMonitorDPIPhysicalPoint calls later succeed.
277 * TODO: Avoid clipping by using a more versatile dpi mapping function. */
280 origin
.x
= window_rect
.left
;
281 screen
.x
+= origin
.x
;
282 if (screen
.x
>= window_rect
.right
) screen
.x
= window_rect
.right
- 1;
286 origin
.x
= window_rect
.right
;
287 screen
.x
+= origin
.x
;
288 if (screen
.x
< window_rect
.left
) screen
.x
= window_rect
.left
;
293 origin
.y
= window_rect
.top
;
294 screen
.y
+= origin
.y
;
295 if (screen
.y
>= window_rect
.bottom
) screen
.y
= window_rect
.bottom
- 1;
299 origin
.y
= window_rect
.bottom
;
300 screen
.y
+= origin
.y
;
301 if (screen
.y
< window_rect
.top
) screen
.y
= window_rect
.top
;
304 /* Transform the relative motion from window coordinates to physical
305 * coordinates required for the input event. */
306 if (!NtUserLogicalToPerMonitorDPIPhysicalPoint(hwnd
, &screen
)) return;
307 if (!NtUserLogicalToPerMonitorDPIPhysicalPoint(hwnd
, &origin
)) return;
308 screen
.x
-= origin
.x
;
309 screen
.y
-= origin
.y
;
311 input
.type
= INPUT_MOUSE
;
312 input
.mi
.dx
= screen
.x
;
313 input
.mi
.dy
= screen
.y
;
314 input
.mi
.dwFlags
= MOUSEEVENTF_MOVE
;
316 TRACE("hwnd=%p wayland_dxdy=%.2f,%.2f screen_dxdy=%d,%d\n",
317 hwnd
, wl_fixed_to_double(dx
), wl_fixed_to_double(dy
),
318 (int)screen
.x
, (int)screen
.y
);
320 NtUserSendHardwareInput(hwnd
, 0, &input
, 0);
323 static const struct zwp_relative_pointer_v1_listener relative_pointer_v1_listener
=
325 relative_pointer_v1_relative_motion
328 void wayland_pointer_init(struct wl_pointer
*wl_pointer
)
330 struct wayland_pointer
*pointer
= &process_wayland
.pointer
;
332 pthread_mutex_lock(&pointer
->mutex
);
333 pointer
->wl_pointer
= wl_pointer
;
334 pointer
->focused_hwnd
= NULL
;
335 pointer
->enter_serial
= 0;
336 pthread_mutex_unlock(&pointer
->mutex
);
337 wl_pointer_add_listener(pointer
->wl_pointer
, &pointer_listener
, NULL
);
340 void wayland_pointer_deinit(void)
342 struct wayland_pointer
*pointer
= &process_wayland
.pointer
;
344 pthread_mutex_lock(&pointer
->mutex
);
345 if (pointer
->zwp_confined_pointer_v1
)
347 zwp_confined_pointer_v1_destroy(pointer
->zwp_confined_pointer_v1
);
348 pointer
->zwp_confined_pointer_v1
= NULL
;
350 if (pointer
->zwp_locked_pointer_v1
)
352 zwp_locked_pointer_v1_destroy(pointer
->zwp_locked_pointer_v1
);
353 pointer
->zwp_locked_pointer_v1
= NULL
;
355 if (pointer
->zwp_relative_pointer_v1
)
357 zwp_relative_pointer_v1_destroy(pointer
->zwp_relative_pointer_v1
);
358 pointer
->zwp_relative_pointer_v1
= NULL
;
360 wl_pointer_release(pointer
->wl_pointer
);
361 pointer
->wl_pointer
= NULL
;
362 pointer
->focused_hwnd
= NULL
;
363 pointer
->enter_serial
= 0;
364 pthread_mutex_unlock(&pointer
->mutex
);
367 /***********************************************************************
368 * create_mono_cursor_buffer
370 * Create a wayland_shm_buffer for a monochrome cursor bitmap.
372 * Adapted from wineandroid.drv code.
374 static struct wayland_shm_buffer
*create_mono_cursor_buffer(HBITMAP bmp
)
376 struct wayland_shm_buffer
*shm_buffer
= NULL
;
379 unsigned int i
, j
, stride
, mask_size
, *ptr
;
381 if (!NtGdiExtGetObjectW(bmp
, sizeof(bm
), &bm
)) goto done
;
382 stride
= ((bm
.bmWidth
+ 15) >> 3) & ~1;
383 mask_size
= stride
* bm
.bmHeight
;
384 if (!(mask
= malloc(mask_size
))) goto done
;
385 if (!NtGdiGetBitmapBits(bmp
, mask_size
, mask
)) goto done
;
388 shm_buffer
= wayland_shm_buffer_create(bm
.bmWidth
, bm
.bmHeight
,
389 WL_SHM_FORMAT_ARGB8888
);
390 if (!shm_buffer
) goto done
;
392 ptr
= shm_buffer
->map_data
;
393 for (i
= 0; i
< bm
.bmHeight
; i
++)
395 for (j
= 0; j
< bm
.bmWidth
; j
++, ptr
++)
397 int and = ((mask
[i
* stride
+ j
/ 8] << (j
% 8)) & 0x80);
398 int xor = ((mask
[(i
+ bm
.bmHeight
) * stride
+ j
/ 8] << (j
% 8)) & 0x80);
401 else if (xor && !and)
404 /* we can't draw "invert" pixels, so render them as black instead */
414 /***********************************************************************
415 * create_color_cursor_buffer
417 * Create a wayland_shm_buffer for a color cursor bitmap.
419 * Adapted from wineandroid.drv code.
421 static struct wayland_shm_buffer
*create_color_cursor_buffer(HDC hdc
, HBITMAP color
,
424 struct wayland_shm_buffer
*shm_buffer
= NULL
;
425 char buffer
[FIELD_OFFSET(BITMAPINFO
, bmiColors
[256])];
426 BITMAPINFO
*info
= (BITMAPINFO
*)buffer
;
428 unsigned int *ptr
, *bits
= NULL
;
429 unsigned char *mask_bits
= NULL
;
431 BOOL has_alpha
= FALSE
;
433 if (!NtGdiExtGetObjectW(color
, sizeof(bm
), &bm
)) goto failed
;
435 shm_buffer
= wayland_shm_buffer_create(bm
.bmWidth
, bm
.bmHeight
,
436 WL_SHM_FORMAT_ARGB8888
);
437 if (!shm_buffer
) goto failed
;
438 bits
= shm_buffer
->map_data
;
440 info
->bmiHeader
.biSize
= sizeof(BITMAPINFOHEADER
);
441 info
->bmiHeader
.biWidth
= bm
.bmWidth
;
442 info
->bmiHeader
.biHeight
= -bm
.bmHeight
;
443 info
->bmiHeader
.biPlanes
= 1;
444 info
->bmiHeader
.biBitCount
= 32;
445 info
->bmiHeader
.biCompression
= BI_RGB
;
446 info
->bmiHeader
.biSizeImage
= bm
.bmWidth
* bm
.bmHeight
* 4;
447 info
->bmiHeader
.biXPelsPerMeter
= 0;
448 info
->bmiHeader
.biYPelsPerMeter
= 0;
449 info
->bmiHeader
.biClrUsed
= 0;
450 info
->bmiHeader
.biClrImportant
= 0;
452 if (!NtGdiGetDIBitsInternal(hdc
, color
, 0, bm
.bmHeight
, bits
, info
,
453 DIB_RGB_COLORS
, 0, 0))
456 for (i
= 0; i
< bm
.bmWidth
* bm
.bmHeight
; i
++)
457 if ((has_alpha
= (bits
[i
] & 0xff000000) != 0)) break;
461 unsigned int width_bytes
= (bm
.bmWidth
+ 31) / 32 * 4;
462 /* generate alpha channel from the mask */
463 info
->bmiHeader
.biBitCount
= 1;
464 info
->bmiHeader
.biSizeImage
= width_bytes
* bm
.bmHeight
;
465 if (!(mask_bits
= malloc(info
->bmiHeader
.biSizeImage
))) goto failed
;
466 if (!NtGdiGetDIBitsInternal(hdc
, mask
, 0, bm
.bmHeight
, mask_bits
,
467 info
, DIB_RGB_COLORS
, 0, 0))
470 for (i
= 0; i
< bm
.bmHeight
; i
++)
472 for (j
= 0; j
< bm
.bmWidth
; j
++, ptr
++)
474 if (!((mask_bits
[i
* width_bytes
+ j
/ 8] << (j
% 8)) & 0x80))
481 /* Wayland requires pre-multiplied alpha values */
482 for (ptr
= bits
, i
= 0; i
< bm
.bmWidth
* bm
.bmHeight
; ptr
++, i
++)
484 unsigned char alpha
= *ptr
>> 24;
489 else if (alpha
!= 255)
491 *ptr
= (alpha
<< 24) |
492 (((BYTE
)(*ptr
>> 16) * alpha
/ 255) << 16) |
493 (((BYTE
)(*ptr
>> 8) * alpha
/ 255) << 8) |
494 (((BYTE
)*ptr
* alpha
/ 255));
501 if (shm_buffer
) wayland_shm_buffer_unref(shm_buffer
);
506 /***********************************************************************
509 * Local GetIconInfoExW helper implementation.
511 static BOOL
get_icon_info(HICON handle
, ICONINFOEXW
*ret
)
513 UNICODE_STRING module
, res_name
;
516 module
.Buffer
= ret
->szModName
;
517 module
.MaximumLength
= sizeof(ret
->szModName
) - sizeof(WCHAR
);
518 res_name
.Buffer
= ret
->szResName
;
519 res_name
.MaximumLength
= sizeof(ret
->szResName
) - sizeof(WCHAR
);
520 if (!NtUserGetIconInfo(handle
, &info
, &module
, &res_name
, NULL
, 0)) return FALSE
;
521 ret
->fIcon
= info
.fIcon
;
522 ret
->xHotspot
= info
.xHotspot
;
523 ret
->yHotspot
= info
.yHotspot
;
524 ret
->hbmColor
= info
.hbmColor
;
525 ret
->hbmMask
= info
.hbmMask
;
526 ret
->wResID
= res_name
.Length
? 0 : LOWORD(res_name
.Buffer
);
527 ret
->szModName
[module
.Length
] = 0;
528 ret
->szResName
[res_name
.Length
] = 0;
532 static void wayland_pointer_update_cursor_buffer(HCURSOR hcursor
, double scale
)
534 struct wayland_cursor
*cursor
= &process_wayland
.pointer
.cursor
;
535 ICONINFOEXW info
= {0};
537 if (!hcursor
) goto clear_cursor
;
539 /* Create a new buffer for the specified cursor. */
540 if (cursor
->shm_buffer
)
542 wayland_shm_buffer_unref(cursor
->shm_buffer
);
543 cursor
->shm_buffer
= NULL
;
546 if (!get_icon_info(hcursor
, &info
))
548 ERR("Failed to get icon info for cursor=%p\n", hcursor
);
554 HDC hdc
= NtGdiCreateCompatibleDC(0);
556 create_color_cursor_buffer(hdc
, info
.hbmColor
, info
.hbmMask
);
557 NtGdiDeleteObjectApp(hdc
);
561 cursor
->shm_buffer
= create_mono_cursor_buffer(info
.hbmMask
);
564 if (info
.hbmColor
) NtGdiDeleteObjectApp(info
.hbmColor
);
565 if (info
.hbmMask
) NtGdiDeleteObjectApp(info
.hbmMask
);
567 cursor
->hotspot_x
= info
.xHotspot
;
568 cursor
->hotspot_y
= info
.yHotspot
;
570 if (!cursor
->shm_buffer
)
572 ERR("Failed to create shm_buffer for cursor=%p\n", hcursor
);
576 /* Make sure the hotspot is valid. */
577 if (cursor
->hotspot_x
>= cursor
->shm_buffer
->width
||
578 cursor
->hotspot_y
>= cursor
->shm_buffer
->height
)
580 cursor
->hotspot_x
= cursor
->shm_buffer
->width
/ 2;
581 cursor
->hotspot_y
= cursor
->shm_buffer
->height
/ 2;
584 cursor
->hotspot_x
= round(cursor
->hotspot_x
/ scale
);
585 cursor
->hotspot_y
= round(cursor
->hotspot_y
/ scale
);
590 if (cursor
->shm_buffer
)
592 wayland_shm_buffer_unref(cursor
->shm_buffer
);
593 cursor
->shm_buffer
= NULL
;
597 static void wayland_pointer_update_cursor_surface(double scale
)
599 struct wayland_cursor
*cursor
= &process_wayland
.pointer
.cursor
;
601 if (!cursor
->shm_buffer
) goto clear_cursor
;
603 if (!cursor
->wl_surface
)
606 wl_compositor_create_surface(process_wayland
.wl_compositor
);
607 if (!cursor
->wl_surface
)
609 ERR("Failed to create wl_surface for cursor\n");
614 if (!cursor
->wp_viewport
&& process_wayland
.wp_viewporter
)
616 cursor
->wp_viewport
=
617 wp_viewporter_get_viewport(process_wayland
.wp_viewporter
,
619 if (!cursor
->wp_viewport
)
620 WARN("Failed to create wp_viewport for cursor\n");
623 /* Commit the cursor buffer to the cursor surface. */
624 wl_surface_attach(cursor
->wl_surface
,
625 cursor
->shm_buffer
->wl_buffer
, 0, 0);
626 wl_surface_damage_buffer(cursor
->wl_surface
, 0, 0,
627 cursor
->shm_buffer
->width
,
628 cursor
->shm_buffer
->height
);
629 /* Setting only the viewport is enough, but some compositors don't
630 * support wp_viewport for cursor surfaces, so also set the buffer
631 * scale. Note that setting the viewport destination overrides
632 * the buffer scale, so it's fine to set both. */
633 wl_surface_set_buffer_scale(cursor
->wl_surface
, round(scale
));
634 if (cursor
->wp_viewport
)
636 wp_viewport_set_destination(cursor
->wp_viewport
,
637 round(cursor
->shm_buffer
->width
/ scale
),
638 round(cursor
->shm_buffer
->height
/ scale
));
640 wl_surface_commit(cursor
->wl_surface
);
645 if (cursor
->shm_buffer
)
647 wayland_shm_buffer_unref(cursor
->shm_buffer
);
648 cursor
->shm_buffer
= NULL
;
650 if (cursor
->wp_viewport
)
652 wp_viewport_destroy(cursor
->wp_viewport
);
653 cursor
->wp_viewport
= NULL
;
655 if (cursor
->wl_surface
)
657 wl_surface_destroy(cursor
->wl_surface
);
658 cursor
->wl_surface
= NULL
;
662 static void reapply_cursor_clipping(void)
665 UINT context
= NtUserSetThreadDpiAwarenessContext(NTUSER_DPI_PER_MONITOR_AWARE
);
666 if (NtUserGetClipCursor(&rect
)) NtUserClipCursor(&rect
);
667 NtUserSetThreadDpiAwarenessContext(context
);
670 static void wayland_set_cursor(HWND hwnd
, HCURSOR hcursor
, BOOL use_hcursor
)
672 struct wayland_pointer
*pointer
= &process_wayland
.pointer
;
673 struct wayland_surface
*surface
;
675 BOOL reapply_clip
= FALSE
;
677 if ((surface
= wayland_surface_lock_hwnd(hwnd
)))
679 scale
= surface
->window
.scale
;
680 if (use_hcursor
) surface
->hcursor
= hcursor
;
681 else hcursor
= surface
->hcursor
;
683 pthread_mutex_unlock(&surface
->mutex
);
690 pthread_mutex_lock(&pointer
->mutex
);
691 if (pointer
->focused_hwnd
== hwnd
)
693 if (use_hcursor
) wayland_pointer_update_cursor_buffer(hcursor
, scale
);
694 wayland_pointer_update_cursor_surface(scale
);
695 wl_pointer_set_cursor(pointer
->wl_pointer
,
696 pointer
->enter_serial
,
697 pointer
->cursor
.wl_surface
,
698 pointer
->cursor
.hotspot_x
,
699 pointer
->cursor
.hotspot_y
);
700 wl_display_flush(process_wayland
.wl_display
);
703 pthread_mutex_unlock(&pointer
->mutex
);
705 /* Reapply cursor clip since cursor visibility affects pointer constraint
707 if (reapply_clip
) reapply_cursor_clipping();
710 /**********************************************************************
711 * wayland_surface_calc_confine
713 * Calculates the pointer confine rect (in surface-local coords)
714 * for the specified clip rectangle (in screen coords using thread dpi).
716 static void wayland_surface_calc_confine(struct wayland_surface
*surface
,
717 const RECT
*clip
, RECT
*confine
)
721 TRACE("hwnd=%p clip=%s window=%s\n",
722 surface
->hwnd
, wine_dbgstr_rect(clip
),
723 wine_dbgstr_rect(&surface
->window
.rect
));
725 /* FIXME: surface->window.(client_)rect is in window dpi, whereas
726 * clip is in thread dpi. */
728 if (!intersect_rect(&window_clip
, clip
, &surface
->window
.rect
))
730 SetRectEmpty(confine
);
734 OffsetRect(&window_clip
,
735 -surface
->window
.rect
.left
,
736 -surface
->window
.rect
.top
);
737 wayland_surface_coords_from_window(surface
,
738 window_clip
.left
, window_clip
.top
,
739 (int *)&confine
->left
, (int *)&confine
->top
);
740 wayland_surface_coords_from_window(surface
,
741 window_clip
.right
, window_clip
.bottom
,
742 (int *)&confine
->right
, (int *)&confine
->bottom
);
745 /**********************************************************************
746 * wayland_surface_client_covers_vscreen
748 * Whether a surface window client area covers the whole virtual screen.
750 static BOOL
wayland_surface_client_covers_vscreen(struct wayland_surface
*surface
)
752 RECT vscreen_rect
, rect
;
754 /* Get individual system metrics to get coords in thread dpi
755 * (NtUserGetVirtualScreenRect would return values in system dpi). */
756 vscreen_rect
.left
= NtUserGetSystemMetrics(SM_XVIRTUALSCREEN
);
757 vscreen_rect
.top
= NtUserGetSystemMetrics(SM_YVIRTUALSCREEN
);
758 vscreen_rect
.right
= vscreen_rect
.left
+
759 NtUserGetSystemMetrics(SM_CXVIRTUALSCREEN
);
760 vscreen_rect
.bottom
= vscreen_rect
.top
+
761 NtUserGetSystemMetrics(SM_CYVIRTUALSCREEN
);
763 /* FIXME: surface->window.client_rect is in window dpi, whereas
764 * vscreen_rect is in thread dpi. */
765 intersect_rect(&rect
, &surface
->window
.client_rect
, &vscreen_rect
);
767 return EqualRect(&vscreen_rect
, &rect
);
770 /***********************************************************************
771 * wayland_pointer_update_constraint
773 * Enables/disables pointer confinement.
775 static void wayland_pointer_update_constraint(struct wl_surface
*wl_surface
,
779 struct wayland_pointer
*pointer
= &process_wayland
.pointer
;
780 BOOL needs_relative
, needs_lock
, needs_confine
;
782 needs_lock
= wl_surface
&& (confine_rect
|| covers_vscreen
) &&
783 !pointer
->cursor
.wl_surface
;
784 needs_confine
= wl_surface
&& confine_rect
&& pointer
->cursor
.wl_surface
;
786 if (!needs_confine
&& pointer
->zwp_confined_pointer_v1
)
788 TRACE("Unconfining from hwnd=%p\n", pointer
->constraint_hwnd
);
789 zwp_confined_pointer_v1_destroy(pointer
->zwp_confined_pointer_v1
);
790 pointer
->zwp_confined_pointer_v1
= NULL
;
791 pointer
->constraint_hwnd
= NULL
;
794 if (!needs_lock
&& pointer
->zwp_locked_pointer_v1
)
796 TRACE("Unlocking from hwnd=%p\n", pointer
->constraint_hwnd
);
797 zwp_locked_pointer_v1_destroy(pointer
->zwp_locked_pointer_v1
);
798 pointer
->zwp_locked_pointer_v1
= NULL
;
799 pointer
->constraint_hwnd
= NULL
;
804 HWND hwnd
= wl_surface_get_user_data(wl_surface
);
805 struct wl_region
*region
;
807 region
= wl_compositor_create_region(process_wayland
.wl_compositor
);
808 wl_region_add(region
, confine_rect
->left
, confine_rect
->top
,
809 confine_rect
->right
- confine_rect
->left
,
810 confine_rect
->bottom
- confine_rect
->top
);
812 if (!pointer
->zwp_confined_pointer_v1
|| pointer
->constraint_hwnd
!= hwnd
)
814 if (pointer
->zwp_confined_pointer_v1
)
815 zwp_confined_pointer_v1_destroy(pointer
->zwp_confined_pointer_v1
);
816 pointer
->zwp_confined_pointer_v1
=
817 zwp_pointer_constraints_v1_confine_pointer(
818 process_wayland
.zwp_pointer_constraints_v1
,
822 ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT
);
823 pointer
->constraint_hwnd
= hwnd
;
827 zwp_confined_pointer_v1_set_region(pointer
->zwp_confined_pointer_v1
,
831 TRACE("Confining to hwnd=%p wayland=%d,%d+%d,%d\n",
832 pointer
->constraint_hwnd
,
833 (int)confine_rect
->left
, (int)confine_rect
->top
,
834 (int)(confine_rect
->right
- confine_rect
->left
),
835 (int)(confine_rect
->bottom
- confine_rect
->top
));
837 wl_region_destroy(region
);
841 HWND hwnd
= wl_surface_get_user_data(wl_surface
);
843 if (!pointer
->zwp_locked_pointer_v1
|| pointer
->constraint_hwnd
!= hwnd
)
845 if (pointer
->zwp_locked_pointer_v1
)
846 zwp_locked_pointer_v1_destroy(pointer
->zwp_locked_pointer_v1
);
847 pointer
->zwp_locked_pointer_v1
=
848 zwp_pointer_constraints_v1_lock_pointer(
849 process_wayland
.zwp_pointer_constraints_v1
,
853 ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT
);
854 pointer
->constraint_hwnd
= hwnd
;
855 TRACE("Locking to hwnd=%p\n", pointer
->constraint_hwnd
);
859 needs_relative
= !pointer
->cursor
.wl_surface
&&
860 pointer
->constraint_hwnd
&&
861 pointer
->constraint_hwnd
== pointer
->focused_hwnd
;
863 if (needs_relative
&& !pointer
->zwp_relative_pointer_v1
)
865 pointer
->zwp_relative_pointer_v1
=
866 zwp_relative_pointer_manager_v1_get_relative_pointer(
867 process_wayland
.zwp_relative_pointer_manager_v1
,
868 pointer
->wl_pointer
);
869 zwp_relative_pointer_v1_add_listener(pointer
->zwp_relative_pointer_v1
,
870 &relative_pointer_v1_listener
, NULL
);
871 TRACE("Enabling relative motion\n");
873 else if (!needs_relative
&& pointer
->zwp_relative_pointer_v1
)
875 zwp_relative_pointer_v1_destroy(pointer
->zwp_relative_pointer_v1
);
876 pointer
->zwp_relative_pointer_v1
= NULL
;
877 TRACE("Disabling relative motion\n");
881 void wayland_pointer_clear_constraint(void)
883 wayland_pointer_update_constraint(NULL
, NULL
, FALSE
);
886 /***********************************************************************
889 void WAYLAND_SetCursor(HWND hwnd
, HCURSOR hcursor
)
891 TRACE("hwnd=%p hcursor=%p\n", hwnd
, hcursor
);
893 wayland_set_cursor(hwnd
, hcursor
, TRUE
);
896 /***********************************************************************
899 BOOL
WAYLAND_ClipCursor(const RECT
*clip
, BOOL reset
)
901 struct wayland_pointer
*pointer
= &process_wayland
.pointer
;
902 struct wl_surface
*wl_surface
= NULL
;
903 struct wayland_surface
*surface
= NULL
;
904 BOOL covers_vscreen
= FALSE
;
907 TRACE("clip=%s reset=%d\n", wine_dbgstr_rect(clip
), reset
);
909 if ((surface
= wayland_surface_lock_hwnd(NtUserGetForegroundWindow())))
911 wl_surface
= surface
->wl_surface
;
912 if (clip
) wayland_surface_calc_confine(surface
, clip
, &confine_rect
);
913 covers_vscreen
= wayland_surface_client_covers_vscreen(surface
);
914 pthread_mutex_unlock(&surface
->mutex
);
917 /* Since we are running in the context of the foreground thread we know
918 * that the wl_surface of the foreground HWND will not be invalidated,
919 * so we can access it without having the surface lock. */
920 pthread_mutex_lock(&pointer
->mutex
);
921 wayland_pointer_update_constraint(wl_surface
,
922 (clip
&& wl_surface
) ? &confine_rect
: NULL
,
924 pthread_mutex_unlock(&pointer
->mutex
);
926 wl_display_flush(process_wayland
.wl_display
);