This MR uses the cursor-shapes-v1 protocol to tell the compositor which system cursor shape to use. If a shape match is not found (or cursor-shapes-v1 is not available) we fall back to setting the cursor buffer from the Windows cursor data as before.
The second commit implements support for a "UseSystemCursors" driver option, similar to what winex11 has. Since this is the first winewayland driver option we also introduce all the related registry reading code.
--
Side note: The registry code is a copy from winex11, and is the third copy in the codebase (x11, mac, wayland). Perhaps it's worth introducing a common ntuser function to perform the option reading for the drivers, something like `NtUserGetDriverOption("X11 Driver", buffer, buffer_size, TRUE /* whether to read app specific option if present */)`. With such a function there is the concern about each call reopening the registry, but we can see if that's actually a problem and how a different API (or some sort of caching) may help.
From: Alexandros Frantzis alexandros.frantzis@collabora.com
Use the cursor-shapes-v1 protocol to tell the compositor which system cursor shape to use. If a shape match is not found (or cursor-shapes-v1 is not available) we fall back to setting the cursor buffer from the Windows cursor data as before. --- dlls/winewayland.drv/Makefile.in | 1 + dlls/winewayland.drv/cursor-shape-v1.xml | 162 +++++++++++++++++ dlls/winewayland.drv/wayland.c | 5 + dlls/winewayland.drv/wayland_pointer.c | 217 ++++++++++++++++++++--- dlls/winewayland.drv/waylanddrv.h | 3 + 5 files changed, 360 insertions(+), 28 deletions(-) create mode 100644 dlls/winewayland.drv/cursor-shape-v1.xml
diff --git a/dlls/winewayland.drv/Makefile.in b/dlls/winewayland.drv/Makefile.in index 4141e36c9a8..d8062a440d4 100644 --- a/dlls/winewayland.drv/Makefile.in +++ b/dlls/winewayland.drv/Makefile.in @@ -5,6 +5,7 @@ UNIX_LIBS = -lwin32u $(WAYLAND_CLIENT_LIBS) $(WAYLAND_EGL_LIBS) $(XKBCOMMON_LIBS IMPORTS = user32 win32u
SOURCES = \ + cursor-shape-v1.xml \ display.c \ dllmain.c \ opengl.c \ diff --git a/dlls/winewayland.drv/cursor-shape-v1.xml b/dlls/winewayland.drv/cursor-shape-v1.xml new file mode 100644 index 00000000000..64b2f9b2c87 --- /dev/null +++ b/dlls/winewayland.drv/cursor-shape-v1.xml @@ -0,0 +1,162 @@ +<?xml version="1.0" encoding="UTF-8"?> +<protocol name="cursor_shape_v1"> + <copyright> + Copyright 2018 The Chromium Authors + Copyright 2023 Simon Ser + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + </copyright> + + <interface name="wp_cursor_shape_manager_v1" version="2"> + <description summary="cursor shape manager"> + This global offers an alternative, optional way to set cursor images. This + new way uses enumerated cursors instead of a wl_surface like + wl_pointer.set_cursor does. + + Warning! The protocol described in this file is currently in the testing + phase. Backward compatible changes may be added together with the + corresponding interface version bump. Backward incompatible changes can + only be done by creating a new major version of the extension. + </description> + + <request name="destroy" type="destructor"> + <description summary="destroy the manager"> + Destroy the cursor shape manager. + </description> + </request> + + <request name="get_pointer"> + <description summary="manage the cursor shape of a pointer device"> + Obtain a wp_cursor_shape_device_v1 for a wl_pointer object. + + When the pointer capability is removed from the wl_seat, the + wp_cursor_shape_device_v1 object becomes inert. + </description> + <arg name="cursor_shape_device" type="new_id" interface="wp_cursor_shape_device_v1"/> + <arg name="pointer" type="object" interface="wl_pointer"/> + </request> + + <request name="get_tablet_tool_v2"> + <description summary="manage the cursor shape of a tablet tool device"> + Obtain a wp_cursor_shape_device_v1 for a zwp_tablet_tool_v2 object. + + When the zwp_tablet_tool_v2 is removed, the wp_cursor_shape_device_v1 + object becomes inert. + </description> + <arg name="cursor_shape_device" type="new_id" interface="wp_cursor_shape_device_v1"/> + <arg name="tablet_tool" type="object" interface="zwp_tablet_tool_v2"/> + </request> + </interface> + + <interface name="wp_cursor_shape_device_v1" version="2"> + <description summary="cursor shape for a device"> + This interface allows clients to set the cursor shape. + </description> + + <enum name="shape"> + <description summary="cursor shapes"> + This enum describes cursor shapes. + + The names are taken from the CSS W3C specification: + https://w3c.github.io/csswg-drafts/css-ui/#cursor + with a few additions. + + Note that there are some groups of cursor shapes that are related: + The first group is drag-and-drop cursors which are used to indicate + the selected action during dnd operations. The second group is resize + cursors which are used to indicate resizing and moving possibilities + on window borders. It is recommended that the shapes in these groups + should use visually compatible images and metaphors. + </description> + <entry name="default" value="1" summary="default cursor"/> + <entry name="context_menu" value="2" summary="a context menu is available for the object under the cursor"/> + <entry name="help" value="3" summary="help is available for the object under the cursor"/> + <entry name="pointer" value="4" summary="pointer that indicates a link or another interactive element"/> + <entry name="progress" value="5" summary="progress indicator"/> + <entry name="wait" value="6" summary="program is busy, user should wait"/> + <entry name="cell" value="7" summary="a cell or set of cells may be selected"/> + <entry name="crosshair" value="8" summary="simple crosshair"/> + <entry name="text" value="9" summary="text may be selected"/> + <entry name="vertical_text" value="10" summary="vertical text may be selected"/> + <entry name="alias" value="11" summary="drag-and-drop: alias of/shortcut to something is to be created"/> + <entry name="copy" value="12" summary="drag-and-drop: something is to be copied"/> + <entry name="move" value="13" summary="drag-and-drop: something is to be moved"/> + <entry name="no_drop" value="14" summary="drag-and-drop: the dragged item cannot be dropped at the current cursor location"/> + <entry name="not_allowed" value="15" summary="drag-and-drop: the requested action will not be carried out"/> + <entry name="grab" value="16" summary="drag-and-drop: something can be grabbed"/> + <entry name="grabbing" value="17" summary="drag-and-drop: something is being grabbed"/> + <entry name="e_resize" value="18" summary="resizing: the east border is to be moved"/> + <entry name="n_resize" value="19" summary="resizing: the north border is to be moved"/> + <entry name="ne_resize" value="20" summary="resizing: the north-east corner is to be moved"/> + <entry name="nw_resize" value="21" summary="resizing: the north-west corner is to be moved"/> + <entry name="s_resize" value="22" summary="resizing: the south border is to be moved"/> + <entry name="se_resize" value="23" summary="resizing: the south-east corner is to be moved"/> + <entry name="sw_resize" value="24" summary="resizing: the south-west corner is to be moved"/> + <entry name="w_resize" value="25" summary="resizing: the west border is to be moved"/> + <entry name="ew_resize" value="26" summary="resizing: the east and west borders are to be moved"/> + <entry name="ns_resize" value="27" summary="resizing: the north and south borders are to be moved"/> + <entry name="nesw_resize" value="28" summary="resizing: the north-east and south-west corners are to be moved"/> + <entry name="nwse_resize" value="29" summary="resizing: the north-west and south-east corners are to be moved"/> + <entry name="col_resize" value="30" summary="resizing: that the item/column can be resized horizontally"/> + <entry name="row_resize" value="31" summary="resizing: that the item/row can be resized vertically"/> + <entry name="all_scroll" value="32" summary="something can be scrolled in any direction"/> + <entry name="zoom_in" value="33" summary="something can be zoomed in"/> + <entry name="zoom_out" value="34" summary="something can be zoomed out"/> + <entry name="dnd_ask" value="35" summary="drag-and-drop: the user will select which action will be carried out (non-css value)" since="2"/> + <entry name="all_resize" value="36" summary="resizing: something can be moved or resized in any direction (non-css value)" since="2"/> + </enum> + + <enum name="error"> + <entry name="invalid_shape" value="1" + summary="the specified shape value is invalid"/> + </enum> + + <request name="destroy" type="destructor"> + <description summary="destroy the cursor shape device"> + Destroy the cursor shape device. + + The device cursor shape remains unchanged. + </description> + </request> + + <request name="set_shape"> + <description summary="set device cursor to the shape"> + Sets the device cursor to the specified shape. The compositor will + change the cursor image based on the specified shape. + + The cursor actually changes only if the input device focus is one of + the requesting client's surfaces. If any, the previous cursor image + (surface or shape) is replaced. + + The "shape" argument must be a valid enum entry, otherwise the + invalid_shape protocol error is raised. + + This is similar to the wl_pointer.set_cursor and + zwp_tablet_tool_v2.set_cursor requests, but this request accepts a + shape instead of contents in the form of a surface. Clients can mix + set_cursor and set_shape requests. + + The serial parameter must match the latest wl_pointer.enter or + zwp_tablet_tool_v2.proximity_in serial number sent to the client. + Otherwise the request will be ignored. + </description> + <arg name="serial" type="uint" summary="serial number of the enter event"/> + <arg name="shape" type="uint" enum="shape"/> + </request> + </interface> +</protocol> diff --git a/dlls/winewayland.drv/wayland.c b/dlls/winewayland.drv/wayland.c index 7caf33c872e..5b4e1747180 100644 --- a/dlls/winewayland.drv/wayland.c +++ b/dlls/winewayland.drv/wayland.c @@ -189,6 +189,11 @@ static void registry_handle_global(void *data, struct wl_registry *registry, process_wayland.wl_data_device_manager = wl_registry_bind(registry, id, &wl_data_device_manager_interface, 2); } + else if (strcmp(interface, "wp_cursor_shape_manager_v1") == 0) + { + process_wayland.wp_cursor_shape_manager_v1 = + wl_registry_bind(registry, id, &wp_cursor_shape_manager_v1_interface, 1); + } }
static void registry_handle_global_remove(void *data, struct wl_registry *registry, diff --git a/dlls/winewayland.drv/wayland_pointer.c b/dlls/winewayland.drv/wayland_pointer.c index 457c3675cf1..a68045c4da0 100644 --- a/dlls/winewayland.drv/wayland_pointer.c +++ b/dlls/winewayland.drv/wayland_pointer.c @@ -29,11 +29,86 @@ #include <math.h> #include <stdlib.h>
+#define OEMRESOURCE + #include "waylanddrv.h" #include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv);
+/* The cursor-shape-v1 protocol file references the zwp_tablet_tool_v2 + * interface object. Since we don't currently use the tablet protocol, + * provide a dummy object here to avoid linking errors. */ +void *zwp_tablet_tool_v2_interface = NULL; + +struct system_cursors +{ + WORD id; + enum wp_cursor_shape_device_v1_shape shape; +}; + +static const struct system_cursors user32_cursors[] = +{ + {OCR_NORMAL, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT}, + {OCR_IBEAM, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_TEXT}, + {OCR_WAIT, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_WAIT}, + {OCR_CROSS, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CROSSHAIR}, + {OCR_SIZE, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ALL_RESIZE}, + {OCR_SIZENWSE, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NWSE_RESIZE}, + {OCR_SIZENESW, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NESW_RESIZE}, + {OCR_SIZEWE, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_EW_RESIZE}, + {OCR_SIZENS, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NS_RESIZE}, + {OCR_SIZEALL, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ALL_RESIZE}, + {OCR_NO, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NOT_ALLOWED}, + {OCR_HAND, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER}, + {OCR_APPSTARTING, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_PROGRESS}, + {OCR_HELP, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_HELP}, + {OCR_RDR2DIM, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ALL_SCROLL}, + {0} +}; + +static const struct system_cursors comctl32_cursors[] = +{ + {102, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_MOVE}, + {104, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_COPY}, + {105, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT}, + {106, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_COL_RESIZE}, + {107, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_COL_RESIZE}, + {108, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER}, + {135, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ROW_RESIZE}, + {0} +}; + +static const struct system_cursors ole32_cursors[] = +{ + {1, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NO_DROP}, + {2, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_MOVE}, + {3, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_COPY}, + {4, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ALIAS}, + {0} +}; + +static const struct system_cursors riched20_cursors[] = +{ + {105, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER}, + {109, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_COPY}, + {110, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_MOVE}, + {111, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NO_DROP}, + {0} +}; + +static const struct +{ + const struct system_cursors *cursors; + WCHAR name[16]; +} module_cursors[] = +{ + {user32_cursors, {'u','s','e','r','3','2','.','d','l','l',0}}, + {comctl32_cursors, {'c','o','m','c','t','l','3','2','.','d','l','l',0}}, + {ole32_cursors, {'o','l','e','3','2','.','d','l','l',0}}, + {riched20_cursors, {'r','i','c','h','e','d','2','0','.','d','l','l',0}} +}; + static HWND wayland_pointer_get_focused_hwnd(void) { struct wayland_pointer *pointer = &process_wayland.pointer; @@ -579,6 +654,27 @@ clear_cursor: } }
+static void wayland_pointer_clear_cursor_surface(void) +{ + struct wayland_cursor *cursor = &process_wayland.pointer.cursor; + + if (cursor->wp_viewport) + { + wp_viewport_destroy(cursor->wp_viewport); + cursor->wp_viewport = NULL; + } + if (cursor->wl_surface) + { + wl_surface_destroy(cursor->wl_surface); + cursor->wl_surface = NULL; + } + if (cursor->shm_buffer) + { + wayland_shm_buffer_unref(cursor->shm_buffer); + cursor->shm_buffer = NULL; + } +} + static void wayland_pointer_update_cursor_surface(double scale) { struct wayland_cursor *cursor = &process_wayland.pointer.cursor; @@ -627,21 +723,7 @@ static void wayland_pointer_update_cursor_surface(double scale) return;
clear_cursor: - if (cursor->shm_buffer) - { - wayland_shm_buffer_unref(cursor->shm_buffer); - cursor->shm_buffer = NULL; - } - if (cursor->wp_viewport) - { - wp_viewport_destroy(cursor->wp_viewport); - cursor->wp_viewport = NULL; - } - if (cursor->wl_surface) - { - wl_surface_destroy(cursor->wl_surface); - cursor->wl_surface = NULL; - } + wayland_pointer_clear_cursor_surface(); }
static void reapply_cursor_clipping(void) @@ -652,6 +734,76 @@ static void reapply_cursor_clipping(void) NtUserSetThreadDpiAwarenessContext(context); }
+static enum wp_cursor_shape_device_v1_shape cursor_shape_from_info(ICONINFOEXW *info, + uint32_t proto_version) +{ + const struct system_cursors *cursors; + const WCHAR *module; + unsigned int i; + enum wp_cursor_shape_device_v1_shape shape = 0; + + if (!info->szModName[0]) return 0; + if ((module = wcsrchr(info->szModName, '\'))) module++; + else module = info->szModName; + for (i = 0; i < ARRAY_SIZE(module_cursors); i++) + if (!wcsicmp(module, module_cursors[i].name)) break; + if (i == ARRAY_SIZE(module_cursors)) return 0; + + cursors = module_cursors[i].cursors; + for (i = 0; cursors[i].id; i++) + { + if (cursors[i].id == info->wResID) + { + shape = cursors[i].shape; + break; + } + } + + if (shape >= WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DND_ASK && proto_version < 2) + shape = 0; + + return shape; +} + +static BOOL wayland_pointer_set_cursor_shape(HCURSOR hcursor) +{ + struct wayland_pointer *pointer = &process_wayland.pointer; + ICONINFOEXW info = {0}; + enum wp_cursor_shape_device_v1_shape shape = 0; + uint32_t proto_version; + + if (!process_wayland.wp_cursor_shape_manager_v1) return FALSE; + if (!hcursor) return FALSE; + if (!get_icon_info(hcursor, &info)) return FALSE; + proto_version = wp_cursor_shape_manager_v1_get_version( + process_wayland.wp_cursor_shape_manager_v1); + if (!(shape = cursor_shape_from_info(&info, proto_version))) return FALSE; + + if (!pointer->wp_cursor_shape_device_v1) + { + pointer->wp_cursor_shape_device_v1 = + wp_cursor_shape_manager_v1_get_pointer( + process_wayland.wp_cursor_shape_manager_v1, pointer->wl_pointer); + } + if (!pointer->wp_cursor_shape_device_v1) return FALSE; + + wp_cursor_shape_device_v1_set_shape(pointer->wp_cursor_shape_device_v1, + pointer->enter_serial, shape); + + return TRUE; +} + +static void wayland_pointer_clear_cursor_shape(void) +{ + struct wayland_pointer *pointer = &process_wayland.pointer; + + if (pointer->wp_cursor_shape_device_v1) + { + wp_cursor_shape_device_v1_destroy(pointer->wp_cursor_shape_device_v1); + pointer->wp_cursor_shape_device_v1 = NULL; + } +} + static void wayland_set_cursor(HWND hwnd, HCURSOR hcursor, BOOL use_hcursor) { struct wayland_pointer *pointer = &process_wayland.pointer; @@ -681,13 +833,22 @@ static void wayland_set_cursor(HWND hwnd, HCURSOR hcursor, BOOL use_hcursor) pthread_mutex_lock(&pointer->mutex); if (pointer->focused_hwnd == hwnd) { - if (use_hcursor) wayland_pointer_update_cursor_buffer(hcursor, scale); - wayland_pointer_update_cursor_surface(scale); - wl_pointer_set_cursor(pointer->wl_pointer, - pointer->enter_serial, - pointer->cursor.wl_surface, - pointer->cursor.hotspot_x, - pointer->cursor.hotspot_y); + if ((!use_hcursor && pointer->wp_cursor_shape_device_v1) || + (use_hcursor && hcursor && wayland_pointer_set_cursor_shape(hcursor))) + { + wayland_pointer_clear_cursor_surface(); + } + else + { + if (use_hcursor) wayland_pointer_update_cursor_buffer(hcursor, scale); + wayland_pointer_update_cursor_surface(scale); + wl_pointer_set_cursor(pointer->wl_pointer, + pointer->enter_serial, + pointer->cursor.wl_surface, + pointer->cursor.hotspot_x, + pointer->cursor.hotspot_y); + wayland_pointer_clear_cursor_shape(); + } wl_display_flush(process_wayland.wl_display); reapply_clip = TRUE; } @@ -770,6 +931,7 @@ static void wayland_pointer_update_constraint(struct wl_surface *wl_surface, { struct wayland_pointer *pointer = &process_wayland.pointer; BOOL needs_relative, needs_lock, needs_confine; + BOOL is_visible; static unsigned int once;
if (!process_wayland.zwp_pointer_constraints_v1) @@ -779,10 +941,10 @@ static void wayland_pointer_update_constraint(struct wl_surface *wl_surface, return; }
- needs_lock = wl_surface && (((confine_rect || covers_vscreen) && - !pointer->cursor.wl_surface) || force_lock); - needs_confine = wl_surface && confine_rect && pointer->cursor.wl_surface && - !force_lock; + is_visible = pointer->cursor.wl_surface || pointer->wp_cursor_shape_device_v1; + needs_lock = wl_surface && + (((confine_rect || covers_vscreen) && !is_visible) || force_lock); + needs_confine = wl_surface && confine_rect && is_visible && !force_lock;
if (!needs_confine && pointer->zwp_confined_pointer_v1) { @@ -864,8 +1026,7 @@ static void wayland_pointer_update_constraint(struct wl_surface *wl_surface, return; }
- needs_relative = !pointer->cursor.wl_surface && - pointer->constraint_hwnd && + needs_relative = !is_visible && pointer->constraint_hwnd && pointer->constraint_hwnd == pointer->focused_hwnd;
if (needs_relative && !pointer->zwp_relative_pointer_v1) diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index 065d4d31873..8deace46498 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -29,6 +29,7 @@ #include <wayland-client.h> #include <xkbcommon/xkbcommon.h> #include <xkbcommon/xkbregistry.h> +#include "cursor-shape-v1-client-protocol.h" #include "pointer-constraints-unstable-v1-client-protocol.h" #include "relative-pointer-unstable-v1-client-protocol.h" #include "text-input-unstable-v3-client-protocol.h" @@ -105,6 +106,7 @@ struct wayland_pointer struct zwp_confined_pointer_v1 *zwp_confined_pointer_v1; struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1; struct zwp_relative_pointer_v1 *zwp_relative_pointer_v1; + struct wp_cursor_shape_device_v1 *wp_cursor_shape_device_v1; HWND focused_hwnd; HWND constraint_hwnd; BOOL pending_warp; @@ -168,6 +170,7 @@ struct wayland struct zwp_text_input_manager_v3 *zwp_text_input_manager_v3; struct zwlr_data_control_manager_v1 *zwlr_data_control_manager_v1; struct wl_data_device_manager *wl_data_device_manager; + struct wp_cursor_shape_manager_v1 *wp_cursor_shape_manager_v1; struct wayland_seat seat; struct wayland_keyboard keyboard; struct wayland_pointer pointer;
From: Alexandros Frantzis alexandros.frantzis@collabora.com
Allow users to disable system cursor shapes through a driver option. --- dlls/winewayland.drv/wayland_pointer.c | 1 + dlls/winewayland.drv/waylanddrv.h | 1 + dlls/winewayland.drv/waylanddrv_main.c | 146 +++++++++++++++++++++++++ 3 files changed, 148 insertions(+)
diff --git a/dlls/winewayland.drv/wayland_pointer.c b/dlls/winewayland.drv/wayland_pointer.c index a68045c4da0..f836528f6ed 100644 --- a/dlls/winewayland.drv/wayland_pointer.c +++ b/dlls/winewayland.drv/wayland_pointer.c @@ -772,6 +772,7 @@ static BOOL wayland_pointer_set_cursor_shape(HCURSOR hcursor) enum wp_cursor_shape_device_v1_shape shape = 0; uint32_t proto_version;
+ if (!option_use_system_cursors) return FALSE; if (!process_wayland.wp_cursor_shape_manager_v1) return FALSE; if (!hcursor) return FALSE; if (!get_icon_info(hcursor, &info)) return FALSE; diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index 8deace46498..990b7cab003 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -56,6 +56,7 @@
extern char *process_name; extern struct wayland process_wayland; +extern BOOL option_use_system_cursors;
/********************************************************************** * Definitions for wayland types diff --git a/dlls/winewayland.drv/waylanddrv_main.c b/dlls/winewayland.drv/waylanddrv_main.c index dba519b1df1..966e325c59c 100644 --- a/dlls/winewayland.drv/waylanddrv_main.c +++ b/dlls/winewayland.drv/waylanddrv_main.c @@ -31,7 +31,11 @@
#include "waylanddrv.h"
+#define IS_OPTION_TRUE(ch) \ + ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1') + char *process_name = NULL; +BOOL option_use_system_cursors = TRUE;
static const struct user_driver_funcs waylanddrv_funcs = { @@ -55,6 +59,147 @@ static const struct user_driver_funcs waylanddrv_funcs = .pwine_get_wgl_driver = WAYLAND_wine_get_wgl_driver, };
+static inline void ascii_to_unicode(WCHAR *dst, const char *src, size_t len) +{ + while (len--) *dst++ = (unsigned char)*src++; +} + +static inline UINT asciiz_to_unicode(WCHAR *dst, const char *src) +{ + WCHAR *p = dst; + while ((*p++ = *src++)); + return (p - dst) * sizeof(WCHAR); +} + +static HKEY reg_open_key(HKEY root, const WCHAR *name, ULONG name_len) +{ + UNICODE_STRING nameW = {name_len, name_len, (WCHAR *)name}; + OBJECT_ATTRIBUTES attr; + HANDLE ret; + + attr.Length = sizeof(attr); + attr.RootDirectory = root; + attr.ObjectName = &nameW; + attr.Attributes = 0; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + return NtOpenKeyEx(&ret, MAXIMUM_ALLOWED, &attr, 0) ? 0 : ret; +} + +static HKEY open_hkcu_key(const char *name) +{ + WCHAR bufferW[256]; + static HKEY hkcu; + + if (!hkcu) + { + char buffer[256]; + DWORD_PTR sid_data[(sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE) / sizeof(DWORD_PTR)]; + DWORD i, len = sizeof(sid_data); + SID *sid; + + if (NtQueryInformationToken(GetCurrentThreadEffectiveToken(), TokenUser, sid_data, len, &len)) + return 0; + + sid = ((TOKEN_USER *)sid_data)->User.Sid; + len = sprintf(buffer, "\Registry\User\S-%u-%u", sid->Revision, + (int)MAKELONG(MAKEWORD(sid->IdentifierAuthority.Value[5], + sid->IdentifierAuthority.Value[4]), + MAKEWORD(sid->IdentifierAuthority.Value[3], + sid->IdentifierAuthority.Value[2]))); + for (i = 0; i < sid->SubAuthorityCount; i++) + len += sprintf(buffer + len, "-%u", (int)sid->SubAuthority[i]); + + ascii_to_unicode(bufferW, buffer, len); + hkcu = reg_open_key(NULL, bufferW, len * sizeof(WCHAR)); + } + + return reg_open_key(hkcu, bufferW, asciiz_to_unicode(bufferW, name) - sizeof(WCHAR)); +} + +static ULONG query_reg_value(HKEY hkey, const WCHAR *name, + KEY_VALUE_PARTIAL_INFORMATION *info, ULONG size) +{ + unsigned int name_size = name ? lstrlenW(name) * sizeof(WCHAR) : 0; + UNICODE_STRING nameW = {name_size, name_size, (WCHAR *)name}; + + if (NtQueryValueKey(hkey, &nameW, KeyValuePartialInformation, + info, size, &size)) + return 0; + + return size - FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data); +} + +static inline DWORD get_config_key(HKEY defkey, HKEY appkey, const char *name, + WCHAR *buffer, DWORD size) +{ + WCHAR nameW[128]; + char buf[2048]; + KEY_VALUE_PARTIAL_INFORMATION *info = (void *)buf; + + asciiz_to_unicode(nameW, name); + + if (appkey && query_reg_value(appkey, nameW, info, sizeof(buf))) + { + size = min(info->DataLength, size - sizeof(WCHAR)); + memcpy(buffer, info->Data, size); + buffer[size / sizeof(WCHAR)] = 0; + return 0; + } + + if (defkey && query_reg_value(defkey, nameW, info, sizeof(buf))) + { + size = min(info->DataLength, size - sizeof(WCHAR)); + memcpy(buffer, info->Data, size); + buffer[size / sizeof(WCHAR)] = 0; + return 0; + } + + return ERROR_FILE_NOT_FOUND; +} + +static void wayland_init_options(void) +{ + static const WCHAR waylanddriverW[] = {'\','W','a','y','l','a','n','d',' ','D','r','i','v','e','r',0}; + WCHAR buffer[MAX_PATH+16], *p, *appname; + HKEY hkey, appkey = 0; + DWORD len; + + /* @@ Wine registry key: HKCU\Software\Wine\Wayland Driver */ + hkey = open_hkcu_key("Software\Wine\Wayland Driver"); + + /* open the app-specific key */ + appname = NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer; + if ((p = wcsrchr(appname, '/'))) appname = p + 1; + if ((p = wcsrchr(appname, '\'))) appname = p + 1; + len = lstrlenW(appname); + + if (len && len < MAX_PATH) + { + HKEY tmpkey; + int i; + for (i = 0; appname[i]; i++) buffer[i] = RtlDowncaseUnicodeChar(appname[i]); + buffer[i] = 0; + appname = buffer; + if ((process_name = malloc(len * 3 + 1))) + ntdll_wcstoumbs(appname, len + 1, process_name, len * 3 + 1, FALSE); + memcpy(appname + i, waylanddriverW, sizeof(waylanddriverW)); + /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\Wayland Driver */ + if ((tmpkey = open_hkcu_key("Software\Wine\AppDefaults"))) + { + appkey = reg_open_key(tmpkey, appname, lstrlenW(appname) * sizeof(WCHAR)); + NtClose(tmpkey); + } + } + + if (!get_config_key(hkey, appkey, "UseSystemCursors", buffer, sizeof(buffer))) + option_use_system_cursors = IS_OPTION_TRUE(buffer[0]); + + NtClose(appkey); + NtClose(hkey); +} + static void wayland_init_process_name(void) { WCHAR *p, *appname; @@ -90,6 +235,7 @@ static NTSTATUS waylanddrv_unix_init(void *arg) __wine_set_user_driver(&waylanddrv_funcs, WINE_GDI_DRIVER_VERSION);
wayland_init_process_name(); + wayland_init_options();
if (!wayland_process_init()) goto err;