winewayland.drv: Prepare to handle different coordinate spaces.
[wine.git] / dlls / winewayland.drv / wayland_pointer.c
blob025793c15d63add164b84ff6a6e97ad705f2f4bb
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(void *data, struct wl_pointer *wl_pointer,
50 uint32_t time, wl_fixed_t sx, wl_fixed_t sy)
52 INPUT input = {0};
53 RECT *window_rect;
54 HWND hwnd;
55 POINT screen;
56 struct wayland_surface *surface;
58 if (!(hwnd = wayland_pointer_get_focused_hwnd())) return;
59 if (!(surface = wayland_surface_lock_hwnd(hwnd))) return;
61 window_rect = &surface->window.rect;
63 wayland_surface_coords_to_window(surface,
64 wl_fixed_to_double(sx),
65 wl_fixed_to_double(sy),
66 (int *)&screen.x, (int *)&screen.y);
67 screen.x += window_rect->left;
68 screen.y += window_rect->top;
69 /* Sometimes, due to rounding, we may end up with pointer coordinates
70 * slightly outside the target window, so bring them within bounds. */
71 if (screen.x >= window_rect->right) screen.x = window_rect->right - 1;
72 else if (screen.x < window_rect->left) screen.x = window_rect->left;
73 if (screen.y >= window_rect->bottom) screen.y = window_rect->bottom - 1;
74 else if (screen.y < window_rect->top) screen.y = window_rect->top;
76 pthread_mutex_unlock(&surface->mutex);
78 /* Hardware input events are in physical coordinates. */
79 if (!NtUserLogicalToPerMonitorDPIPhysicalPoint(hwnd, &screen)) return;
81 input.type = INPUT_MOUSE;
82 input.mi.dx = screen.x;
83 input.mi.dy = screen.y;
84 input.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
86 TRACE("hwnd=%p wayland_xy=%.2f,%.2f screen_xy=%d,%d\n",
87 hwnd, wl_fixed_to_double(sx), wl_fixed_to_double(sy),
88 (int)screen.x, (int)screen.y);
90 __wine_send_input(hwnd, &input, NULL);
93 static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer,
94 uint32_t serial, struct wl_surface *wl_surface,
95 wl_fixed_t sx, wl_fixed_t sy)
97 struct wayland_pointer *pointer = &process_wayland.pointer;
98 HWND hwnd;
100 if (!wl_surface) return;
101 /* The wl_surface user data remains valid and immutable for the whole
102 * lifetime of the object, so it's safe to access without locking. */
103 hwnd = wl_surface_get_user_data(wl_surface);
105 TRACE("hwnd=%p\n", hwnd);
107 pthread_mutex_lock(&pointer->mutex);
108 pointer->focused_hwnd = hwnd;
109 pointer->enter_serial = serial;
110 /* The cursor is undefined at every enter, so we set it again with
111 * the latest information we have. */
112 wl_pointer_set_cursor(pointer->wl_pointer,
113 pointer->enter_serial,
114 pointer->cursor.wl_surface,
115 pointer->cursor.hotspot_x,
116 pointer->cursor.hotspot_y);
117 pthread_mutex_unlock(&pointer->mutex);
119 /* Handle the enter as a motion, to account for cases where the
120 * window first appears beneath the pointer and won't get a separate
121 * motion event. */
122 pointer_handle_motion(data, wl_pointer, 0, sx, sy);
125 static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer,
126 uint32_t serial, struct wl_surface *wl_surface)
128 struct wayland_pointer *pointer = &process_wayland.pointer;
130 if (!wl_surface) return;
132 TRACE("hwnd=%p\n", wl_surface_get_user_data(wl_surface));
134 pthread_mutex_lock(&pointer->mutex);
135 pointer->focused_hwnd = NULL;
136 pointer->enter_serial = 0;
137 pthread_mutex_unlock(&pointer->mutex);
140 static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
141 uint32_t serial, uint32_t time, uint32_t button,
142 uint32_t state)
144 struct wayland_pointer *pointer = &process_wayland.pointer;
145 INPUT input = {0};
146 HWND hwnd;
148 if (!(hwnd = wayland_pointer_get_focused_hwnd())) return;
150 input.type = INPUT_MOUSE;
152 switch (button)
154 case BTN_LEFT: input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN; break;
155 case BTN_RIGHT: input.mi.dwFlags = MOUSEEVENTF_RIGHTDOWN; break;
156 case BTN_MIDDLE: input.mi.dwFlags = MOUSEEVENTF_MIDDLEDOWN; break;
157 default: break;
160 if (state == WL_POINTER_BUTTON_STATE_RELEASED) input.mi.dwFlags <<= 1;
162 pthread_mutex_lock(&pointer->mutex);
163 pointer->button_serial = state == WL_POINTER_BUTTON_STATE_PRESSED ?
164 serial : 0;
165 pthread_mutex_unlock(&pointer->mutex);
167 TRACE("hwnd=%p button=%#x state=%u\n", hwnd, button, state);
169 __wine_send_input(hwnd, &input, NULL);
172 static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
173 uint32_t time, uint32_t axis, wl_fixed_t value)
177 static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer)
181 static void pointer_handle_axis_source(void *data, struct wl_pointer *wl_pointer,
182 uint32_t axis_source)
186 static void pointer_handle_axis_stop(void *data, struct wl_pointer *wl_pointer,
187 uint32_t time, uint32_t axis)
191 static void pointer_handle_axis_discrete(void *data, struct wl_pointer *wl_pointer,
192 uint32_t axis, int32_t discrete)
194 INPUT input = {0};
195 HWND hwnd;
197 if (!(hwnd = wayland_pointer_get_focused_hwnd())) return;
199 input.type = INPUT_MOUSE;
201 switch (axis)
203 case WL_POINTER_AXIS_VERTICAL_SCROLL:
204 input.mi.dwFlags = MOUSEEVENTF_WHEEL;
205 input.mi.mouseData = -WHEEL_DELTA * discrete;
206 break;
207 case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
208 input.mi.dwFlags = MOUSEEVENTF_HWHEEL;
209 input.mi.mouseData = WHEEL_DELTA * discrete;
210 break;
211 default: break;
214 TRACE("hwnd=%p axis=%u discrete=%d\n", hwnd, axis, discrete);
216 __wine_send_input(hwnd, &input, NULL);
219 static const struct wl_pointer_listener pointer_listener =
221 pointer_handle_enter,
222 pointer_handle_leave,
223 pointer_handle_motion,
224 pointer_handle_button,
225 pointer_handle_axis,
226 pointer_handle_frame,
227 pointer_handle_axis_source,
228 pointer_handle_axis_stop,
229 pointer_handle_axis_discrete
232 void wayland_pointer_init(struct wl_pointer *wl_pointer)
234 struct wayland_pointer *pointer = &process_wayland.pointer;
236 pthread_mutex_lock(&pointer->mutex);
237 pointer->wl_pointer = wl_pointer;
238 pointer->focused_hwnd = NULL;
239 pointer->enter_serial = 0;
240 pthread_mutex_unlock(&pointer->mutex);
241 wl_pointer_add_listener(pointer->wl_pointer, &pointer_listener, NULL);
244 void wayland_pointer_deinit(void)
246 struct wayland_pointer *pointer = &process_wayland.pointer;
248 pthread_mutex_lock(&pointer->mutex);
249 wl_pointer_release(pointer->wl_pointer);
250 pointer->wl_pointer = NULL;
251 pointer->focused_hwnd = NULL;
252 pointer->enter_serial = 0;
253 pthread_mutex_unlock(&pointer->mutex);
256 /***********************************************************************
257 * create_mono_cursor_buffer
259 * Create a wayland_shm_buffer for a monochrome cursor bitmap.
261 * Adapted from wineandroid.drv code.
263 static struct wayland_shm_buffer *create_mono_cursor_buffer(HBITMAP bmp)
265 struct wayland_shm_buffer *shm_buffer = NULL;
266 BITMAP bm;
267 char *mask = NULL;
268 unsigned int i, j, stride, mask_size, *ptr;
270 if (!NtGdiExtGetObjectW(bmp, sizeof(bm), &bm)) goto done;
271 stride = ((bm.bmWidth + 15) >> 3) & ~1;
272 mask_size = stride * bm.bmHeight;
273 if (!(mask = malloc(mask_size))) goto done;
274 if (!NtGdiGetBitmapBits(bmp, mask_size, mask)) goto done;
276 bm.bmHeight /= 2;
277 shm_buffer = wayland_shm_buffer_create(bm.bmWidth, bm.bmHeight,
278 WL_SHM_FORMAT_ARGB8888);
279 if (!shm_buffer) goto done;
281 ptr = shm_buffer->map_data;
282 for (i = 0; i < bm.bmHeight; i++)
284 for (j = 0; j < bm.bmWidth; j++, ptr++)
286 int and = ((mask[i * stride + j / 8] << (j % 8)) & 0x80);
287 int xor = ((mask[(i + bm.bmHeight) * stride + j / 8] << (j % 8)) & 0x80);
288 if (!xor && and)
289 *ptr = 0;
290 else if (xor && !and)
291 *ptr = 0xffffffff;
292 else
293 /* we can't draw "invert" pixels, so render them as black instead */
294 *ptr = 0xff000000;
298 done:
299 free(mask);
300 return shm_buffer;
303 /***********************************************************************
304 * create_color_cursor_buffer
306 * Create a wayland_shm_buffer for a color cursor bitmap.
308 * Adapted from wineandroid.drv code.
310 static struct wayland_shm_buffer *create_color_cursor_buffer(HDC hdc, HBITMAP color,
311 HBITMAP mask)
313 struct wayland_shm_buffer *shm_buffer = NULL;
314 char buffer[FIELD_OFFSET(BITMAPINFO, bmiColors[256])];
315 BITMAPINFO *info = (BITMAPINFO *)buffer;
316 BITMAP bm;
317 unsigned int *ptr, *bits = NULL;
318 unsigned char *mask_bits = NULL;
319 int i, j;
320 BOOL has_alpha = FALSE;
322 if (!NtGdiExtGetObjectW(color, sizeof(bm), &bm)) goto failed;
324 shm_buffer = wayland_shm_buffer_create(bm.bmWidth, bm.bmHeight,
325 WL_SHM_FORMAT_ARGB8888);
326 if (!shm_buffer) goto failed;
327 bits = shm_buffer->map_data;
329 info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
330 info->bmiHeader.biWidth = bm.bmWidth;
331 info->bmiHeader.biHeight = -bm.bmHeight;
332 info->bmiHeader.biPlanes = 1;
333 info->bmiHeader.biBitCount = 32;
334 info->bmiHeader.biCompression = BI_RGB;
335 info->bmiHeader.biSizeImage = bm.bmWidth * bm.bmHeight * 4;
336 info->bmiHeader.biXPelsPerMeter = 0;
337 info->bmiHeader.biYPelsPerMeter = 0;
338 info->bmiHeader.biClrUsed = 0;
339 info->bmiHeader.biClrImportant = 0;
341 if (!NtGdiGetDIBitsInternal(hdc, color, 0, bm.bmHeight, bits, info,
342 DIB_RGB_COLORS, 0, 0))
343 goto failed;
345 for (i = 0; i < bm.bmWidth * bm.bmHeight; i++)
346 if ((has_alpha = (bits[i] & 0xff000000) != 0)) break;
348 if (!has_alpha)
350 unsigned int width_bytes = (bm.bmWidth + 31) / 32 * 4;
351 /* generate alpha channel from the mask */
352 info->bmiHeader.biBitCount = 1;
353 info->bmiHeader.biSizeImage = width_bytes * bm.bmHeight;
354 if (!(mask_bits = malloc(info->bmiHeader.biSizeImage))) goto failed;
355 if (!NtGdiGetDIBitsInternal(hdc, mask, 0, bm.bmHeight, mask_bits,
356 info, DIB_RGB_COLORS, 0, 0))
357 goto failed;
358 ptr = bits;
359 for (i = 0; i < bm.bmHeight; i++)
361 for (j = 0; j < bm.bmWidth; j++, ptr++)
363 if (!((mask_bits[i * width_bytes + j / 8] << (j % 8)) & 0x80))
364 *ptr |= 0xff000000;
367 free(mask_bits);
370 /* Wayland requires pre-multiplied alpha values */
371 for (ptr = bits, i = 0; i < bm.bmWidth * bm.bmHeight; ptr++, i++)
373 unsigned char alpha = *ptr >> 24;
374 if (alpha == 0)
376 *ptr = 0;
378 else if (alpha != 255)
380 *ptr = (alpha << 24) |
381 (((BYTE)(*ptr >> 16) * alpha / 255) << 16) |
382 (((BYTE)(*ptr >> 8) * alpha / 255) << 8) |
383 (((BYTE)*ptr * alpha / 255));
387 return shm_buffer;
389 failed:
390 if (shm_buffer) wayland_shm_buffer_unref(shm_buffer);
391 free(mask_bits);
392 return NULL;
395 /***********************************************************************
396 * get_icon_info
398 * Local GetIconInfoExW helper implementation.
400 static BOOL get_icon_info(HICON handle, ICONINFOEXW *ret)
402 UNICODE_STRING module, res_name;
403 ICONINFO info;
405 module.Buffer = ret->szModName;
406 module.MaximumLength = sizeof(ret->szModName) - sizeof(WCHAR);
407 res_name.Buffer = ret->szResName;
408 res_name.MaximumLength = sizeof(ret->szResName) - sizeof(WCHAR);
409 if (!NtUserGetIconInfo(handle, &info, &module, &res_name, NULL, 0)) return FALSE;
410 ret->fIcon = info.fIcon;
411 ret->xHotspot = info.xHotspot;
412 ret->yHotspot = info.yHotspot;
413 ret->hbmColor = info.hbmColor;
414 ret->hbmMask = info.hbmMask;
415 ret->wResID = res_name.Length ? 0 : LOWORD(res_name.Buffer);
416 ret->szModName[module.Length] = 0;
417 ret->szResName[res_name.Length] = 0;
418 return TRUE;
421 static void wayland_pointer_update_cursor(HCURSOR hcursor)
423 struct wayland_cursor *cursor = &process_wayland.pointer.cursor;
424 ICONINFOEXW info = {0};
426 if (!hcursor) goto clear_cursor;
428 /* Create a new buffer for the specified cursor. */
429 if (cursor->shm_buffer)
431 wayland_shm_buffer_unref(cursor->shm_buffer);
432 cursor->shm_buffer = NULL;
435 if (!get_icon_info(hcursor, &info))
437 ERR("Failed to get icon info for cursor=%p\n", hcursor);
438 goto clear_cursor;
441 if (info.hbmColor)
443 HDC hdc = NtGdiCreateCompatibleDC(0);
444 cursor->shm_buffer =
445 create_color_cursor_buffer(hdc, info.hbmColor, info.hbmMask);
446 NtGdiDeleteObjectApp(hdc);
448 else
450 cursor->shm_buffer = create_mono_cursor_buffer(info.hbmMask);
453 if (info.hbmColor) NtGdiDeleteObjectApp(info.hbmColor);
454 if (info.hbmMask) NtGdiDeleteObjectApp(info.hbmMask);
456 cursor->hotspot_x = info.xHotspot;
457 cursor->hotspot_y = info.yHotspot;
459 if (!cursor->shm_buffer)
461 ERR("Failed to create shm_buffer for cursor=%p\n", hcursor);
462 goto clear_cursor;
465 /* Make sure the hotspot is valid. */
466 if (cursor->hotspot_x >= cursor->shm_buffer->width ||
467 cursor->hotspot_y >= cursor->shm_buffer->height)
469 cursor->hotspot_x = cursor->shm_buffer->width / 2;
470 cursor->hotspot_y = cursor->shm_buffer->height / 2;
473 if (!cursor->wl_surface)
475 cursor->wl_surface =
476 wl_compositor_create_surface(process_wayland.wl_compositor);
477 if (!cursor->wl_surface)
479 ERR("Failed to create wl_surface for cursor\n");
480 goto clear_cursor;
484 /* Commit the cursor buffer to the cursor surface. */
485 wl_surface_attach(cursor->wl_surface,
486 cursor->shm_buffer->wl_buffer, 0, 0);
487 wl_surface_damage_buffer(cursor->wl_surface, 0, 0,
488 cursor->shm_buffer->width,
489 cursor->shm_buffer->height);
490 wl_surface_commit(cursor->wl_surface);
492 return;
494 clear_cursor:
495 if (cursor->shm_buffer)
497 wayland_shm_buffer_unref(cursor->shm_buffer);
498 cursor->shm_buffer = NULL;
500 if (cursor->wl_surface)
502 wl_surface_destroy(cursor->wl_surface);
503 cursor->wl_surface = NULL;
507 /***********************************************************************
508 * WAYLAND_SetCursor
510 void WAYLAND_SetCursor(HWND hwnd, HCURSOR hcursor)
512 struct wayland_pointer *pointer = &process_wayland.pointer;
514 TRACE("hwnd=%p hcursor=%p\n", hwnd, hcursor);
516 pthread_mutex_lock(&pointer->mutex);
517 if (pointer->focused_hwnd == hwnd)
519 wayland_pointer_update_cursor(hcursor);
520 wl_pointer_set_cursor(pointer->wl_pointer,
521 pointer->enter_serial,
522 pointer->cursor.wl_surface,
523 pointer->cursor.hotspot_x,
524 pointer->cursor.hotspot_y);
525 wl_display_flush(process_wayland.wl_display);
527 pthread_mutex_unlock(&pointer->mutex);