[PATCH v3 0/1] MR11101: winewayland: Implement fractional scaling protocol.
The idea behind this is to use fractional scaling to make it appear as if the Wine environment is running at the user-specified DPI in winecfg while keeping the windows looking crisp. This enables users to have different fractional scales per display under wine without causing blur. This matches the behavior under XWayland and is actually better than the XWayland behavior when using multiple displays with different fractional scales. FWIW: Mutter has a bug with its implementation of fractional scaling that has been recently fixed in case this compositor is being used to try this implementation: <https://gitlab.gnome.org/GNOME/mutter/-/work_items/4834> -- v3: winewayland: Implement fractional scaling protocol. https://gitlab.winehq.org/wine/wine/-/merge_requests/11101
From: Etaash Mathamsetty <etaash.mathamsetty@gmail.com> --- dlls/winewayland.drv/Makefile.in | 1 + dlls/winewayland.drv/fractional-scale-v1.xml | 102 +++++++++++++++++++ dlls/winewayland.drv/wayland.c | 8 ++ dlls/winewayland.drv/wayland_surface.c | 80 +++++++++++++++ dlls/winewayland.drv/waylanddrv.h | 3 + dlls/winewayland.drv/window.c | 1 - 6 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 dlls/winewayland.drv/fractional-scale-v1.xml diff --git a/dlls/winewayland.drv/Makefile.in b/dlls/winewayland.drv/Makefile.in index cbc49823cd6..54c3f19012d 100644 --- a/dlls/winewayland.drv/Makefile.in +++ b/dlls/winewayland.drv/Makefile.in @@ -10,6 +10,7 @@ SOURCES = \ cursor-shape-v1.xml \ display.c \ dllmain.c \ + fractional-scale-v1.xml \ opengl.c \ pointer-constraints-unstable-v1.xml \ pointer-warp-v1.xml \ diff --git a/dlls/winewayland.drv/fractional-scale-v1.xml b/dlls/winewayland.drv/fractional-scale-v1.xml new file mode 100644 index 00000000000..350bfc01eaf --- /dev/null +++ b/dlls/winewayland.drv/fractional-scale-v1.xml @@ -0,0 +1,102 @@ +<?xml version="1.0" encoding="UTF-8"?> +<protocol name="fractional_scale_v1"> + <copyright> + Copyright © 2022 Kenny Levinsen + + 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> + + <description summary="Protocol for requesting fractional surface scales"> + This protocol allows a compositor to suggest for surfaces to render at + fractional scales. + + A client can submit scaled content by utilizing wp_viewport. This is done by + creating a wp_viewport object for the surface and setting the destination + rectangle to the surface size before the scale factor is applied. + + The buffer size is calculated by multiplying the surface size by the + intended scale. + + The wl_surface buffer scale should remain set to 1. + + If a surface has a surface-local size of 100 px by 50 px and wishes to + submit buffers with a scale of 1.5, then a buffer of 150px by 75 px should + be used and the wp_viewport destination rectangle should be 100 px by 50 px. + + For toplevel surfaces, the size is rounded halfway away from zero. The + rounding algorithm for subsurface position and size is not defined. + </description> + + <interface name="wp_fractional_scale_manager_v1" version="1"> + <description summary="fractional surface scale information"> + A global interface for requesting surfaces to use fractional scales. + </description> + + <request name="destroy" type="destructor"> + <description summary="unbind the fractional surface scale interface"> + Informs the server that the client will not be using this protocol + object anymore. This does not affect any other objects, + wp_fractional_scale_v1 objects included. + </description> + </request> + + <enum name="error"> + <entry name="fractional_scale_exists" value="0" + summary="the surface already has a fractional_scale object associated"/> + </enum> + + <request name="get_fractional_scale"> + <description summary="extend surface interface for scale information"> + Create an add-on object for the the wl_surface to let the compositor + request fractional scales. If the given wl_surface already has a + wp_fractional_scale_v1 object associated, the fractional_scale_exists + protocol error is raised. + </description> + <arg name="id" type="new_id" interface="wp_fractional_scale_v1" + summary="the new surface scale info interface id"/> + <arg name="surface" type="object" interface="wl_surface" + summary="the surface"/> + </request> + </interface> + + <interface name="wp_fractional_scale_v1" version="1"> + <description summary="fractional scale interface to a wl_surface"> + An additional interface to a wl_surface object which allows the compositor + to inform the client of the preferred scale. + </description> + + <request name="destroy" type="destructor"> + <description summary="remove surface scale information for surface"> + Destroy the fractional scale object. When this object is destroyed, + preferred_scale events will no longer be sent. + </description> + </request> + + <event name="preferred_scale"> + <description summary="notify of new preferred scale"> + Notification of a new preferred scale for this surface that the + compositor suggests that the client should use. + + The sent scale is the numerator of a fraction with a denominator of 120. + </description> + <arg name="scale" type="uint" summary="the new preferred scale"/> + </event> + </interface> +</protocol> diff --git a/dlls/winewayland.drv/wayland.c b/dlls/winewayland.drv/wayland.c index b3d6b3c89e7..d5afc1f970e 100644 --- a/dlls/winewayland.drv/wayland.c +++ b/dlls/winewayland.drv/wayland.c @@ -205,6 +205,11 @@ static void registry_handle_global(void *data, struct wl_registry *registry, process_wayland.wp_pointer_warp_v1 = wl_registry_bind(registry, id, &wp_pointer_warp_v1_interface, 1); } + else if (strcmp(interface, "wp_fractional_scale_manager_v1") == 0) + { + process_wayland.wp_fractional_scale_manager_v1 = + wl_registry_bind(registry, id, &wp_fractional_scale_manager_v1_interface, 1); + } } static void registry_handle_global_remove(void *data, struct wl_registry *registry, @@ -338,6 +343,9 @@ BOOL wayland_process_init(void) if (!process_wayland.xdg_toplevel_icon_manager_v1) ERR("Wayland compositor doesn't support xdg_toplevel_icon_manager_v1 (window icons will not be supported)\n"); + if (!process_wayland.wp_fractional_scale_manager_v1) + ERR("Wayland compositor doesn't support wp_fractional_scale_manager_v1 (fractional scaling will be broken)\n"); + process_wayland.initialized = TRUE; return TRUE; diff --git a/dlls/winewayland.drv/wayland_surface.c b/dlls/winewayland.drv/wayland_surface.c index 2f8275ddb52..140de7f9685 100644 --- a/dlls/winewayland.drv/wayland_surface.c +++ b/dlls/winewayland.drv/wayland_surface.c @@ -138,6 +138,53 @@ static const struct xdg_toplevel_listener xdg_toplevel_listener = xdg_toplevel_handle_close }; +void wp_fractional_scale_handle_scale(void* user_data, + struct wp_fractional_scale_v1 *fractional_scale_v1, + uint32_t scale_fixed) +{ + struct wayland_win_data *data; + struct wayland_client_surface *client; + struct wayland_surface *surface; + HWND hwnd = user_data; + BOOL updated = FALSE; + + if ((data = wayland_win_data_get(hwnd))) + { + if ((surface = data->wayland_surface)) + { + double scale = scale_fixed / 120.0; + + updated = (scale != surface->window.scale); + surface->window.scale = scale; + + if (updated) + { + /* reattach the client surface as its rect has changed */ + if ((client = data->client_surface)) + wayland_client_surface_attach(client, client->toplevel); + + /* the subsurface rect has changed */ + if (surface->role == WAYLAND_SURFACE_ROLE_SUBSURFACE) + { + surface->processing.serial = 1; + surface->processing.processed = TRUE; + } + } + + TRACE("hwnd=%p scale %lf\n", hwnd, scale); + } + + wayland_win_data_release(data); + } + + if (updated) NtUserExposeWindowSurface(hwnd, 0, NULL, 0); +} + +static const struct wp_fractional_scale_v1_listener wp_fractional_scale_listener = +{ + wp_fractional_scale_handle_scale +}; + /********************************************************************** * wayland_surface_create * @@ -241,6 +288,27 @@ void wayland_surface_destroy(struct wayland_surface *surface) free(surface); } +static void wayland_surface_init_fractional_scale(struct wayland_surface *surface, + double initial_scale) +{ + if (!process_wayland.wp_fractional_scale_manager_v1) return; + + surface->window.scale = initial_scale; + surface->wp_fractional_scale_v1 = + wp_fractional_scale_manager_v1_get_fractional_scale( + process_wayland.wp_fractional_scale_manager_v1, + surface->wl_surface); + if (!surface->wp_fractional_scale_v1) + { + ERR("Failed to create wp_fractional_scale_v1\n"); + return; + } + wp_fractional_scale_v1_add_listener( + surface->wp_fractional_scale_v1, + &wp_fractional_scale_listener, + surface->hwnd); +} + /********************************************************************** * wayland_surface_make_toplevel * @@ -276,6 +344,8 @@ void wayland_surface_make_toplevel(struct wayland_surface *surface) wayland_surface_assign_icon(surface); + wayland_surface_init_fractional_scale(surface, 1.0); + wl_surface_commit(surface->wl_surface); wl_display_flush(process_wayland.wl_display); @@ -312,6 +382,8 @@ void wayland_surface_make_subsurface(struct wayland_surface *surface, goto err; } + wayland_surface_init_fractional_scale(surface, parent->window.scale); + surface->role = WAYLAND_SURFACE_ROLE_SUBSURFACE; surface->toplevel_hwnd = parent->hwnd; @@ -338,6 +410,14 @@ void wayland_surface_clear_role(struct wayland_surface *surface) { TRACE("surface=%p\n", surface); + /* some objects are shared between several roles */ + + if (surface->wp_fractional_scale_v1) + { + wp_fractional_scale_v1_destroy(surface->wp_fractional_scale_v1); + surface->wp_fractional_scale_v1 = NULL; + } + switch (surface->role) { case WAYLAND_SURFACE_ROLE_NONE: diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h index 5f4bd6a7cf6..7d95d939e59 100644 --- a/dlls/winewayland.drv/waylanddrv.h +++ b/dlls/winewayland.drv/waylanddrv.h @@ -39,6 +39,7 @@ #include "wlr-data-control-unstable-v1-client-protocol.h" #include "xdg-toplevel-icon-v1-client-protocol.h" #include "pointer-warp-v1-client-protocol.h" +#include "fractional-scale-v1-client-protocol.h" #include "windef.h" #include "winbase.h" @@ -172,6 +173,7 @@ struct wayland struct wl_shm *wl_shm; struct wp_viewporter *wp_viewporter; struct wl_subcompositor *wl_subcompositor; + struct wp_fractional_scale_manager_v1 *wp_fractional_scale_manager_v1; struct zwp_pointer_constraints_v1 *zwp_pointer_constraints_v1; struct zwp_relative_pointer_manager_v1 *zwp_relative_pointer_manager_v1; struct zwp_text_input_manager_v3 *zwp_text_input_manager_v3; @@ -268,6 +270,7 @@ struct wayland_surface struct wl_surface *wl_surface; struct wp_viewport *wp_viewport; + struct wp_fractional_scale_v1 *wp_fractional_scale_v1; struct wayland_shm_buffer *small_icon_buffer; struct wayland_shm_buffer *big_icon_buffer; diff --git a/dlls/winewayland.drv/window.c b/dlls/winewayland.drv/window.c index 88eee3421d7..fe95217bb4b 100644 --- a/dlls/winewayland.drv/window.c +++ b/dlls/winewayland.drv/window.c @@ -175,7 +175,6 @@ static void wayland_win_data_get_config(struct wayland_win_data *data, } conf->state = window_state; - conf->scale = NtUserGetSystemDpiForProcess(0) / 96.0; conf->visible = (style & WS_VISIBLE) == WS_VISIBLE; conf->managed = data->managed; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11101
v2: Make win32u DPI and Wayland surface scale more distinct. Rework to make the implementation simpler. This effectively makes the fractional scale protocol required (but when its not supported we just fall back to 1.0 scale) -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11101#note_142711
participants (2)
-
Etaash Mathamsetty -
Etaash Mathamsetty (@etaash.mathamsetty)