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(void *data
, struct wl_pointer
*wl_pointer
,
50 uint32_t time
, wl_fixed_t sx
, wl_fixed_t sy
)
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
;
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
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
,
144 struct wayland_pointer
*pointer
= &process_wayland
.pointer
;
148 if (!(hwnd
= wayland_pointer_get_focused_hwnd())) return;
150 input
.type
= INPUT_MOUSE
;
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;
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
?
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
)
197 if (!(hwnd
= wayland_pointer_get_focused_hwnd())) return;
199 input
.type
= INPUT_MOUSE
;
203 case WL_POINTER_AXIS_VERTICAL_SCROLL
:
204 input
.mi
.dwFlags
= MOUSEEVENTF_WHEEL
;
205 input
.mi
.mouseData
= -WHEEL_DELTA
* discrete
;
207 case WL_POINTER_AXIS_HORIZONTAL_SCROLL
:
208 input
.mi
.dwFlags
= MOUSEEVENTF_HWHEEL
;
209 input
.mi
.mouseData
= WHEEL_DELTA
* discrete
;
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
,
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
;
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
;
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);
290 else if (xor && !and)
293 /* we can't draw "invert" pixels, so render them as black instead */
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
,
313 struct wayland_shm_buffer
*shm_buffer
= NULL
;
314 char buffer
[FIELD_OFFSET(BITMAPINFO
, bmiColors
[256])];
315 BITMAPINFO
*info
= (BITMAPINFO
*)buffer
;
317 unsigned int *ptr
, *bits
= NULL
;
318 unsigned char *mask_bits
= NULL
;
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))
345 for (i
= 0; i
< bm
.bmWidth
* bm
.bmHeight
; i
++)
346 if ((has_alpha
= (bits
[i
] & 0xff000000) != 0)) break;
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))
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))
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;
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));
390 if (shm_buffer
) wayland_shm_buffer_unref(shm_buffer
);
395 /***********************************************************************
398 * Local GetIconInfoExW helper implementation.
400 static BOOL
get_icon_info(HICON handle
, ICONINFOEXW
*ret
)
402 UNICODE_STRING module
, res_name
;
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;
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
);
443 HDC hdc
= NtGdiCreateCompatibleDC(0);
445 create_color_cursor_buffer(hdc
, info
.hbmColor
, info
.hbmMask
);
446 NtGdiDeleteObjectApp(hdc
);
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
);
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
)
476 wl_compositor_create_surface(process_wayland
.wl_compositor
);
477 if (!cursor
->wl_surface
)
479 ERR("Failed to create wl_surface for cursor\n");
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
);
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 /***********************************************************************
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
);