include/mscvpdb.h: Use flexible array members for the rest of structures.
[wine.git] / dlls / winewayland.drv / wayland_pointer.c
blob1d8acaeabd2da1247803340138c1fe7f02d518dd
1 /*
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
21 #if 0
22 #pragma makedep unix
23 #endif
25 #include "config.h"
27 #include <linux/input.h>
28 #undef SW_MAX /* Also defined in winuser.rh */
29 #include <math.h>
30 #include <stdlib.h>
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;
40 HWND hwnd;
42 pthread_mutex_lock(&pointer->mutex);
43 hwnd = pointer->focused_hwnd;
44 pthread_mutex_unlock(&pointer->mutex);
46 return hwnd;
49 static void pointer_handle_motion_internal(wl_fixed_t sx, wl_fixed_t sy)
51 INPUT input = {0};
52 RECT *window_rect;
53 HWND hwnd;
54 POINT screen;
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;
110 HWND hwnd;
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
130 * motion event. */
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,
151 uint32_t state)
153 struct wayland_pointer *pointer = &process_wayland.pointer;
154 INPUT input = {0};
155 HWND hwnd;
157 if (!(hwnd = wayland_pointer_get_focused_hwnd())) return;
159 input.type = INPUT_MOUSE;
161 switch (button)
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;
166 case BTN_SIDE:
167 case BTN_BACK:
168 input.mi.dwFlags = MOUSEEVENTF_XDOWN;
169 input.mi.mouseData = XBUTTON1;
170 break;
171 case BTN_EXTRA:
172 case BTN_FORWARD:
173 input.mi.dwFlags = MOUSEEVENTF_XDOWN;
174 input.mi.mouseData = XBUTTON2;
175 break;
176 default: break;
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 ?
183 serial : 0;
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)
213 INPUT input = {0};
214 HWND hwnd;
216 if (!(hwnd = wayland_pointer_get_focused_hwnd())) return;
218 input.type = INPUT_MOUSE;
220 switch (axis)
222 case WL_POINTER_AXIS_VERTICAL_SCROLL:
223 input.mi.dwFlags = MOUSEEVENTF_WHEEL;
224 input.mi.mouseData = -WHEEL_DELTA * discrete;
225 break;
226 case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
227 input.mi.dwFlags = MOUSEEVENTF_HWHEEL;
228 input.mi.mouseData = WHEEL_DELTA * discrete;
229 break;
230 default: break;
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,
244 pointer_handle_axis,
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)
257 INPUT input = {0};
258 HWND hwnd;
259 POINT screen, origin;
260 struct wayland_surface *surface;
261 RECT window_rect;
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. */
278 if (screen.x >= 0)
280 origin.x = window_rect.left;
281 screen.x += origin.x;
282 if (screen.x >= window_rect.right) screen.x = window_rect.right - 1;
284 else
286 origin.x = window_rect.right;
287 screen.x += origin.x;
288 if (screen.x < window_rect.left) screen.x = window_rect.left;
291 if (screen.y >= 0)
293 origin.y = window_rect.top;
294 screen.y += origin.y;
295 if (screen.y >= window_rect.bottom) screen.y = window_rect.bottom - 1;
297 else
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;
377 BITMAP bm;
378 char *mask = 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;
387 bm.bmHeight /= 2;
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);
399 if (!xor && and)
400 *ptr = 0;
401 else if (xor && !and)
402 *ptr = 0xffffffff;
403 else
404 /* we can't draw "invert" pixels, so render them as black instead */
405 *ptr = 0xff000000;
409 done:
410 free(mask);
411 return shm_buffer;
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,
422 HBITMAP mask)
424 struct wayland_shm_buffer *shm_buffer = NULL;
425 char buffer[FIELD_OFFSET(BITMAPINFO, bmiColors[256])];
426 BITMAPINFO *info = (BITMAPINFO *)buffer;
427 BITMAP bm;
428 unsigned int *ptr, *bits = NULL;
429 unsigned char *mask_bits = NULL;
430 int i, j;
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))
454 goto failed;
456 for (i = 0; i < bm.bmWidth * bm.bmHeight; i++)
457 if ((has_alpha = (bits[i] & 0xff000000) != 0)) break;
459 if (!has_alpha)
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))
468 goto failed;
469 ptr = bits;
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))
475 *ptr |= 0xff000000;
478 free(mask_bits);
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;
485 if (alpha == 0)
487 *ptr = 0;
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));
498 return shm_buffer;
500 failed:
501 if (shm_buffer) wayland_shm_buffer_unref(shm_buffer);
502 free(mask_bits);
503 return NULL;
506 /***********************************************************************
507 * get_icon_info
509 * Local GetIconInfoExW helper implementation.
511 static BOOL get_icon_info(HICON handle, ICONINFOEXW *ret)
513 UNICODE_STRING module, res_name;
514 ICONINFO info;
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;
529 return TRUE;
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);
549 goto clear_cursor;
552 if (info.hbmColor)
554 HDC hdc = NtGdiCreateCompatibleDC(0);
555 cursor->shm_buffer =
556 create_color_cursor_buffer(hdc, info.hbmColor, info.hbmMask);
557 NtGdiDeleteObjectApp(hdc);
559 else
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);
573 goto clear_cursor;
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);
587 return;
589 clear_cursor:
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)
605 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");
610 goto clear_cursor;
614 if (!cursor->wp_viewport && process_wayland.wp_viewporter)
616 cursor->wp_viewport =
617 wp_viewporter_get_viewport(process_wayland.wp_viewporter,
618 cursor->wl_surface);
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);
642 return;
644 clear_cursor:
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)
664 RECT rect;
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;
674 double scale;
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;
682 use_hcursor = TRUE;
683 pthread_mutex_unlock(&surface->mutex);
685 else
687 scale = 1.0;
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);
701 reapply_clip = TRUE;
703 pthread_mutex_unlock(&pointer->mutex);
705 /* Reapply cursor clip since cursor visibility affects pointer constraint
706 * behavior. */
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)
719 RECT window_clip;
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);
731 return;
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,
776 RECT *confine_rect,
777 BOOL covers_vscreen)
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;
802 if (needs_confine)
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,
819 wl_surface,
820 pointer->wl_pointer,
821 region,
822 ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
823 pointer->constraint_hwnd = hwnd;
825 else
827 zwp_confined_pointer_v1_set_region(pointer->zwp_confined_pointer_v1,
828 region);
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);
839 else if (needs_lock)
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,
850 wl_surface,
851 pointer->wl_pointer,
852 NULL,
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 /***********************************************************************
887 * WAYLAND_SetCursor
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 /***********************************************************************
897 * WAYLAND_ClipCursor
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;
905 RECT confine_rect;
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,
923 covers_vscreen);
924 pthread_mutex_unlock(&pointer->mutex);
926 wl_display_flush(process_wayland.wl_display);
928 return TRUE;