diff --git a/client/displayservers/Wayland/CMakeLists.txt b/client/displayservers/Wayland/CMakeLists.txt index ab98b684..c2586b10 100644 --- a/client/displayservers/Wayland/CMakeLists.txt +++ b/client/displayservers/Wayland/CMakeLists.txt @@ -74,6 +74,9 @@ wayland_generate( wayland_generate( "${WAYLAND_PROTOCOLS_BASE}/stable/presentation-time/presentation-time.xml" "${CMAKE_BINARY_DIR}/wayland/wayland-presentation-time-client-protocol") +wayland_generate( + "${WAYLAND_PROTOCOLS_BASE}/stable/viewporter/viewporter.xml" + "${CMAKE_BINARY_DIR}/wayland/wayland-viewporter-client-protocol") wayland_generate( "${WAYLAND_PROTOCOLS_BASE}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml" "${CMAKE_BINARY_DIR}/wayland/wayland-xdg-decoration-unstable-v1-client-protocol") @@ -89,3 +92,6 @@ wayland_generate( wayland_generate( "${WAYLAND_PROTOCOLS_BASE}/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml" "${CMAKE_BINARY_DIR}/wayland/wayland-idle-inhibit-unstable-v1-client-protocol") +wayland_generate( + "${WAYLAND_PROTOCOLS_BASE}/unstable/xdg-output/xdg-output-unstable-v1.xml" + "${CMAKE_BINARY_DIR}/wayland/wayland-xdg-output-unstable-v1-client-protocol") diff --git a/client/displayservers/Wayland/gl.c b/client/displayservers/Wayland/gl.c index cbef6b3d..e641058d 100644 --- a/client/displayservers/Wayland/gl.c +++ b/client/displayservers/Wayland/gl.c @@ -88,15 +88,37 @@ void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct if (wlWm.needsResize) { - wl_egl_window_resize(wlWm.eglWindow, wlWm.width * wlWm.scale, wlWm.height * wlWm.scale, 0, 0); - wl_surface_set_buffer_scale(wlWm.surface, wlWm.scale); + wl_egl_window_resize(wlWm.eglWindow, wl_fixed_to_int(wlWm.width * wlWm.scale), + wl_fixed_to_int(wlWm.height * wlWm.scale), 0, 0); + + if (wlWm.fractionalScale) + { + wl_surface_set_buffer_scale(wlWm.surface, 1); + if (!wlWm.viewport) + wlWm.viewport = wp_viewporter_get_viewport(wlWm.viewporter, wlWm.surface); + wp_viewport_set_source(wlWm.viewport, 0, 0, wlWm.width * wlWm.scale, wlWm.height * wlWm.scale); + wp_viewport_set_destination(wlWm.viewport, wlWm.width, wlWm.height); + } + else + { + if (wlWm.viewport) + { + wl_fixed_t clear = wl_fixed_from_int(-1); + wp_viewport_set_source(wlWm.viewport, clear, clear, clear, clear); + wp_viewport_set_destination(wlWm.viewport, -1, -1); + wp_viewport_destroy(wlWm.viewport); + wlWm.viewport = NULL; + } + wl_surface_set_buffer_scale(wlWm.surface, wl_fixed_to_int(wlWm.scale)); + } struct wl_region * region = wl_compositor_create_region(wlWm.compositor); wl_region_add(region, 0, 0, wlWm.width, wlWm.height); wl_surface_set_opaque_region(wlWm.surface, region); wl_region_destroy(region); - app_handleResizeEvent(wlWm.width, wlWm.height, wlWm.scale, (struct Border) {0, 0, 0, 0}); + app_handleResizeEvent(wlWm.width, wlWm.height, wl_fixed_to_double(wlWm.scale), + (struct Border) {0, 0, 0, 0}); wlWm.needsResize = false; } diff --git a/client/displayservers/Wayland/output.c b/client/displayservers/Wayland/output.c index b016359f..1214b4b8 100644 --- a/client/displayservers/Wayland/output.c +++ b/client/displayservers/Wayland/output.c @@ -27,29 +27,63 @@ #include "common/debug.h" -static void outputGeometryHandler(void * data, struct wl_output * output, int32_t x, int32_t y, +static void outputUpdateScale(struct WaylandOutput * node) +{ + wl_fixed_t original = node->scale; + + if (!wlWm.viewporter || !node->logicalWidth || !node->logicalHeight) + node->scale = wl_fixed_from_int(node->scaleInt); + else + { + int32_t modeWidth = node->modeRotate ? node->modeHeight : node->modeWidth; + node->scale = wl_fixed_from_double(1.0 * modeWidth / node->logicalWidth); + } + + if (original != node->scale) + waylandWindowUpdateScale(); +} + +static void outputGeometryHandler(void * opaque, struct wl_output * output, int32_t x, int32_t y, int32_t physical_width, int32_t physical_height, int32_t subpixel, const char * make, const char * model, int32_t output_transform) { - // Do nothing. + struct WaylandOutput * node = opaque; + + switch (output_transform) + { + case WL_OUTPUT_TRANSFORM_90: + case WL_OUTPUT_TRANSFORM_270: + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + node->modeRotate = true; + break; + + default: + node->modeRotate = false; + } } -static void outputModeHandler(void * data, struct wl_output * wl_output, uint32_t flags, +static void outputModeHandler(void * opaque, struct wl_output * wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { - // Do nothing. + if (!(flags & WL_OUTPUT_MODE_CURRENT)) + return; + + struct WaylandOutput * node = opaque; + node->modeWidth = width; + node->modeHeight = height; } -static void outputDoneHandler(void * data, struct wl_output * output) +static void outputDoneHandler(void * opaque, struct wl_output * output) { - // Do nothing. + struct WaylandOutput * node = opaque; + outputUpdateScale(node); } static void outputScaleHandler(void * opaque, struct wl_output * output, int32_t scale) { struct WaylandOutput * node = opaque; - node->scale = scale; - waylandWindowUpdateScale(); + node->scaleInt = scale; } static const struct wl_output_listener outputListener = { @@ -59,6 +93,45 @@ static const struct wl_output_listener outputListener = { .scale = outputScaleHandler, }; +static void xdgOutputLogicalPositionHandler(void * opaque, struct zxdg_output_v1 * xdgOutput, + int32_t x, int32_t y) +{ + // Do nothing. +} + +static void xdgOutputLogicalSizeHandler(void * opaque, struct zxdg_output_v1 * xdgOutput, + int32_t width, int32_t height) +{ + struct WaylandOutput * node = opaque; + node->logicalWidth = width; + node->logicalHeight = height; +} + +static void xdgOutputDoneHandler(void * opaque, struct zxdg_output_v1 * xdgOutput) +{ + struct WaylandOutput * node = opaque; + outputUpdateScale(node); +} + +static void xdgOutputNameHandler(void * opaque, struct zxdg_output_v1 * xdgOutput, const char * name) +{ + // Do nothing. +} + +static void xdgOutputDescriptionHandler(void * opaque, struct zxdg_output_v1 * xdgOutput, + const char * description) +{ + // Do nothing. +} + +static const struct zxdg_output_v1_listener xdgOutputListener = { + .logical_position = xdgOutputLogicalPositionHandler, + .logical_size = xdgOutputLogicalSizeHandler, + .done = xdgOutputDoneHandler, + .name = xdgOutputNameHandler, + .description = xdgOutputDescriptionHandler, +}; + bool waylandOutputInit(void) { wl_list_init(&wlWm.outputs); @@ -73,6 +146,8 @@ void waylandOutputFree(void) { if (node->version >= 3) wl_output_release(node->output); + if (node->xdgOutput) + zxdg_output_v1_destroy(node->xdgOutput); wl_list_remove(&node->link); free(node); } @@ -80,7 +155,7 @@ void waylandOutputFree(void) void waylandOutputBind(uint32_t name, uint32_t version) { - struct WaylandOutput * node = malloc(sizeof(struct WaylandOutput)); + struct WaylandOutput * node = calloc(1, sizeof(struct WaylandOutput)); if (!node) return; @@ -103,6 +178,13 @@ void waylandOutputBind(uint32_t name, uint32_t version) return; } + if (wlWm.xdgOutputManager) + { + node->xdgOutput = zxdg_output_manager_v1_get_xdg_output(wlWm.xdgOutputManager, node->output); + if (node->xdgOutput) + zxdg_output_v1_add_listener(node->xdgOutput, &xdgOutputListener, node); + } + wl_output_add_listener(node->output, &outputListener, node); wl_list_insert(&wlWm.outputs, &node->link); } @@ -117,6 +199,8 @@ void waylandOutputTryUnbind(uint32_t name) { if (node->version >= 3) wl_output_release(node->output); + if (node->xdgOutput) + zxdg_output_v1_destroy(node->xdgOutput); wl_list_remove(&node->link); free(node); break; @@ -124,7 +208,7 @@ void waylandOutputTryUnbind(uint32_t name) } } -int32_t waylandOutputGetScale(struct wl_output * output) +wl_fixed_t waylandOutputGetScale(struct wl_output * output) { struct WaylandOutput * node; diff --git a/client/displayservers/Wayland/registry.c b/client/displayservers/Wayland/registry.c index afde486b..85288ddb 100644 --- a/client/displayservers/Wayland/registry.c +++ b/client/displayservers/Wayland/registry.c @@ -50,6 +50,9 @@ static void registryGlobalHandler(void * data, struct wl_registry * registry, else if (!strcmp(interface, wp_presentation_interface.name)) wlWm.presentation = wl_registry_bind(wlWm.registry, name, &wp_presentation_interface, 1); + else if (!strcmp(interface, wp_viewporter_interface.name)) + wlWm.viewporter = wl_registry_bind(wlWm.registry, name, + &wp_viewporter_interface, 1); else if (!strcmp(interface, zwp_relative_pointer_manager_v1_interface.name)) wlWm.relativePointerManager = wl_registry_bind(wlWm.registry, name, &zwp_relative_pointer_manager_v1_interface, 1); @@ -65,6 +68,9 @@ static void registryGlobalHandler(void * data, struct wl_registry * registry, else if (!strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name)) wlWm.idleInhibitManager = wl_registry_bind(wlWm.registry, name, &zwp_idle_inhibit_manager_v1_interface, 1); + else if (!strcmp(interface, zxdg_output_manager_v1_interface.name) && version >= 2) + wlWm.xdgOutputManager = wl_registry_bind(wlWm.registry, name, + &zxdg_output_manager_v1_interface, 2); } static void registryGlobalRemoveHandler(void * data, diff --git a/client/displayservers/Wayland/wayland.h b/client/displayservers/Wayland/wayland.h index 14f6bfa6..31a6de87 100644 --- a/client/displayservers/Wayland/wayland.h +++ b/client/displayservers/Wayland/wayland.h @@ -40,11 +40,13 @@ #include "wayland-xdg-shell-client-protocol.h" #include "wayland-presentation-time-client-protocol.h" +#include "wayland-viewporter-client-protocol.h" #include "wayland-xdg-decoration-unstable-v1-client-protocol.h" #include "wayland-keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h" #include "wayland-pointer-constraints-unstable-v1-client-protocol.h" #include "wayland-relative-pointer-unstable-v1-client-protocol.h" #include "wayland-idle-inhibit-unstable-v1-client-protocol.h" +#include "wayland-xdg-output-unstable-v1-client-protocol.h" typedef void (*WaylandPollCallback)(uint32_t events, void * opaque); @@ -60,8 +62,15 @@ struct WaylandPoll struct WaylandOutput { uint32_t name; - int32_t scale; + wl_fixed_t scale; + int32_t scaleInt; + int32_t logicalWidth; + int32_t logicalHeight; + int32_t modeWidth; + int32_t modeHeight; + bool modeRotate; struct wl_output * output; + struct zxdg_output_v1 * xdgOutput; uint32_t version; struct wl_list link; }; @@ -93,7 +102,9 @@ struct WaylandDSState struct wl_shm * shm; struct wl_compositor * compositor; - int32_t width, height, scale; + int32_t width, height; + wl_fixed_t scale; + bool fractionalScale; bool needsResize; bool fullscreen; uint32_t resizeSerial; @@ -158,6 +169,9 @@ struct WaylandDSState struct zwp_idle_inhibit_manager_v1 * idleInhibitManager; struct zwp_idle_inhibitor_v1 * idleInhibitor; + struct wp_viewporter * viewporter; + struct wp_viewport * viewport; + struct zxdg_output_manager_v1 * xdgOutputManager; struct wl_list outputs; // WaylandOutput::link struct wl_list surfaceOutputs; // SurfaceOutput::link @@ -259,7 +273,7 @@ bool waylandOutputInit(void); void waylandOutputFree(void); void waylandOutputBind(uint32_t name, uint32_t version); void waylandOutputTryUnbind(uint32_t name); -int32_t waylandOutputGetScale(struct wl_output * output); +wl_fixed_t waylandOutputGetScale(struct wl_output * output); // poll module bool waylandPollInit(void); diff --git a/client/displayservers/Wayland/window.c b/client/displayservers/Wayland/window.c index 5504108d..a0e26789 100644 --- a/client/displayservers/Wayland/window.c +++ b/client/displayservers/Wayland/window.c @@ -33,12 +33,12 @@ void waylandWindowUpdateScale(void) { - int32_t maxScale = 0; + wl_fixed_t maxScale = 0; struct SurfaceOutput * node; wl_list_for_each(node, &wlWm.surfaceOutputs, link) { - int32_t scale = waylandOutputGetScale(node->output); + wl_fixed_t scale = waylandOutputGetScale(node->output); if (scale > maxScale) maxScale = scale; } @@ -46,6 +46,7 @@ void waylandWindowUpdateScale(void) if (maxScale) { wlWm.scale = maxScale; + wlWm.fractionalScale = wl_fixed_from_int(wl_fixed_to_int(maxScale)) != maxScale; wlWm.needsResize = true; } } @@ -77,7 +78,7 @@ static const struct wl_surface_listener wlSurfaceListener = { bool waylandWindowInit(const char * title, bool fullscreen, bool maximize, bool borderless) { - wlWm.scale = 1; + wlWm.scale = wl_fixed_from_int(1); if (!wlWm.compositor) {