mirror of
git://source.winehq.org/git/wine.git
synced 2024-10-03 00:42:46 +00:00
winewayland.drv: Implement SetCursor using cursor bitmap data.
Set the cursor image used for Wayland surfaces by using the Windows cursor bitmap data.
This commit is contained in:
parent
b8b90e403d
commit
1b987bfde6
|
@ -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"
|
||||
|
@ -93,6 +94,14 @@ static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer,
|
|||
|
||||
pthread_mutex_lock(&pointer->mutex);
|
||||
pointer->focused_hwnd = hwnd;
|
||||
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(pointer->wl_pointer,
|
||||
pointer->enter_serial,
|
||||
pointer->cursor.wl_surface,
|
||||
pointer->cursor.hotspot_x,
|
||||
pointer->cursor.hotspot_y);
|
||||
pthread_mutex_unlock(&pointer->mutex);
|
||||
|
||||
/* Handle the enter as a motion, to account for cases where the
|
||||
|
@ -112,6 +121,7 @@ static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer,
|
|||
|
||||
pthread_mutex_lock(&pointer->mutex);
|
||||
pointer->focused_hwnd = NULL;
|
||||
pointer->enter_serial = 0;
|
||||
pthread_mutex_unlock(&pointer->mutex);
|
||||
}
|
||||
|
||||
|
@ -208,6 +218,7 @@ void wayland_pointer_init(struct wl_pointer *wl_pointer)
|
|||
pthread_mutex_lock(&pointer->mutex);
|
||||
pointer->wl_pointer = wl_pointer;
|
||||
pointer->focused_hwnd = NULL;
|
||||
pointer->enter_serial = 0;
|
||||
pthread_mutex_unlock(&pointer->mutex);
|
||||
wl_pointer_add_listener(pointer->wl_pointer, &pointer_listener, NULL);
|
||||
}
|
||||
|
@ -220,5 +231,280 @@ void wayland_pointer_deinit(void)
|
|||
wl_pointer_release(pointer->wl_pointer);
|
||||
pointer->wl_pointer = NULL;
|
||||
pointer->focused_hwnd = NULL;
|
||||
pointer->enter_serial = 0;
|
||||
pthread_mutex_unlock(&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_pointer_update_cursor(HCURSOR hcursor)
|
||||
{
|
||||
struct wayland_cursor *cursor = &process_wayland.pointer.cursor;
|
||||
ICONINFOEXW info = {0};
|
||||
|
||||
if (!hcursor) goto clear_cursor;
|
||||
|
||||
/* Create a new buffer for the specified cursor. */
|
||||
if (cursor->shm_buffer)
|
||||
{
|
||||
wayland_shm_buffer_unref(cursor->shm_buffer);
|
||||
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);
|
||||
cursor->shm_buffer =
|
||||
create_color_cursor_buffer(hdc, info.hbmColor, info.hbmMask);
|
||||
NtGdiDeleteObjectApp(hdc);
|
||||
}
|
||||
else
|
||||
{
|
||||
cursor->shm_buffer = create_mono_cursor_buffer(info.hbmMask);
|
||||
}
|
||||
|
||||
if (info.hbmColor) NtGdiDeleteObjectApp(info.hbmColor);
|
||||
if (info.hbmMask) NtGdiDeleteObjectApp(info.hbmMask);
|
||||
|
||||
cursor->hotspot_x = info.xHotspot;
|
||||
cursor->hotspot_y = info.yHotspot;
|
||||
|
||||
if (!cursor->shm_buffer)
|
||||
{
|
||||
ERR("Failed to create shm_buffer for cursor=%p\n", hcursor);
|
||||
goto clear_cursor;
|
||||
}
|
||||
|
||||
/* Make sure the hotspot is valid. */
|
||||
if (cursor->hotspot_x >= cursor->shm_buffer->width ||
|
||||
cursor->hotspot_y >= cursor->shm_buffer->height)
|
||||
{
|
||||
cursor->hotspot_x = cursor->shm_buffer->width / 2;
|
||||
cursor->hotspot_y = cursor->shm_buffer->height / 2;
|
||||
}
|
||||
|
||||
if (!cursor->wl_surface)
|
||||
{
|
||||
cursor->wl_surface =
|
||||
wl_compositor_create_surface(process_wayland.wl_compositor);
|
||||
if (!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(cursor->wl_surface,
|
||||
cursor->shm_buffer->wl_buffer, 0, 0);
|
||||
wl_surface_damage_buffer(cursor->wl_surface, 0, 0,
|
||||
cursor->shm_buffer->width,
|
||||
cursor->shm_buffer->height);
|
||||
wl_surface_commit(cursor->wl_surface);
|
||||
|
||||
return;
|
||||
|
||||
clear_cursor:
|
||||
if (cursor->shm_buffer)
|
||||
{
|
||||
wayland_shm_buffer_unref(cursor->shm_buffer);
|
||||
cursor->shm_buffer = NULL;
|
||||
}
|
||||
if (cursor->wl_surface)
|
||||
{
|
||||
wl_surface_destroy(cursor->wl_surface);
|
||||
cursor->wl_surface = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* WAYLAND_SetCursor
|
||||
*/
|
||||
void WAYLAND_SetCursor(HWND hwnd, HCURSOR hcursor)
|
||||
{
|
||||
struct wayland_pointer *pointer = &process_wayland.pointer;
|
||||
|
||||
TRACE("hwnd=%p hcursor=%p\n", hwnd, hcursor);
|
||||
|
||||
pthread_mutex_lock(&pointer->mutex);
|
||||
if (pointer->focused_hwnd == hwnd)
|
||||
{
|
||||
wayland_pointer_update_cursor(hcursor);
|
||||
wl_pointer_set_cursor(pointer->wl_pointer,
|
||||
pointer->enter_serial,
|
||||
pointer->cursor.wl_surface,
|
||||
pointer->cursor.hotspot_x,
|
||||
pointer->cursor.hotspot_y);
|
||||
wl_display_flush(process_wayland.wl_display);
|
||||
}
|
||||
pthread_mutex_unlock(&pointer->mutex);
|
||||
}
|
||||
|
|
|
@ -126,7 +126,10 @@ void wayland_surface_destroy(struct wayland_surface *surface)
|
|||
{
|
||||
pthread_mutex_lock(&process_wayland.pointer.mutex);
|
||||
if (process_wayland.pointer.focused_hwnd == surface->hwnd)
|
||||
{
|
||||
process_wayland.pointer.focused_hwnd = NULL;
|
||||
process_wayland.pointer.enter_serial = 0;
|
||||
}
|
||||
pthread_mutex_unlock(&process_wayland.pointer.mutex);
|
||||
|
||||
pthread_mutex_lock(&xdg_data_mutex);
|
||||
|
|
|
@ -56,10 +56,19 @@ enum wayland_window_message
|
|||
WM_WAYLAND_INIT_DISPLAY_DEVICES = 0x80001000
|
||||
};
|
||||
|
||||
struct wayland_cursor
|
||||
{
|
||||
struct wayland_shm_buffer *shm_buffer;
|
||||
struct wl_surface *wl_surface;
|
||||
int hotspot_x, hotspot_y;
|
||||
};
|
||||
|
||||
struct wayland_pointer
|
||||
{
|
||||
struct wl_pointer *wl_pointer;
|
||||
HWND focused_hwnd;
|
||||
uint32_t enter_serial;
|
||||
struct wayland_cursor cursor;
|
||||
pthread_mutex_t mutex;
|
||||
};
|
||||
|
||||
|
@ -204,6 +213,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;
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue