From: Alexandros Frantzis alexandros.frantzis@collabora.com
Set the cursor image used for Wayland surfaces by using the Windows cursor bitmap data. --- dlls/winewayland.drv/wayland_pointer.c | 293 +++++++++++++++++++++++++ dlls/winewayland.drv/waylanddrv.h | 9 + dlls/winewayland.drv/waylanddrv_main.c | 1 + dlls/winewayland.drv/window.c | 18 ++ 4 files changed, 321 insertions(+)
diff --git a/dlls/winewayland.drv/wayland_pointer.c b/dlls/winewayland.drv/wayland_pointer.c index 6b4a5aacee9..31d8a1378be 100644 --- a/dlls/winewayland.drv/wayland_pointer.c +++ b/dlls/winewayland.drv/wayland_pointer.c @@ -27,6 +27,7 @@ #include <linux/input.h> #undef SW_MAX /* Also defined in winuser.rh */ #include <math.h> +#include <stdlib.h>
#include "waylanddrv.h" #include "wine/debug.h" @@ -106,6 +107,14 @@ static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer, { TRACE("surface=%p hwnd=%p\n", surface, surface->hwnd); process_wayland.pointer.surface = surface; + process_wayland.pointer.enter_serial = serial; + /* The cursor is undefined at every enter, so we set it again with + * the latest information we have. */ + wl_pointer_set_cursor(process_wayland.pointer.wl_pointer, + process_wayland.pointer.enter_serial, + surface->cursor.wl_surface, + surface->cursor.hotspot_x, + surface->cursor.hotspot_y); pthread_mutex_unlock(&surface->mutex); } pthread_mutex_unlock(&process_wayland.pointer.mutex); @@ -130,6 +139,7 @@ static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer, { TRACE("surface=%p hwnd=%p\n", surface, surface->hwnd); process_wayland.pointer.surface = NULL; + process_wayland.pointer.enter_serial = 0; } pthread_mutex_unlock(&surface->mutex); } @@ -245,3 +255,286 @@ void wayland_pointer_deinit(void) process_wayland.pointer.surface = NULL; pthread_mutex_unlock(&process_wayland.pointer.mutex); } + +/*********************************************************************** + * create_mono_cursor_buffer + * + * Create a wayland_shm_buffer for a monochrome cursor bitmap. + * + * Adapted from wineandroid.drv code. + */ +static struct wayland_shm_buffer *create_mono_cursor_buffer(HBITMAP bmp) +{ + struct wayland_shm_buffer *shm_buffer = NULL; + BITMAP bm; + char *mask = NULL; + unsigned int i, j, stride, mask_size, *ptr; + + if (!NtGdiExtGetObjectW(bmp, sizeof(bm), &bm)) goto done; + stride = ((bm.bmWidth + 15) >> 3) & ~1; + mask_size = stride * bm.bmHeight; + if (!(mask = malloc(mask_size))) goto done; + if (!NtGdiGetBitmapBits(bmp, mask_size, mask)) goto done; + + bm.bmHeight /= 2; + shm_buffer = wayland_shm_buffer_create(bm.bmWidth, bm.bmHeight, + WL_SHM_FORMAT_ARGB8888); + if (!shm_buffer) goto done; + + ptr = shm_buffer->map_data; + for (i = 0; i < bm.bmHeight; i++) + { + for (j = 0; j < bm.bmWidth; j++, ptr++) + { + int and = ((mask[i * stride + j / 8] << (j % 8)) & 0x80); + int xor = ((mask[(i + bm.bmHeight) * stride + j / 8] << (j % 8)) & 0x80); + if (!xor && and) + *ptr = 0; + else if (xor && !and) + *ptr = 0xffffffff; + else + /* we can't draw "invert" pixels, so render them as black instead */ + *ptr = 0xff000000; + } + } + +done: + free(mask); + return shm_buffer; +} + +/*********************************************************************** + * create_color_cursor_buffer + * + * Create a wayland_shm_buffer for a color cursor bitmap. + * + * Adapted from wineandroid.drv code. + */ +static struct wayland_shm_buffer *create_color_cursor_buffer(HDC hdc, HBITMAP color, + HBITMAP mask) +{ + struct wayland_shm_buffer *shm_buffer = NULL; + char buffer[FIELD_OFFSET(BITMAPINFO, bmiColors[256])]; + BITMAPINFO *info = (BITMAPINFO *)buffer; + BITMAP bm; + unsigned int *ptr, *bits = NULL; + unsigned char *mask_bits = NULL; + int i, j; + BOOL has_alpha = FALSE; + + if (!NtGdiExtGetObjectW(color, sizeof(bm), &bm)) goto failed; + + shm_buffer = wayland_shm_buffer_create(bm.bmWidth, bm.bmHeight, + WL_SHM_FORMAT_ARGB8888); + if (!shm_buffer) goto failed; + bits = shm_buffer->map_data; + + info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + info->bmiHeader.biWidth = bm.bmWidth; + info->bmiHeader.biHeight = -bm.bmHeight; + info->bmiHeader.biPlanes = 1; + info->bmiHeader.biBitCount = 32; + info->bmiHeader.biCompression = BI_RGB; + info->bmiHeader.biSizeImage = bm.bmWidth * bm.bmHeight * 4; + info->bmiHeader.biXPelsPerMeter = 0; + info->bmiHeader.biYPelsPerMeter = 0; + info->bmiHeader.biClrUsed = 0; + info->bmiHeader.biClrImportant = 0; + + if (!NtGdiGetDIBitsInternal(hdc, color, 0, bm.bmHeight, bits, info, + DIB_RGB_COLORS, 0, 0)) + goto failed; + + for (i = 0; i < bm.bmWidth * bm.bmHeight; i++) + if ((has_alpha = (bits[i] & 0xff000000) != 0)) break; + + if (!has_alpha) + { + unsigned int width_bytes = (bm.bmWidth + 31) / 32 * 4; + /* generate alpha channel from the mask */ + info->bmiHeader.biBitCount = 1; + info->bmiHeader.biSizeImage = width_bytes * bm.bmHeight; + if (!(mask_bits = malloc(info->bmiHeader.biSizeImage))) goto failed; + if (!NtGdiGetDIBitsInternal(hdc, mask, 0, bm.bmHeight, mask_bits, + info, DIB_RGB_COLORS, 0, 0)) + goto failed; + ptr = bits; + for (i = 0; i < bm.bmHeight; i++) + { + for (j = 0; j < bm.bmWidth; j++, ptr++) + { + if (!((mask_bits[i * width_bytes + j / 8] << (j % 8)) & 0x80)) + *ptr |= 0xff000000; + } + } + free(mask_bits); + } + + /* Wayland requires pre-multiplied alpha values */ + for (ptr = bits, i = 0; i < bm.bmWidth * bm.bmHeight; ptr++, i++) + { + unsigned char alpha = *ptr >> 24; + if (alpha == 0) + { + *ptr = 0; + } + else if (alpha != 255) + { + *ptr = (alpha << 24) | + (((BYTE)(*ptr >> 16) * alpha / 255) << 16) | + (((BYTE)(*ptr >> 8) * alpha / 255) << 8) | + (((BYTE)*ptr * alpha / 255)); + } + } + + return shm_buffer; + +failed: + if (shm_buffer) wayland_shm_buffer_unref(shm_buffer); + free(mask_bits); + return NULL; +} + +/*********************************************************************** + * get_icon_info + * + * Local GetIconInfoExW helper implementation. + */ +static BOOL get_icon_info(HICON handle, ICONINFOEXW *ret) +{ + UNICODE_STRING module, res_name; + ICONINFO info; + + module.Buffer = ret->szModName; + module.MaximumLength = sizeof(ret->szModName) - sizeof(WCHAR); + res_name.Buffer = ret->szResName; + res_name.MaximumLength = sizeof(ret->szResName) - sizeof(WCHAR); + if (!NtUserGetIconInfo(handle, &info, &module, &res_name, NULL, 0)) return FALSE; + ret->fIcon = info.fIcon; + ret->xHotspot = info.xHotspot; + ret->yHotspot = info.yHotspot; + ret->hbmColor = info.hbmColor; + ret->hbmMask = info.hbmMask; + ret->wResID = res_name.Length ? 0 : LOWORD(res_name.Buffer); + ret->szModName[module.Length] = 0; + ret->szResName[res_name.Length] = 0; + return TRUE; +} + +static void wayland_surface_update_cursor(struct wayland_surface *surface, + HCURSOR hcursor) +{ + ICONINFOEXW info = {0}; + + if (!hcursor) goto clear_cursor; + + /* Create a new buffer for the specified cursor. */ + if (surface->cursor.shm_buffer) + { + wayland_shm_buffer_unref(surface->cursor.shm_buffer); + surface->cursor.shm_buffer = NULL; + } + + if (!get_icon_info(hcursor, &info)) + { + ERR("Failed to get icon info for cursor=%p\n", hcursor); + goto clear_cursor; + } + + if (info.hbmColor) + { + HDC hdc = NtGdiCreateCompatibleDC(0); + surface->cursor.shm_buffer = + create_color_cursor_buffer(hdc, info.hbmColor, info.hbmMask); + NtGdiDeleteObjectApp(hdc); + } + else + { + surface->cursor.shm_buffer = create_mono_cursor_buffer(info.hbmMask); + } + + if (info.hbmColor) NtGdiDeleteObjectApp(info.hbmColor); + if (info.hbmMask) NtGdiDeleteObjectApp(info.hbmMask); + + surface->cursor.hotspot_x = info.xHotspot; + surface->cursor.hotspot_y = info.yHotspot; + + if (!surface->cursor.shm_buffer) + { + ERR("Failed to create shm_buffer for cursor=%p\n", hcursor); + goto clear_cursor; + } + + /* Make sure the hotspot is valid. */ + if (surface->cursor.hotspot_x >= surface->cursor.shm_buffer->width || + surface->cursor.hotspot_y >= surface->cursor.shm_buffer->height) + { + surface->cursor.hotspot_x = surface->cursor.shm_buffer->width / 2; + surface->cursor.hotspot_y = surface->cursor.shm_buffer->height / 2; + } + + if (!surface->cursor.wl_surface) + { + surface->cursor.wl_surface = + wl_compositor_create_surface(process_wayland.wl_compositor); + if (!surface->cursor.wl_surface) + { + ERR("Failed to create wl_surface for cursor\n"); + goto clear_cursor; + } + } + + /* Commit the cursor buffer to the cursor surface. */ + wl_surface_attach(surface->cursor.wl_surface, + surface->cursor.shm_buffer->wl_buffer, 0, 0); + wl_surface_damage_buffer(surface->cursor.wl_surface, 0, 0, + surface->cursor.shm_buffer->width, + surface->cursor.shm_buffer->height); + wl_surface_commit(surface->cursor.wl_surface); + + return; + +clear_cursor: + if (surface->cursor.shm_buffer) + { + wayland_shm_buffer_unref(surface->cursor.shm_buffer); + surface->cursor.shm_buffer = NULL; + } + if (surface->cursor.wl_surface) + { + wl_surface_destroy(surface->cursor.wl_surface); + surface->cursor.wl_surface = NULL; + } +} + +/*********************************************************************** + * WAYLAND_SetCursor + */ +void WAYLAND_SetCursor(HWND hwnd, HCURSOR hcursor) +{ + struct wayland_surface *surface; + + TRACE("hwnd=%p hcursor=%p\n", hwnd, hcursor); + + pthread_mutex_lock(&process_wayland.pointer.mutex); + + if ((surface = wayland_surface_lock_hwnd(hwnd))) + { + wayland_surface_update_cursor(surface, hcursor); + + if (process_wayland.pointer.surface == surface) + { + wl_pointer_set_cursor(process_wayland.pointer.wl_pointer, + process_wayland.pointer.enter_serial, + surface->cursor.wl_surface, + surface->cursor.hotspot_x, + surface->cursor.hotspot_y); + } + + wl_display_flush(process_wayland.wl_display); + + pthread_mutex_unlock(&surface->mutex); + } + + pthread_mutex_unlock(&process_wayland.pointer.mutex); +} diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index 14d0c7e5575..17025c37048 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -71,6 +71,7 @@ struct wayland { struct wl_pointer *wl_pointer; struct wayland_surface *surface; + uint32_t enter_serial; pthread_mutex_t mutex; } pointer; struct wl_list output_list; @@ -115,6 +116,12 @@ struct wayland_surface pthread_mutex_t mutex; uint32_t current_serial; struct wayland_shm_buffer *latest_window_buffer; + struct + { + struct wayland_shm_buffer *shm_buffer; + struct wl_surface *wl_surface; + int hotspot_x, hotspot_y; + } cursor; };
struct wayland_shm_buffer @@ -156,6 +163,7 @@ void wayland_surface_attach_shm(struct wayland_surface *surface, struct wayland_shm_buffer *shm_buffer, HRGN surface_damage_region) DECLSPEC_HIDDEN; struct wayland_surface *wayland_surface_lock_proxy(struct wl_proxy *wl_proxy) DECLSPEC_HIDDEN; +struct wayland_surface *wayland_surface_lock_hwnd(HWND hwnd) DECLSPEC_HIDDEN;
/********************************************************************** * Wayland SHM buffer @@ -203,6 +211,7 @@ RGNDATA *get_region_data(HRGN region) DECLSPEC_HIDDEN;
LRESULT WAYLAND_DesktopWindowProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) DECLSPEC_HIDDEN; void WAYLAND_DestroyWindow(HWND hwnd) DECLSPEC_HIDDEN; +void WAYLAND_SetCursor(HWND hwnd, HCURSOR hcursor) DECLSPEC_HIDDEN; BOOL WAYLAND_UpdateDisplayDevices(const struct gdi_device_manager *device_manager, BOOL force, void *param) DECLSPEC_HIDDEN; LRESULT WAYLAND_WindowMessage(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) DECLSPEC_HIDDEN; diff --git a/dlls/winewayland.drv/waylanddrv_main.c b/dlls/winewayland.drv/waylanddrv_main.c index 530cdd83d90..eb54efe1180 100644 --- a/dlls/winewayland.drv/waylanddrv_main.c +++ b/dlls/winewayland.drv/waylanddrv_main.c @@ -33,6 +33,7 @@ static const struct user_driver_funcs waylanddrv_funcs = { .pDesktopWindowProc = WAYLAND_DesktopWindowProc, .pDestroyWindow = WAYLAND_DestroyWindow, + .pSetCursor = WAYLAND_SetCursor, .pUpdateDisplayDevices = WAYLAND_UpdateDisplayDevices, .pWindowMessage = WAYLAND_WindowMessage, .pWindowPosChanged = WAYLAND_WindowPosChanged, diff --git a/dlls/winewayland.drv/window.c b/dlls/winewayland.drv/window.c index efe72baf92e..6f4867dd561 100644 --- a/dlls/winewayland.drv/window.c +++ b/dlls/winewayland.drv/window.c @@ -338,3 +338,21 @@ void wayland_window_flush(HWND hwnd)
wayland_win_data_release(data); } + +/********************************************************************** + * wayland_surface_lock_hwnd + */ +struct wayland_surface *wayland_surface_lock_hwnd(HWND hwnd) +{ + struct wayland_win_data *data = wayland_win_data_get(hwnd); + struct wayland_surface *surface = NULL; + + if (data) + { + surface = data->wayland_surface; + if (surface) pthread_mutex_lock(&data->wayland_surface->mutex); + wayland_win_data_release(data); + } + + return surface; +}