From b10516e1e8352f3140d68fa580b0ed32e13c2d58 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 12 Jul 2024 23:46:57 +0200 Subject: [PATCH 001/519] build: bump version to 0.19.0-dev --- meson.build | 2 +- tinywl/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index c28bb772b..59df19b2b 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'wlroots', 'c', - version: '0.18.0', + version: '0.19.0-dev', license: 'MIT', meson_version: '>=0.59.0', default_options: [ diff --git a/tinywl/Makefile b/tinywl/Makefile index 82cbebd28..64e7af473 100644 --- a/tinywl/Makefile +++ b/tinywl/Makefile @@ -2,7 +2,7 @@ PKG_CONFIG?=pkg-config WAYLAND_PROTOCOLS=$(shell $(PKG_CONFIG) --variable=pkgdatadir wayland-protocols) WAYLAND_SCANNER=$(shell $(PKG_CONFIG) --variable=wayland_scanner wayland-scanner) -PKGS="wlroots-0.18" wayland-server xkbcommon +PKGS="wlroots-0.19" wayland-server xkbcommon CFLAGS+=$(shell $(PKG_CONFIG) --cflags $(PKGS)) LIBS=$(shell $(PKG_CONFIG) --libs $(PKGS)) From 5ecbd23c1d44119cb32b345782d50c9664853109 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Thu, 15 Oct 2020 15:11:52 +0200 Subject: [PATCH 002/519] wlr_surface: fix argument order consistency This swaps the argument order of wlr_surface_accepts_touch() and wlr_surface_accepts_tablet_v2(), putting the wlr_surface argument first as should be the case for functions namespaced with wlr_surface_*. --- include/wlr/types/wlr_seat.h | 2 +- include/wlr/types/wlr_tablet_v2.h | 4 ++-- types/seat/wlr_seat_touch.c | 2 +- types/tablet_v2/wlr_tablet_v2_pad.c | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h index 06d8224ef..24ed5c92f 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -748,6 +748,6 @@ struct wlr_seat_client *wlr_seat_client_from_pointer_resource( /** * Check whether a surface has bound to touch events. */ -bool wlr_surface_accepts_touch(struct wlr_seat *wlr_seat, struct wlr_surface *surface); +bool wlr_surface_accepts_touch(struct wlr_surface *surface, struct wlr_seat *wlr_seat); #endif diff --git a/include/wlr/types/wlr_tablet_v2.h b/include/wlr/types/wlr_tablet_v2.h index 6da893b9c..57e067d75 100644 --- a/include/wlr/types/wlr_tablet_v2.h +++ b/include/wlr/types/wlr_tablet_v2.h @@ -327,6 +327,6 @@ struct wlr_tablet_pad_v2_grab_interface { void wlr_tablet_v2_end_grab(struct wlr_tablet_v2_tablet_pad *pad); void wlr_tablet_v2_start_grab(struct wlr_tablet_v2_tablet_pad *pad, struct wlr_tablet_pad_v2_grab *grab); -bool wlr_surface_accepts_tablet_v2(struct wlr_tablet_v2_tablet *tablet, - struct wlr_surface *surface); +bool wlr_surface_accepts_tablet_v2(struct wlr_surface *surface, + struct wlr_tablet_v2_tablet *tablet); #endif /* WLR_TYPES_WLR_TABLET_V2_H */ diff --git a/types/seat/wlr_seat_touch.c b/types/seat/wlr_seat_touch.c index 09f33d667..3927bd230 100644 --- a/types/seat/wlr_seat_touch.c +++ b/types/seat/wlr_seat_touch.c @@ -476,7 +476,7 @@ bool wlr_seat_validate_touch_grab_serial(struct wlr_seat *seat, return false; } -bool wlr_surface_accepts_touch(struct wlr_seat *wlr_seat, struct wlr_surface *surface) { +bool wlr_surface_accepts_touch(struct wlr_surface *surface, struct wlr_seat *wlr_seat) { struct wl_client *client = wl_resource_get_client(surface->resource); struct wlr_seat_client *seat_client = wlr_seat_client_for_wl_client(wlr_seat, client); if (!seat_client) { diff --git a/types/tablet_v2/wlr_tablet_v2_pad.c b/types/tablet_v2/wlr_tablet_v2_pad.c index ab7c3fc49..052a017d8 100644 --- a/types/tablet_v2/wlr_tablet_v2_pad.c +++ b/types/tablet_v2/wlr_tablet_v2_pad.c @@ -560,8 +560,8 @@ uint32_t wlr_send_tablet_v2_tablet_pad_mode(struct wlr_tablet_v2_tablet_pad *pad return serial; } -bool wlr_surface_accepts_tablet_v2(struct wlr_tablet_v2_tablet *tablet, - struct wlr_surface *surface) { +bool wlr_surface_accepts_tablet_v2(struct wlr_surface *surface, + struct wlr_tablet_v2_tablet *tablet) { struct wl_client *client = wl_resource_get_client(surface->resource); if (tablet->current_client && From d3b7e040af46ab03114d5a40e9ed0c7c6aff15be Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Wed, 22 Sep 2021 12:13:15 +0200 Subject: [PATCH 003/519] wlr_xwayland_surface: fix prefix of two functions Since we're breaking this API anyways, replace the ambiguous "or" in the function name with the explicit "override redirect" to avoid confusion. --- include/wlr/xwayland/xwayland.h | 4 ++-- xwayland/xwm.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/wlr/xwayland/xwayland.h b/include/wlr/xwayland/xwayland.h index 4b476a036..837a57e02 100644 --- a/include/wlr/xwayland/xwayland.h +++ b/include/wlr/xwayland/xwayland.h @@ -299,10 +299,10 @@ void wlr_xwayland_surface_ping(struct wlr_xwayland_surface *surface); * Returns: true if the window should receive focus * false if it should be ignored */ -bool wlr_xwayland_or_surface_wants_focus( +bool wlr_xwayland_surface_override_redirect_wants_focus( const struct wlr_xwayland_surface *xsurface); -enum wlr_xwayland_icccm_input_model wlr_xwayland_icccm_input_model( +enum wlr_xwayland_icccm_input_model wlr_xwayland_surface_icccm_input_model( const struct wlr_xwayland_surface *xsurface); /** diff --git a/xwayland/xwm.c b/xwayland/xwm.c index c65851bb8..b22ed9a7c 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -2322,7 +2322,7 @@ void wlr_xwayland_surface_ping(struct wlr_xwayland_surface *surface) { surface->pinging = true; } -bool wlr_xwayland_or_surface_wants_focus( +bool wlr_xwayland_surface_override_redirect_wants_focus( const struct wlr_xwayland_surface *xsurface) { static const enum atom_name needles[] = { NET_WM_WINDOW_TYPE_COMBO, @@ -2347,8 +2347,8 @@ bool wlr_xwayland_or_surface_wants_focus( return true; } -enum wlr_xwayland_icccm_input_model wlr_xwayland_icccm_input_model( - const struct wlr_xwayland_surface *xsurface) { +enum wlr_xwayland_icccm_input_model wlr_xwayland_surface_icccm_input_model( + const struct wlr_xwayland_surface *xsurface) { bool take_focus = xwm_atoms_contains(xsurface->xwm, xsurface->protocols, xsurface->protocols_len, WM_TAKE_FOCUS); From e17916d413910e0e565381e97aeb1f292b7c4c5d Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Sat, 25 Sep 2021 12:50:11 +0300 Subject: [PATCH 004/519] Rename wlr_surface_get_extends() to wlr_surface_get_extents() Extend (verb): cause to cover a wider area; make larger. --- include/wlr/types/wlr_compositor.h | 2 +- types/wlr_compositor.c | 2 +- types/xdg_shell/wlr_xdg_surface.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/wlr/types/wlr_compositor.h b/include/wlr/types/wlr_compositor.h index fa9e354b9..174869f44 100644 --- a/include/wlr/types/wlr_compositor.h +++ b/include/wlr/types/wlr_compositor.h @@ -381,7 +381,7 @@ void wlr_surface_send_frame_done(struct wlr_surface *surface, * surface coordinates. * X and y may be negative, if there are subsurfaces with negative position. */ -void wlr_surface_get_extends(struct wlr_surface *surface, struct wlr_box *box); +void wlr_surface_get_extents(struct wlr_surface *surface, struct wlr_box *box); /** * Get the struct wlr_surface corresponding to a wl_surface resource. diff --git a/types/wlr_compositor.c b/types/wlr_compositor.c index c5044ce44..9aee912e0 100644 --- a/types/wlr_compositor.c +++ b/types/wlr_compositor.c @@ -1175,7 +1175,7 @@ static void handle_bounding_box_surface(struct wlr_surface *surface, acc->max_y = max(y + surface->current.height, acc->max_y); } -void wlr_surface_get_extends(struct wlr_surface *surface, struct wlr_box *box) { +void wlr_surface_get_extents(struct wlr_surface *surface, struct wlr_box *box) { struct bound_acc acc = { .min_x = 0, .min_y = 0, diff --git a/types/xdg_shell/wlr_xdg_surface.c b/types/xdg_shell/wlr_xdg_surface.c index 029ab81a6..9842a563c 100644 --- a/types/xdg_shell/wlr_xdg_surface.c +++ b/types/xdg_shell/wlr_xdg_surface.c @@ -614,7 +614,7 @@ void wlr_xdg_surface_for_each_popup_surface(struct wlr_xdg_surface *surface, void wlr_xdg_surface_get_geometry(struct wlr_xdg_surface *surface, struct wlr_box *box) { - wlr_surface_get_extends(surface->surface, box); + wlr_surface_get_extents(surface->surface, box); /* The client never set the geometry */ if (wlr_box_empty(&surface->current.geometry)) { From 22adc65586a6ced166df8003c5794ec342366e2a Mon Sep 17 00:00:00 2001 From: Bill Li Date: Mon, 15 Jul 2024 05:24:31 +0800 Subject: [PATCH 005/519] ci: use package x11-servers/xwayland instead of x11-servers/xwayland-devel --- .builds/freebsd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index 193f59a33..7e2abff6c 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -20,7 +20,7 @@ packages: - x11/xcb-util-errors - x11/xcb-util-renderutil - x11/xcb-util-wm - - x11-servers/xwayland-devel + - x11-servers/xwayland - sysutils/libdisplay-info - sysutils/seatd - gmake From 179ed7c2962708c6b53070323e985514457b5391 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 26 Jun 2024 08:19:26 +0200 Subject: [PATCH 006/519] build: use fs.relative_to() instead of hand-rolled logic Meson has introduced a relative_to() function [1] in its fs module since version 1.3. [1]: https://mesonbuild.com/Fs-module.html#relative_to --- meson.build | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/meson.build b/meson.build index 59df19b2b..956936d76 100644 --- a/meson.build +++ b/meson.build @@ -3,7 +3,7 @@ project( 'c', version: '0.19.0-dev', license: 'MIT', - meson_version: '>=0.59.0', + meson_version: '>=1.3', default_options: [ 'c_std=' + (meson.version().version_compare('>=1.3.0') ? 'c23,c11' : 'c11'), 'warning_level=2', @@ -48,31 +48,10 @@ add_project_arguments(cc.get_supported_arguments([ '-Wno-unused-parameter', ]), language: 'c') -# Compute the relative path used by compiler invocations. -source_root = meson.current_source_dir().split('/') -build_root = meson.global_build_root().split('/') -relative_dir_parts = [] -i = 0 -in_prefix = true -foreach p : build_root - if i >= source_root.length() or not in_prefix or p != source_root[i] - in_prefix = false - relative_dir_parts += '..' - endif - i += 1 -endforeach -i = 0 -in_prefix = true -foreach p : source_root - if i >= build_root.length() or not in_prefix or build_root[i] != p - in_prefix = false - relative_dir_parts += p - endif - i += 1 -endforeach -relative_dir = join_paths(relative_dir_parts) + '/' +fs = import('fs') # Strip relative path prefixes from the code if possible, otherwise hide them. +relative_dir = fs.relative_to(meson.current_source_dir(), meson.global_build_root()) + '/' if cc.has_argument('-fmacro-prefix-map=/prefix/to/hide=') add_project_arguments( '-fmacro-prefix-map=@0@='.format(relative_dir), From 2a8a23c467868a615b660d127801f3de60d1824b Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sat, 13 Jul 2024 23:39:17 +0200 Subject: [PATCH 007/519] wlr_output: remove dead function --- include/wlr/types/wlr_output.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index ff675bd34..2fb987802 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -369,12 +369,6 @@ void wlr_output_lock_attach_render(struct wlr_output *output, bool lock); * a lock. */ void wlr_output_lock_software_cursors(struct wlr_output *output, bool lock); -/** - * Renders software cursors. This is a utility function that can be called when - * compositors render. - */ -void wlr_output_render_software_cursors(struct wlr_output *output, - const pixman_region32_t *damage); /** * Render software cursors. * From 7550e483ae04e1f1d601d90b5a624036554c1533 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Mon, 15 Jul 2024 14:10:15 +0200 Subject: [PATCH 008/519] docs: update comments for wlr_output API changes The old wlr_output_{commit,test}() functions are still mentioned in multiple places. --- examples/output-layers.c | 2 +- include/wlr/types/wlr_output.h | 7 ++++--- include/wlr/types/wlr_output_layer.h | 20 ++++++++++---------- include/wlr/types/wlr_presentation_time.h | 2 +- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/examples/output-layers.c b/examples/output-layers.c index 5089c6aa5..1863d16e0 100644 --- a/examples/output-layers.c +++ b/examples/output-layers.c @@ -87,7 +87,7 @@ static void output_handle_frame(struct wl_listener *listener, void *data) { layers_arr.size / sizeof(struct wlr_output_layer_state)); if (!wlr_output_test_state(output->wlr_output, &output_state)) { - wlr_log(WLR_ERROR, "wlr_output_test() failed"); + wlr_log(WLR_ERROR, "wlr_output_test_state() failed"); return; } diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 2fb987802..ead470396 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -121,8 +121,9 @@ struct wlr_render_pass; * The `frame` event will be emitted when it is a good time for the compositor * to submit a new frame. * - * To render a new frame, compositors should call wlr_output_begin_render_pass(), - * perform rendering on that render pass and finally call wlr_output_commit(). + * To render a new frame compositors should call wlr_output_begin_render_pass(), + * perform rendering on that render pass, and finally call + * wlr_output_commit_state(). */ struct wlr_output { const struct wlr_output_impl *impl; @@ -280,7 +281,7 @@ void wlr_output_destroy_global(struct wlr_output *output); * the allocator and renderer to different values. * * Call this function prior to any call to wlr_output_begin_render_pass(), - * wlr_output_commit() or wlr_output_cursor_create(). + * wlr_output_commit_state() or wlr_output_cursor_create(). * * The buffer capabilities of the provided must match the capabilities of the * output's backend. Returns false otherwise. diff --git a/include/wlr/types/wlr_output_layer.h b/include/wlr/types/wlr_output_layer.h index af843d07a..461e13ddc 100644 --- a/include/wlr/types/wlr_output_layer.h +++ b/include/wlr/types/wlr_output_layer.h @@ -23,16 +23,16 @@ * * To configure output layers, callers should call wlr_output_layer_create() to * create layers, attach struct wlr_output_layer_state onto - * struct wlr_output_state via wlr_output_set_layers() to describe their new - * state, and commit the output via wlr_output_commit(). + * struct wlr_output_state via wlr_output_state_set_layers() to describe their new + * state, and commit the output via wlr_output_commit_state(). * * Backends may have arbitrary limitations when it comes to displaying output * layers. Backends indicate whether or not a layer can be displayed via - * wlr_output_layer_state.accepted after wlr_output_test() or - * wlr_output_commit() is called. Compositors using the output layers API - * directly are expected to setup layers, call wlr_output_test(), paint the - * layers that the backend rejected with the renderer, then call - * wlr_output_commit(). + * wlr_output_layer_state.accepted after wlr_output_test_state() or + * wlr_output_commit_state() is called. Compositors using the output layers API + * directly are expected to setup layers, call wlr_output_test_state(), paint + * the layers that the backend rejected with the renderer, then call + * wlr_output_commit_state(). * * Callers are responsible for disabling output layers when they need the full * output contents to be composited onto a single buffer, e.g. during screen @@ -72,9 +72,9 @@ struct wlr_output_layer_state { // to damage the whole buffer. const pixman_region32_t *damage; - // Populated by the backend after wlr_output_test() and wlr_output_commit(), - // indicates whether the backend has acknowledged and will take care of - // displaying the layer + // Populated by the backend after wlr_output_test_state() and + // wlr_output_commit_state(), indicates whether the backend has acknowledged + // and will take care of displaying the layer bool accepted; }; diff --git a/include/wlr/types/wlr_presentation_time.h b/include/wlr/types/wlr_presentation_time.h index b772295e7..bedd77a6c 100644 --- a/include/wlr/types/wlr_presentation_time.h +++ b/include/wlr/types/wlr_presentation_time.h @@ -89,7 +89,7 @@ void wlr_presentation_event_from_output(struct wlr_presentation_event *event, * * Instead of calling wlr_presentation_surface_sampled() and managing the * struct wlr_presentation_feedback itself, the compositor can call this function - * before a wlr_output_commit() call to indicate that the surface's current + * before a wlr_output_commit_state() call to indicate that the surface's current * contents have been copied to a buffer which will be displayed on the output. */ void wlr_presentation_surface_textured_on_output(struct wlr_surface *surface, From 015bb8512ee314e1deb858cf7350b0220fc58702 Mon Sep 17 00:00:00 2001 From: chenyongxing Date: Tue, 16 Jul 2024 14:24:46 +0800 Subject: [PATCH 009/519] render/vulkan: Fix draw rect clip region invalid in blend none mod --- render/vulkan/pass.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index c879b71c8..3697dcda6 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -572,16 +572,10 @@ static void render_pass_add_rect(struct wlr_render_pass *wlr_pass, }, }; VkClearRect clear_rect = { - .rect = { - .offset = { box.x, box.y }, - .extent = { box.width, box.height }, - }, .layerCount = 1, }; for (int i = 0; i < clip_rects_len; i++) { - VkRect2D rect; - convert_pixman_box_to_vk_rect(&clip_rects[i], &rect); - vkCmdSetScissor(cb, 0, 1, &rect); + convert_pixman_box_to_vk_rect(&clip_rects[i], &clear_rect.rect); vkCmdClearAttachments(cb, 1, &clear_att, 1, &clear_rect); } break; From f16a3c11806d0a52c99b7c843334b89330b84c73 Mon Sep 17 00:00:00 2001 From: BiRD <1094438-BiRD04@users.noreply.gitlab.freedesktop.org> Date: Wed, 10 Jul 2024 11:54:27 -0400 Subject: [PATCH 010/519] tinywl: Edit Makefile for bmake compatibility - Replace 'shell' calls with '!=' assignments - Add default target 'all' - Don't use var '$<' for non-sources --- tinywl/Makefile | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/tinywl/Makefile b/tinywl/Makefile index 64e7af473..a5cedfcc8 100644 --- a/tinywl/Makefile +++ b/tinywl/Makefile @@ -1,10 +1,13 @@ PKG_CONFIG?=pkg-config -WAYLAND_PROTOCOLS=$(shell $(PKG_CONFIG) --variable=pkgdatadir wayland-protocols) -WAYLAND_SCANNER=$(shell $(PKG_CONFIG) --variable=wayland_scanner wayland-scanner) +WAYLAND_PROTOCOLS!=$(PKG_CONFIG) --variable=pkgdatadir wayland-protocols +WAYLAND_SCANNER!=$(PKG_CONFIG) --variable=wayland_scanner wayland-scanner PKGS="wlroots-0.19" wayland-server xkbcommon -CFLAGS+=$(shell $(PKG_CONFIG) --cflags $(PKGS)) -LIBS=$(shell $(PKG_CONFIG) --libs $(PKGS)) +CFLAGS_PKG_CONFIG!=$(PKG_CONFIG) --cflags $(PKGS) +CFLAGS+=$(CFLAGS_PKG_CONFIG) +LIBS!=$(PKG_CONFIG) --libs $(PKGS) + +all: tinywl # wayland-scanner is a tool which generates C headers and rigging for Wayland # protocols, which are specified in XML. wlroots requires you to rig these up @@ -14,12 +17,11 @@ xdg-shell-protocol.h: $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@ tinywl.o: tinywl.c xdg-shell-protocol.h - $(CC) -g -Werror $(CFLAGS) -I. -DWLR_USE_UNSTABLE -o $@ -c $< + $(CC) -c $< -g -Werror $(CFLAGS) -I. -DWLR_USE_UNSTABLE -o $@ tinywl: tinywl.o - $(CC) $< -g -Werror $(CFLAGS) $(LDFLAGS) $(LIBS) -o $@ + $(CC) $^ $> -g -Werror $(CFLAGS) $(LDFLAGS) $(LIBS) -o $@ clean: rm -f tinywl tinywl.o xdg-shell-protocol.h -.DEFAULT_GOAL=tinywl -.PHONY: clean +.PHONY: all clean From cd2cf1bafbea0eaf2afa62b5ded4008b0eb21039 Mon Sep 17 00:00:00 2001 From: BiRD <1094438-BiRD04@users.noreply.gitlab.freedesktop.org> Date: Tue, 16 Jul 2024 11:24:32 -0400 Subject: [PATCH 011/519] ci: Remove package 'gmake' --- .builds/freebsd.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index 7e2abff6c..83cea7495 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -23,7 +23,6 @@ packages: - x11-servers/xwayland - sysutils/libdisplay-info - sysutils/seatd - - gmake - hwdata sources: - https://gitlab.freedesktop.org/wlroots/wlroots.git @@ -35,4 +34,4 @@ tasks: sudo ninja -C build install - tinywl: | cd wlroots/tinywl - gmake + make From 42673a282137ad4dc39b6a70c011dba4d822b85c Mon Sep 17 00:00:00 2001 From: BiRD <1094438-BiRD04@users.noreply.gitlab.freedesktop.org> Date: Fri, 26 Jul 2024 15:11:31 -0400 Subject: [PATCH 012/519] tinywl: Update .gitignore --- tinywl/.gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tinywl/.gitignore b/tinywl/.gitignore index 2b5bb6f84..5de73fac9 100644 --- a/tinywl/.gitignore +++ b/tinywl/.gitignore @@ -1,3 +1,3 @@ tinywl -*-protocol.c +tinywl.o *-protocol.h From de574ac0988758ff0632a315bf11ddeb485355a9 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Thu, 1 Aug 2024 15:24:20 +0300 Subject: [PATCH 013/519] output-power-management: send zwlr_output_power_v1.failed on output destroy From the event description: This event indicates that the output power management mode control is no longer valid. This can happen for a number of reasons, including: <...> - The output disappeared --- types/wlr_output_power_management_v1.c | 1 + 1 file changed, 1 insertion(+) diff --git a/types/wlr_output_power_management_v1.c b/types/wlr_output_power_management_v1.c index 763d0d6af..96e0b6028 100644 --- a/types/wlr_output_power_management_v1.c +++ b/types/wlr_output_power_management_v1.c @@ -46,6 +46,7 @@ static void output_power_handle_output_destroy(struct wl_listener *listener, void *data) { struct wlr_output_power_v1 *output_power = wl_container_of(listener, output_power, output_destroy_listener); + zwlr_output_power_v1_send_failed(output_power->resource); output_power_destroy(output_power); } From aa1163e64021762ba70d2e861c3724895778cdaf Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Wed, 14 Feb 2024 05:45:43 -0500 Subject: [PATCH 014/519] xwayland: factor out xwm_set_focused_window() Currently _NET_WM_STATE is updated in xwm_focus_window() but _NET_ACTIVE_WINDOW is updated in xwm_surface_activate(). In some cases (for example, client-initiated focus changes) the two properties can get out of sync. Factor out a new function which updates both properties in sync. Adjust the logic in xwm_handle_focus_in() to call either xwm_focus_window() or xwm_set_focused_window(), or neither, as appropriate. --- xwayland/xwm.c | 66 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/xwayland/xwm.c b/xwayland/xwm.c index b22ed9a7c..3300d27ca 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -353,18 +353,13 @@ static void xwm_set_net_client_list_stacking(struct wlr_xwm *xwm) { static void xsurface_set_net_wm_state(struct wlr_xwayland_surface *xsurface); -static void xwm_set_focus_window(struct wlr_xwm *xwm, +// Gives input (keyboard) focus to a window. +// Normally followed by xwm_set_focused_window(). +static void xwm_focus_window(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface) { - struct wlr_xwayland_surface *unfocus_surface = xwm->focus_surface; // We handle cases where focus_surface == xsurface because we // want to be able to deny FocusIn events. - xwm->focus_surface = xsurface; - - if (unfocus_surface) { - xsurface_set_net_wm_state(unfocus_surface); - } - if (!xsurface) { xcb_set_input_focus_checked(xwm->xcb_conn, XCB_INPUT_FOCUS_POINTER_ROOT, @@ -391,25 +386,43 @@ static void xwm_set_focus_window(struct wlr_xwm *xwm, XCB_INPUT_FOCUS_POINTER_ROOT, xsurface->window_id, XCB_CURRENT_TIME); xwm->last_focus_seq = cookie.sequence; } - - xsurface_set_net_wm_state(xsurface); } -static void xwm_surface_activate(struct wlr_xwm *xwm, +// Updates _NET_ACTIVE_WINDOW and _NET_WM_STATE when focus changes. +static void xwm_set_focused_window(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface) { + struct wlr_xwayland_surface *unfocus_surface = xwm->focus_surface; + if (xwm->focus_surface == xsurface || (xsurface && xsurface->override_redirect)) { return; } + xwm->focus_surface = xsurface; + + if (unfocus_surface) { + xsurface_set_net_wm_state(unfocus_surface); + } + if (xsurface) { + xsurface_set_net_wm_state(xsurface); xwm_set_net_active_window(xwm, xsurface->window_id); } else { xwm_set_net_active_window(xwm, XCB_WINDOW_NONE); } +} - xwm_set_focus_window(xwm, xsurface); +static void xwm_surface_activate(struct wlr_xwm *xwm, + struct wlr_xwayland_surface *xsurface) { + if (xsurface && xsurface->override_redirect) { + return; + } + if (xsurface != xwm->focus_surface) { + xwm_focus_window(xwm, xsurface); + } + + xwm_set_focused_window(xwm, xsurface); xcb_flush(xwm->xcb_conn); } @@ -1588,22 +1601,23 @@ static void xwm_handle_focus_in(struct wlr_xwm *xwm, return; } - // Do not let X clients change the focus behind the compositor's - // back. Reset the focus to the old one if it changed. - // - // Note: Some applications rely on being able to change focus, for ex. Steam: - // https://github.com/swaywm/sway/issues/1865 - // Because of that, we allow changing focus between surfaces belonging to the - // same application. We must be careful to ignore requests that are too old - // though, because otherwise it may lead to race conditions: + // Ignore any out-of-date FocusIn event (older than the last + // known WM-initiated focus change) to avoid race conditions. // https://github.com/swaywm/wlroots/issues/2324 - struct wlr_xwayland_surface *requested_focus = lookup_surface(xwm, ev->event); - if (xwm->focus_surface && requested_focus && - requested_focus->pid == xwm->focus_surface->pid && - validate_focus_serial(xwm->last_focus_seq, ev->sequence)) { - xwm_set_focus_window(xwm, requested_focus); + if (!validate_focus_serial(xwm->last_focus_seq, ev->sequence)) { + return; + } + + // Allow focus changes between surfaces belonging to the same + // application. Steam for example relies on this: + // https://github.com/swaywm/sway/issues/1865 + struct wlr_xwayland_surface *xsurface = lookup_surface(xwm, ev->event); + if (xsurface && xwm->focus_surface && xsurface->pid == xwm->focus_surface->pid) { + xwm_set_focused_window(xwm, xsurface); } else { - xwm_set_focus_window(xwm, xwm->focus_surface); + // Try to prevent clients from changing focus between + // applications, by refocusing the previous surface. + xwm_focus_window(xwm, xwm->focus_surface); } } From eb5312022a527586ea379b36e3fb930522bed3b5 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Wed, 14 Feb 2024 05:53:46 -0500 Subject: [PATCH 015/519] xwayland: add focus_in and grab_focus events Allows the compositor to know when the XWayland focus changes. --- include/wlr/xwayland/xwayland.h | 2 ++ xwayland/xwm.c | 24 ++++++++++++++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/include/wlr/xwayland/xwayland.h b/include/wlr/xwayland/xwayland.h index 837a57e02..bf778c4cc 100644 --- a/include/wlr/xwayland/xwayland.h +++ b/include/wlr/xwayland/xwayland.h @@ -186,6 +186,8 @@ struct wlr_xwayland_surface { struct wl_signal set_strut_partial; struct wl_signal set_override_redirect; struct wl_signal set_geometry; + struct wl_signal focus_in; + struct wl_signal grab_focus; /* can be used to set initial maximized/fullscreen geometry */ struct wl_signal map_request; struct wl_signal ping_timeout; diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 3300d27ca..4480aeb36 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -219,6 +219,8 @@ static struct wlr_xwayland_surface *xwayland_surface_create( wl_signal_init(&surface->events.set_strut_partial); wl_signal_init(&surface->events.set_override_redirect); wl_signal_init(&surface->events.set_geometry); + wl_signal_init(&surface->events.focus_in); + wl_signal_init(&surface->events.grab_focus); wl_signal_init(&surface->events.map_request); wl_signal_init(&surface->events.ping_timeout); @@ -1591,16 +1593,26 @@ static bool validate_focus_serial(uint16_t last_focus_seq, uint16_t event_seq) { static void xwm_handle_focus_in(struct wlr_xwm *xwm, xcb_focus_in_event_t *ev) { - // Do not interfere with grabs - if (ev->mode == XCB_NOTIFY_MODE_GRAB || - ev->mode == XCB_NOTIFY_MODE_UNGRAB) { - return; - } // Ignore pointer focus change events if (ev->detail == XCB_NOTIFY_DETAIL_POINTER) { return; } + // Do not interfere with keyboard grabs, but notify the + // compositor. Note that many legitimate X11 applications use + // keyboard grabs to "steal" focus for e.g. popup menus. + struct wlr_xwayland_surface *xsurface = lookup_surface(xwm, ev->event); + if (ev->mode == XCB_NOTIFY_MODE_GRAB) { + if (xsurface) { + wl_signal_emit_mutable(&xsurface->events.grab_focus, NULL); + } + return; + } + if (ev->mode == XCB_NOTIFY_MODE_UNGRAB) { + /* Do we need an ungrab_focus event? */ + return; + } + // Ignore any out-of-date FocusIn event (older than the last // known WM-initiated focus change) to avoid race conditions. // https://github.com/swaywm/wlroots/issues/2324 @@ -1611,9 +1623,9 @@ static void xwm_handle_focus_in(struct wlr_xwm *xwm, // Allow focus changes between surfaces belonging to the same // application. Steam for example relies on this: // https://github.com/swaywm/sway/issues/1865 - struct wlr_xwayland_surface *xsurface = lookup_surface(xwm, ev->event); if (xsurface && xwm->focus_surface && xsurface->pid == xwm->focus_surface->pid) { xwm_set_focused_window(xwm, xsurface); + wl_signal_emit_mutable(&xsurface->events.focus_in, NULL); } else { // Try to prevent clients from changing focus between // applications, by refocusing the previous surface. From 5083efe18bb0c1e420b40da85b20c8979c49cd89 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Wed, 14 Feb 2024 06:05:04 -0500 Subject: [PATCH 016/519] xwayland: add wlr_xwayland_surface_offer_focus() In labwc, we have had trouble with XWayland windows using the Globally Active input model (see wlr_xwayland_icccm_input_model()). Under traditional X11, these windows do not expect to be given focus directly by the window manager; rather, the WM sends them a WM_TAKE_FOCUS message prompting the client to take focus voluntarily. Currently, these clients are difficult to support with wlroots, because wlr_xwayland_surface_activate() assumes the client window will always accept the keyboard focus after being sent WM_TAKE_FOCUS. Some Globally Active client windows (e.g. panels/toolbars) don't want to be focused. It's useless at best to focus them, and might even make them misbehave. Others do need keyboard focus to be functional -- and there doesn't seem to be any reliable way to know this in advance. Adding wlr_xwayland_surface_offer_focus() allows the compositor to send WM_TAKE_FOCUS to a client window supporting it and then see whether the client accepts or ignores the offer. If it accepts, the surface will emit the focus_in signal notifying the compositor that it has received focus. This is entirely opt-in. A compositor that doesn't want to use the new function can continue to call wlr_xwayland_surface_activate() directly just as before. --- include/wlr/xwayland/xwayland.h | 12 +++++++++++ include/xwayland/xwm.h | 1 + xwayland/xwm.c | 38 +++++++++++++++++++++++++++++---- 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/include/wlr/xwayland/xwayland.h b/include/wlr/xwayland/xwayland.h index bf778c4cc..c02f45e3e 100644 --- a/include/wlr/xwayland/xwayland.h +++ b/include/wlr/xwayland/xwayland.h @@ -278,6 +278,18 @@ void wlr_xwayland_set_seat(struct wlr_xwayland *xwayland, struct wlr_xwayland_surface *wlr_xwayland_surface_try_from_wlr_surface( struct wlr_surface *surface); +/** + * Offer focus by sending WM_TAKE_FOCUS to a client window supporting it. + * The client may accept or ignore the offer. If it accepts, the surface will + * emit the focus_in signal notifying the compositor that it has received focus. + * + * This is a more compatible method of giving focus to windows using the + * Globally Active input model (see wlr_xwayland_icccm_input_model()) than + * calling wlr_xwayland_surface_activate() unconditionally, since there is no + * reliable way to know in advance whether these windows want to be focused. + */ +void wlr_xwayland_surface_offer_focus(struct wlr_xwayland_surface *xsurface); + void wlr_xwayland_surface_ping(struct wlr_xwayland_surface *surface); /** Metric to guess if an OR window should "receive" focus diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h index 6259dd9bb..762fd4428 100644 --- a/include/xwayland/xwm.h +++ b/include/xwayland/xwm.h @@ -113,6 +113,7 @@ struct wlr_xwm { struct wlr_xwm_selection dnd_selection; struct wlr_xwayland_surface *focus_surface; + struct wlr_xwayland_surface *offered_focus; // Surfaces in creation order struct wl_list surfaces; // wlr_xwayland_surface.link diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 4480aeb36..2ec053e4f 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -395,12 +395,17 @@ static void xwm_set_focused_window(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface) { struct wlr_xwayland_surface *unfocus_surface = xwm->focus_surface; - if (xwm->focus_surface == xsurface || - (xsurface && xsurface->override_redirect)) { + if (xsurface && xsurface->override_redirect) { return; } xwm->focus_surface = xsurface; + // cancel any pending focus offer + xwm->offered_focus = xsurface; + + if (xsurface == unfocus_surface) { + return; + } if (unfocus_surface) { xsurface_set_net_wm_state(unfocus_surface); @@ -414,13 +419,34 @@ static void xwm_set_focused_window(struct wlr_xwm *xwm, } } +void wlr_xwayland_surface_offer_focus(struct wlr_xwayland_surface *xsurface) { + if (!xsurface || xsurface->override_redirect) { + return; + } + + struct wlr_xwm *xwm = xsurface->xwm; + if (!xwm_atoms_contains(xwm, xsurface->protocols, + xsurface->protocols_len, WM_TAKE_FOCUS)) { + return; + } + + xwm->offered_focus = xsurface; + + xcb_client_message_data_t message_data = { 0 }; + message_data.data32[0] = xwm->atoms[WM_TAKE_FOCUS]; + message_data.data32[1] = XCB_TIME_CURRENT_TIME; + xwm_send_wm_message(xsurface, &message_data, XCB_EVENT_MASK_NO_EVENT); + + xcb_flush(xwm->xcb_conn); +} + static void xwm_surface_activate(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface) { if (xsurface && xsurface->override_redirect) { return; } - if (xsurface != xwm->focus_surface) { + if (xsurface != xwm->focus_surface && xsurface != xwm->offered_focus) { xwm_focus_window(xwm, xsurface); } @@ -503,6 +529,9 @@ static void xwayland_surface_destroy(struct wlr_xwayland_surface *xsurface) { if (xsurface == xsurface->xwm->focus_surface) { xwm_surface_activate(xsurface->xwm, NULL); } + if (xsurface == xsurface->xwm->offered_focus) { + xsurface->xwm->offered_focus = NULL; + } wl_list_remove(&xsurface->link); wl_list_remove(&xsurface->parent_link); @@ -1623,7 +1652,8 @@ static void xwm_handle_focus_in(struct wlr_xwm *xwm, // Allow focus changes between surfaces belonging to the same // application. Steam for example relies on this: // https://github.com/swaywm/sway/issues/1865 - if (xsurface && xwm->focus_surface && xsurface->pid == xwm->focus_surface->pid) { + if (xsurface && ((xwm->focus_surface && xsurface->pid == xwm->focus_surface->pid) || + (xwm->offered_focus && xsurface->pid == xwm->offered_focus->pid))) { xwm_set_focused_window(xwm, xsurface); wl_signal_emit_mutable(&xsurface->events.focus_in, NULL); } else { From f9199bb6d4a31f8647bdd20bfbd5b7cf38bd9f53 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Wed, 14 Feb 2024 06:09:45 -0500 Subject: [PATCH 017/519] xwayland: set focus to XCB_POINTER_ROOT rather than XCB_NONE Fixes an issue with keyboard grabs not working when no XWayland windows are focused. --- xwayland/xwm.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 2ec053e4f..67b00829e 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -363,9 +363,13 @@ static void xwm_focus_window(struct wlr_xwm *xwm, // We handle cases where focus_surface == xsurface because we // want to be able to deny FocusIn events. if (!xsurface) { + // XCB_POINTER_ROOT is described in xcb documentation but isn't + // actually defined in the headers. It's distinct from XCB_NONE + // (which disables keyboard input entirely and causes issues + // with keyboard grabs for e.g. popups). xcb_set_input_focus_checked(xwm->xcb_conn, XCB_INPUT_FOCUS_POINTER_ROOT, - XCB_NONE, XCB_CURRENT_TIME); + 1L /*XCB_POINTER_ROOT*/, XCB_CURRENT_TIME); return; } From ceb4fcedca30d323a05836b0872bfe773a047ccc Mon Sep 17 00:00:00 2001 From: Consolatis <40171-Consolatis@users.noreply.gitlab.freedesktop.org> Date: Sat, 13 Apr 2024 14:48:59 +0200 Subject: [PATCH 018/519] xwm: expose individual axis for _set_maximized() This allows compositors to support both axis individually. To keep existing behavior, compositors can supply the same maximized state for both axis. --- include/wlr/xwayland/xwayland.h | 2 +- xwayland/xwm.c | 18 ++++++++---------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/include/wlr/xwayland/xwayland.h b/include/wlr/xwayland/xwayland.h index c02f45e3e..d656036b1 100644 --- a/include/wlr/xwayland/xwayland.h +++ b/include/wlr/xwayland/xwayland.h @@ -261,7 +261,7 @@ void wlr_xwayland_surface_set_minimized(struct wlr_xwayland_surface *surface, bool minimized); void wlr_xwayland_surface_set_maximized(struct wlr_xwayland_surface *surface, - bool maximized); + bool maximized_horz, bool maximized_vert); void wlr_xwayland_surface_set_fullscreen(struct wlr_xwayland_surface *surface, bool fullscreen); diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 67b00829e..a6851da83 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -1392,11 +1392,6 @@ static bool update_state(int action, bool *state) { return changed; } -static bool xsurface_is_maximized( - struct wlr_xwayland_surface *xsurface) { - return xsurface->maximized_horz && xsurface->maximized_vert; -} - static void xwm_handle_net_wm_state_message(struct wlr_xwm *xwm, xcb_client_message_event_t *client_message) { struct wlr_xwayland_surface *xsurface = @@ -1409,7 +1404,8 @@ static void xwm_handle_net_wm_state_message(struct wlr_xwm *xwm, } bool fullscreen = xsurface->fullscreen; - bool maximized = xsurface_is_maximized(xsurface); + bool maximized_vert = xsurface->maximized_vert; + bool maximized_horz = xsurface->maximized_horz; bool minimized = xsurface->minimized; uint32_t action = client_message->data.data32[0]; @@ -1445,7 +1441,9 @@ static void xwm_handle_net_wm_state_message(struct wlr_xwm *xwm, wl_signal_emit_mutable(&xsurface->events.request_fullscreen, NULL); } - if (maximized != xsurface_is_maximized(xsurface)) { + if (maximized_vert != xsurface->maximized_vert + || maximized_horz != xsurface->maximized_horz) { + wl_signal_emit_mutable(&xsurface->events.request_maximize, NULL); } @@ -2342,9 +2340,9 @@ void wlr_xwayland_surface_set_minimized(struct wlr_xwayland_surface *surface, } void wlr_xwayland_surface_set_maximized(struct wlr_xwayland_surface *surface, - bool maximized) { - surface->maximized_horz = maximized; - surface->maximized_vert = maximized; + bool maximized_horz, bool maximized_vert) { + surface->maximized_horz = maximized_horz; + surface->maximized_vert = maximized_vert; xsurface_set_net_wm_state(surface); xcb_flush(surface->xwm->xcb_conn); } From 7e13dfdd4dd3e2a1c2c671ba9709c77a9eb045b2 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Fri, 2 Aug 2024 17:52:19 +0300 Subject: [PATCH 019/519] xwayland: remove stray empty line --- xwayland/xwm.c | 1 - 1 file changed, 1 deletion(-) diff --git a/xwayland/xwm.c b/xwayland/xwm.c index a6851da83..1d0d77b45 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -1443,7 +1443,6 @@ static void xwm_handle_net_wm_state_message(struct wlr_xwm *xwm, if (maximized_vert != xsurface->maximized_vert || maximized_horz != xsurface->maximized_horz) { - wl_signal_emit_mutable(&xsurface->events.request_maximize, NULL); } From 42df9414fbf63abee51cb3094cedb94ac0f0f8a3 Mon Sep 17 00:00:00 2001 From: Philipp Kaeser Date: Sun, 4 Aug 2024 11:43:17 +0200 Subject: [PATCH 020/519] wlr_scene: Add documentation to wlr_scene_create about how to destroy the allocated resources. --- include/wlr/types/wlr_scene.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index a8fc21595..a09ae8f1c 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -306,6 +306,9 @@ struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node, /** * Create a new scene-graph. + * + * The graph is also a wlr_scene_node. Associated resources can be destroyed + * through wlr_scene_node_destroy(). */ struct wlr_scene *wlr_scene_create(void); From d400b4587a914c451614feb1ce986aadcb60cc87 Mon Sep 17 00:00:00 2001 From: Philipp Kaeser Date: Sun, 4 Aug 2024 11:57:27 +0200 Subject: [PATCH 021/519] wlr_scene: Add 'struct' to comment in wlr_scene_create() to permit auto-linkify. --- include/wlr/types/wlr_scene.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index a09ae8f1c..d1fbca7de 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -307,8 +307,8 @@ struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node, /** * Create a new scene-graph. * - * The graph is also a wlr_scene_node. Associated resources can be destroyed - * through wlr_scene_node_destroy(). + * The graph is also a struct wlr_scene_node. Associated resources can be + * destroyed through wlr_scene_node_destroy(). */ struct wlr_scene *wlr_scene_create(void); From 6261bd9684b3c62738546e7bec7922706b0df8d8 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Mon, 5 Aug 2024 19:32:24 +0300 Subject: [PATCH 022/519] .mailmap: add myself --- .mailmap | 1 + 1 file changed, 1 insertion(+) diff --git a/.mailmap b/.mailmap index 0e050b333..42dca553e 100644 --- a/.mailmap +++ b/.mailmap @@ -1 +1,2 @@ Isaac Freund +Kirill Primak From 4481c6b243e979ccee156c4e1a6dfbedcddc99c6 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Mon, 5 Aug 2024 14:46:30 -0400 Subject: [PATCH 023/519] wlr_scene: Force blend mode to PREMULTIPLIED if calculate visibility is disabled We do it here so WLR_SCENE_HIGHLIGHT_TRANSPARENT_REGION doesn't break --- types/scene/wlr_scene.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 46768aabe..c38584db9 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1242,7 +1242,8 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren .clip = &render_region, .alpha = &scene_buffer->opacity, .filter_mode = scene_buffer->filter_mode, - .blend_mode = pixman_region32_not_empty(&opaque) ? + .blend_mode = !data->output->scene->calculate_visibility || + pixman_region32_not_empty(&opaque) ? WLR_RENDER_BLEND_MODE_PREMULTIPLIED : WLR_RENDER_BLEND_MODE_NONE, }); From a1635fdb76cdcc6e66c9394962a9ecf746dc37e2 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 10 Jul 2023 10:53:12 +0200 Subject: [PATCH 024/519] render: add explicit sync API --- include/wlr/render/pass.h | 22 ++++++++++++++++++++++ include/wlr/render/wlr_renderer.h | 6 ++++++ 2 files changed, 28 insertions(+) diff --git a/include/wlr/render/pass.h b/include/wlr/render/pass.h index 66480f7f7..2159bc3df 100644 --- a/include/wlr/render/pass.h +++ b/include/wlr/render/pass.h @@ -33,6 +33,17 @@ struct wlr_buffer_pass_options { /* Color transform to apply to the output of the render pass, * leave NULL to indicate sRGB/no custom transform */ struct wlr_color_transform *color_transform; + + /* Signal a timeline synchronization point when the render pass completes. + * + * When a compositor provides a signal timeline, the renderer may skip + * implicit signal synchronization. + * + * Support for this feature is advertised by features.timeline in + * struct wlr_renderer. + */ + struct wlr_drm_syncobj_timeline *signal_timeline; + uint64_t signal_point; }; /** @@ -88,6 +99,17 @@ struct wlr_render_texture_options { enum wlr_scale_filter_mode filter_mode; /* Blend mode */ enum wlr_render_blend_mode blend_mode; + + /* Wait for a timeline synchronization point before texturing. + * + * When a compositor provides a wait timeline, the renderer may skip + * implicit wait synchronization. + * + * Support for this feature is advertised by features.timeline in + * struct wlr_renderer. + */ + struct wlr_drm_syncobj_timeline *wait_timeline; + uint64_t wait_point; }; /** diff --git a/include/wlr/render/wlr_renderer.h b/include/wlr/render/wlr_renderer.h index bb9a55fcf..4a8ccd398 100644 --- a/include/wlr/render/wlr_renderer.h +++ b/include/wlr/render/wlr_renderer.h @@ -45,6 +45,12 @@ struct wlr_renderer { * Does the renderer support color transforms on its output? */ bool output_color_transform; + /** + * Whether wait/signal timelines are supported. + * + * See struct wlr_drm_syncobj_timeline. + */ + bool timeline; } features; // private state From 19ffbfe3565f95fff313d240da4d7193795be04d Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 16 Mar 2020 17:24:25 +0100 Subject: [PATCH 025/519] render/egl: add support for explicit sync extensions --- include/render/egl.h | 12 +++++++ render/egl.c | 74 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/include/render/egl.h b/include/render/egl.h index 0765cce74..103ab57df 100644 --- a/include/render/egl.h +++ b/include/render/egl.h @@ -38,6 +38,10 @@ struct wlr_egl { PFNEGLQUERYDISPLAYATTRIBEXTPROC eglQueryDisplayAttribEXT; PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT; PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT; + PFNEGLCREATESYNCKHRPROC eglCreateSyncKHR; + PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR; + PFNEGLDUPNATIVEFENCEFDANDROIDPROC eglDupNativeFenceFDANDROID; + PFNEGLWAITSYNCKHRPROC eglWaitSyncKHR; } procs; bool has_modifiers; @@ -105,4 +109,12 @@ bool wlr_egl_make_current(struct wlr_egl *egl, struct wlr_egl_context *save_cont bool wlr_egl_unset_current(struct wlr_egl *egl); +EGLSyncKHR wlr_egl_create_sync(struct wlr_egl *egl, int fence_fd); + +void wlr_egl_destroy_sync(struct wlr_egl *egl, EGLSyncKHR sync); + +int wlr_egl_dup_fence_fd(struct wlr_egl *egl, EGLSyncKHR sync); + +bool wlr_egl_wait_sync(struct wlr_egl *egl, EGLSyncKHR sync); + #endif diff --git a/render/egl.c b/render/egl.c index 19868ca84..ce0c95d91 100644 --- a/render/egl.c +++ b/render/egl.c @@ -348,6 +348,18 @@ static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display) { return false; } + if (check_egl_ext(display_exts_str, "EGL_KHR_fence_sync") && + check_egl_ext(display_exts_str, "EGL_ANDROID_native_fence_sync")) { + load_egl_proc(&egl->procs.eglCreateSyncKHR, "eglCreateSyncKHR"); + load_egl_proc(&egl->procs.eglDestroySyncKHR, "eglDestroySyncKHR"); + load_egl_proc(&egl->procs.eglDupNativeFenceFDANDROID, + "eglDupNativeFenceFDANDROID"); + } + + if (check_egl_ext(display_exts_str, "EGL_KHR_wait_sync")) { + load_egl_proc(&egl->procs.eglWaitSyncKHR, "eglWaitSyncKHR"); + } + egl->exts.IMG_context_priority = check_egl_ext(display_exts_str, "EGL_IMG_context_priority"); @@ -1014,3 +1026,65 @@ int wlr_egl_dup_drm_fd(struct wlr_egl *egl) { } return fd; } + +EGLSyncKHR wlr_egl_create_sync(struct wlr_egl *egl, int fence_fd) { + if (!egl->procs.eglCreateSyncKHR) { + return EGL_NO_SYNC_KHR; + } + + EGLint attribs[3] = { EGL_NONE }; + int dup_fd = -1; + if (fence_fd >= 0) { + dup_fd = fcntl(fence_fd, F_DUPFD_CLOEXEC, 0); + if (dup_fd < 0) { + wlr_log_errno(WLR_ERROR, "dup failed"); + return EGL_NO_SYNC_KHR; + } + + attribs[0] = EGL_SYNC_NATIVE_FENCE_FD_ANDROID; + attribs[1] = dup_fd; + attribs[2] = EGL_NONE; + } + + EGLSyncKHR sync = egl->procs.eglCreateSyncKHR(egl->display, + EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); + if (sync == EGL_NO_SYNC_KHR) { + wlr_log(WLR_ERROR, "eglCreateSyncKHR failed"); + if (dup_fd >= 0) { + close(dup_fd); + } + } + return sync; +} + +void wlr_egl_destroy_sync(struct wlr_egl *egl, EGLSyncKHR sync) { + if (sync == EGL_NO_SYNC_KHR) { + return; + } + assert(egl->procs.eglDestroySyncKHR); + if (egl->procs.eglDestroySyncKHR(egl->display, sync) != EGL_TRUE) { + wlr_log(WLR_ERROR, "eglDestroySyncKHR failed"); + } +} + +int wlr_egl_dup_fence_fd(struct wlr_egl *egl, EGLSyncKHR sync) { + if (!egl->procs.eglDupNativeFenceFDANDROID) { + return -1; + } + + int fd = egl->procs.eglDupNativeFenceFDANDROID(egl->display, sync); + if (fd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { + wlr_log(WLR_ERROR, "eglDupNativeFenceFDANDROID failed"); + return -1; + } + + return fd; +} + +bool wlr_egl_wait_sync(struct wlr_egl *egl, EGLSyncKHR sync) { + if (egl->procs.eglWaitSyncKHR(egl->display, sync, 0) != EGL_TRUE) { + wlr_log(WLR_ERROR, "eglWaitSyncKHR failed"); + return false; + } + return true; +} From d2374b3e4ed802b8676029631e480d6cf75edb13 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 10 Jul 2023 16:15:59 +0200 Subject: [PATCH 026/519] render/gles2: implement explicit sync API --- include/render/gles2.h | 5 +++- render/gles2/pass.c | 60 ++++++++++++++++++++++++++++++++++++++--- render/gles2/renderer.c | 6 ++++- 3 files changed, 66 insertions(+), 5 deletions(-) diff --git a/include/render/gles2.h b/include/render/gles2.h index a472ee9c6..0f24ae38e 100644 --- a/include/render/gles2.h +++ b/include/render/gles2.h @@ -138,6 +138,8 @@ struct wlr_gles2_render_pass { float projection_matrix[9]; struct wlr_egl_context prev_ctx; struct wlr_gles2_render_timer *timer; + struct wlr_drm_syncobj_timeline *signal_timeline; + uint64_t signal_point; }; bool is_gles2_pixel_format_supported(const struct wlr_gles2_renderer *renderer, @@ -169,6 +171,7 @@ void push_gles2_debug_(struct wlr_gles2_renderer *renderer, void pop_gles2_debug(struct wlr_gles2_renderer *renderer); struct wlr_gles2_render_pass *begin_gles2_buffer_pass(struct wlr_gles2_buffer *buffer, - struct wlr_egl_context *prev_ctx, struct wlr_gles2_render_timer *timer); + struct wlr_egl_context *prev_ctx, struct wlr_gles2_render_timer *timer, + struct wlr_drm_syncobj_timeline *signal_timeline, uint64_t signal_point); #endif diff --git a/render/gles2/pass.c b/render/gles2/pass.c index 9177b0a1f..2942ab587 100644 --- a/render/gles2/pass.c +++ b/render/gles2/pass.c @@ -2,8 +2,11 @@ #include #include #include +#include +#include #include #include +#include "render/egl.h" #include "render/gles2.h" #include "types/wlr_matrix.h" @@ -21,6 +24,7 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { struct wlr_gles2_render_pass *pass = get_render_pass(wlr_pass); struct wlr_gles2_renderer *renderer = pass->buffer->renderer; struct wlr_gles2_render_timer *timer = pass->timer; + bool ok = false; push_gles2_debug(renderer); @@ -36,16 +40,40 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { clock_gettime(CLOCK_MONOTONIC, &timer->cpu_end); } - glFlush(); + if (pass->signal_timeline != NULL) { + EGLSyncKHR sync = wlr_egl_create_sync(renderer->egl, -1); + if (sync == EGL_NO_SYNC_KHR) { + goto out; + } + + int sync_file_fd = wlr_egl_dup_fence_fd(renderer->egl, sync); + wlr_egl_destroy_sync(renderer->egl, sync); + if (sync_file_fd < 0) { + goto out; + } + + bool ok = wlr_drm_syncobj_timeline_import_sync_file(pass->signal_timeline, pass->signal_point, sync_file_fd); + close(sync_file_fd); + if (!ok) { + goto out; + } + } else { + glFlush(); + } + + ok = true; + +out: glBindFramebuffer(GL_FRAMEBUFFER, 0); pop_gles2_debug(renderer); wlr_egl_restore_context(&pass->prev_ctx); + wlr_drm_syncobj_timeline_unref(pass->signal_timeline); wlr_buffer_unlock(pass->buffer->buffer); free(pass); - return true; + return ok; } static void render(const struct wlr_box *box, const pixman_region32_t *clip, GLint attrib) { @@ -175,6 +203,27 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, src_fbox.height /= options->texture->height; push_gles2_debug(renderer); + + if (options->wait_timeline != NULL) { + int sync_file_fd = + wlr_drm_syncobj_timeline_export_sync_file(options->wait_timeline, options->wait_point); + if (sync_file_fd < 0) { + return; + } + + EGLSyncKHR sync = wlr_egl_create_sync(renderer->egl, sync_file_fd); + close(sync_file_fd); + if (sync == EGL_NO_SYNC_KHR) { + return; + } + + bool ok = wlr_egl_wait_sync(renderer->egl, sync); + wlr_egl_destroy_sync(renderer->egl, sync); + if (!ok) { + return; + } + } + setup_blending(!texture->has_alpha && alpha == 1.0 ? WLR_RENDER_BLEND_MODE_NONE : options->blend_mode); @@ -247,7 +296,8 @@ static const char *reset_status_str(GLenum status) { } struct wlr_gles2_render_pass *begin_gles2_buffer_pass(struct wlr_gles2_buffer *buffer, - struct wlr_egl_context *prev_ctx, struct wlr_gles2_render_timer *timer) { + struct wlr_egl_context *prev_ctx, struct wlr_gles2_render_timer *timer, + struct wlr_drm_syncobj_timeline *signal_timeline, uint64_t signal_point) { struct wlr_gles2_renderer *renderer = buffer->renderer; struct wlr_buffer *wlr_buffer = buffer->buffer; @@ -275,6 +325,10 @@ struct wlr_gles2_render_pass *begin_gles2_buffer_pass(struct wlr_gles2_buffer *b pass->buffer = buffer; pass->timer = timer; pass->prev_ctx = *prev_ctx; + if (signal_timeline != NULL) { + pass->signal_timeline = wlr_drm_syncobj_timeline_ref(signal_timeline); + pass->signal_point = signal_point; + } matrix_projection(pass->projection_matrix, wlr_buffer->width, wlr_buffer->height, WL_OUTPUT_TRANSFORM_FLIPPED_180); diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c index 4694b2a8f..3eeb1ba9c 100644 --- a/render/gles2/renderer.c +++ b/render/gles2/renderer.c @@ -259,7 +259,8 @@ static struct wlr_render_pass *gles2_begin_buffer_pass(struct wlr_renderer *wlr_ return NULL; } - struct wlr_gles2_render_pass *pass = begin_gles2_buffer_pass(buffer, &prev_ctx, timer); + struct wlr_gles2_render_pass *pass = begin_gles2_buffer_pass(buffer, + &prev_ctx, timer, options->signal_timeline, options->signal_point); if (!pass) { return NULL; } @@ -683,6 +684,9 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { get_gles2_shm_formats(renderer, &renderer->shm_texture_formats); + renderer->wlr_renderer.features.timeline = + egl->procs.eglDupNativeFenceFDANDROID && egl->procs.eglWaitSyncKHR; + return &renderer->wlr_renderer; error: From 1ad42bea99134ccb5e1938983259eb51ac4fa4db Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 20 Oct 2021 19:29:00 +0200 Subject: [PATCH 027/519] output: add explicit sync API --- include/wlr/types/wlr_output.h | 36 ++++++++++++++++++++++++++++++++++ types/output/output.c | 22 ++++++++++++++++++--- types/output/state.c | 34 +++++++++++++++++++++++++++++++- 3 files changed, 88 insertions(+), 4 deletions(-) diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index ead470396..1dae4b7db 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -66,6 +66,8 @@ enum wlr_output_state_field { WLR_OUTPUT_STATE_RENDER_FORMAT = 1 << 8, WLR_OUTPUT_STATE_SUBPIXEL = 1 << 9, WLR_OUTPUT_STATE_LAYERS = 1 << 10, + WLR_OUTPUT_STATE_WAIT_TIMELINE = 1 << 11, + WLR_OUTPUT_STATE_SIGNAL_TIMELINE = 1 << 12, }; enum wlr_output_state_mode_type { @@ -109,6 +111,11 @@ struct wlr_output_state { struct wlr_output_layer_state *layers; size_t layers_len; + + struct wlr_drm_syncobj_timeline *wait_timeline; + uint64_t wait_point; + struct wlr_drm_syncobj_timeline *signal_timeline; + uint64_t signal_point; }; struct wlr_output_impl; @@ -162,6 +169,8 @@ struct wlr_output { // true for example with VR headsets bool non_desktop; + // Whether wait/signal timelines are supported + bool timeline; // Commit sequence number. Incremented on each commit, may overflow. uint32_t commit_seq; @@ -536,6 +545,33 @@ void wlr_output_state_set_damage(struct wlr_output_state *state, */ void wlr_output_state_set_layers(struct wlr_output_state *state, struct wlr_output_layer_state *layers, size_t layers_len); +/** + * Set a timeline point to wait on before displaying the next frame. + * + * Committing a wait timeline point without a buffer is invalid. + * + * There is only a single wait timeline point, waiting for multiple timeline + * points is unsupported. + * + * Support for this feature is advertised by the timeline field in + * struct wlr_output. + */ +void wlr_output_state_set_wait_timeline(struct wlr_output_state *state, + struct wlr_drm_syncobj_timeline *timeline, uint64_t src_point); +/** + * Set a timeline point to be signalled when the frame is no longer being used + * by the backend. + * + * Committing a signal timeline point without a buffer is invalid. + * + * There is only a single signal timeline point, signalling multiple timeline + * points is unsupported. + * + * Support for this feature is advertised by the timeline field in + * struct wlr_output. + */ +void wlr_output_state_set_signal_timeline(struct wlr_output_state *state, + struct wlr_drm_syncobj_timeline *timeline, uint64_t dst_point); /** * Copies the output state from src to dst. It is safe to then diff --git a/types/output/output.c b/types/output/output.c index 818f4549e..1c9a00779 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -563,9 +563,19 @@ static bool output_basic_test(struct wlr_output *output, wlr_log(WLR_DEBUG, "Primary buffer size mismatch"); return false; } - } else if (state->tearing_page_flip) { - wlr_log(WLR_ERROR, "Trying to commit a tearing page flip without a buffer?"); - return false; + } else { + if (state->tearing_page_flip) { + wlr_log(WLR_ERROR, "Tried to commit a tearing page flip without a buffer"); + return false; + } + if (state->committed & WLR_OUTPUT_STATE_WAIT_TIMELINE) { + wlr_log(WLR_DEBUG, "Tried to set wait timeline without a buffer"); + return false; + } + if (state->committed & WLR_OUTPUT_STATE_SIGNAL_TIMELINE) { + wlr_log(WLR_DEBUG, "Tried to set signal timeline without a buffer"); + return false; + } } if (state->committed & WLR_OUTPUT_STATE_RENDER_FORMAT) { @@ -632,6 +642,12 @@ static bool output_basic_test(struct wlr_output *output, } } + if ((state->committed & (WLR_OUTPUT_STATE_WAIT_TIMELINE | WLR_OUTPUT_STATE_SIGNAL_TIMELINE)) && + !output->timeline) { + wlr_log(WLR_DEBUG, "Wait/signal timelines are not supported for this output"); + return false; + } + return true; } diff --git a/types/output/state.c b/types/output/state.c index 0909b3e8a..465b54ada 100644 --- a/types/output/state.c +++ b/types/output/state.c @@ -1,5 +1,6 @@ #include #include +#include #include #include "types/wlr_output.h" @@ -16,6 +17,8 @@ void wlr_output_state_finish(struct wlr_output_state *state) { state->buffer = NULL; pixman_region32_fini(&state->damage); free(state->gamma_lut); + wlr_drm_syncobj_timeline_unref(state->wait_timeline); + wlr_drm_syncobj_timeline_unref(state->signal_timeline); } void wlr_output_state_set_enabled(struct wlr_output_state *state, @@ -114,16 +117,36 @@ void wlr_output_state_set_layers(struct wlr_output_state *state, state->layers_len = layers_len; } +void wlr_output_state_set_wait_timeline(struct wlr_output_state *state, + struct wlr_drm_syncobj_timeline *timeline, uint64_t src_point) { + state->committed |= WLR_OUTPUT_STATE_WAIT_TIMELINE; + wlr_drm_syncobj_timeline_unref(state->wait_timeline); + state->wait_timeline = wlr_drm_syncobj_timeline_ref(timeline); + state->wait_point = src_point; +} + +void wlr_output_state_set_signal_timeline(struct wlr_output_state *state, + struct wlr_drm_syncobj_timeline *timeline, uint64_t dst_point) { + state->committed |= WLR_OUTPUT_STATE_SIGNAL_TIMELINE; + wlr_drm_syncobj_timeline_unref(state->signal_timeline); + state->signal_timeline = wlr_drm_syncobj_timeline_ref(timeline); + state->signal_point = dst_point; +} + bool wlr_output_state_copy(struct wlr_output_state *dst, const struct wlr_output_state *src) { struct wlr_output_state copy = *src; copy.committed &= ~(WLR_OUTPUT_STATE_BUFFER | WLR_OUTPUT_STATE_DAMAGE | - WLR_OUTPUT_STATE_GAMMA_LUT); + WLR_OUTPUT_STATE_GAMMA_LUT | + WLR_OUTPUT_STATE_WAIT_TIMELINE | + WLR_OUTPUT_STATE_SIGNAL_TIMELINE); copy.buffer = NULL; pixman_region32_init(©.damage); copy.gamma_lut = NULL; copy.gamma_lut_size = 0; + copy.wait_timeline = NULL; + copy.signal_timeline = NULL; if (src->committed & WLR_OUTPUT_STATE_BUFFER) { wlr_output_state_set_buffer(©, src->buffer); @@ -142,6 +165,15 @@ bool wlr_output_state_copy(struct wlr_output_state *dst, } } + if (src->committed & WLR_OUTPUT_STATE_WAIT_TIMELINE) { + wlr_output_state_set_wait_timeline(©, src->wait_timeline, + src->wait_point); + } + if (src->committed & WLR_OUTPUT_STATE_SIGNAL_TIMELINE) { + wlr_output_state_set_signal_timeline(©, src->signal_timeline, + src->signal_point); + } + wlr_output_state_finish(dst); *dst = copy; return true; From 3067e45c2ee913a0eeb1688c9b830490331d37ea Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 20 Oct 2021 20:09:38 +0200 Subject: [PATCH 028/519] backend/drm: add support for explicit sync APIs --- backend/drm/atomic.c | 62 ++++++++++++++++++++++++++++++-- backend/drm/drm.c | 10 +++++- backend/drm/properties.c | 2 ++ include/backend/drm/drm.h | 1 + include/backend/drm/properties.h | 2 ++ 5 files changed, 74 insertions(+), 3 deletions(-) diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index 2649a1a55..d3a91efbe 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -1,6 +1,8 @@ #include #include #include +#include +#include #include #include #include @@ -272,6 +274,15 @@ bool drm_atomic_connector_prepare(struct wlr_drm_connector_state *state, bool mo state->primary_fb->wlr_buf->height, &state->base->damage, &fb_damage_clips); } + int in_fence_fd = -1; + if (state->base->committed & WLR_OUTPUT_STATE_WAIT_TIMELINE) { + in_fence_fd = wlr_drm_syncobj_timeline_export_sync_file(state->base->wait_timeline, + state->base->wait_point); + if (in_fence_fd < 0) { + return false; + } + } + bool prev_vrr_enabled = output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED; bool vrr_enabled = prev_vrr_enabled; @@ -285,6 +296,7 @@ bool drm_atomic_connector_prepare(struct wlr_drm_connector_state *state, bool mo state->mode_id = mode_id; state->gamma_lut = gamma_lut; state->fb_damage_clips = fb_damage_clips; + state->primary_in_fence_fd = in_fence_fd; state->vrr_enabled = vrr_enabled; return true; } @@ -305,6 +317,15 @@ void drm_atomic_connector_apply_commit(struct wlr_drm_connector_state *state) { WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED : WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED; destroy_blob(drm, state->fb_damage_clips); + if (state->primary_in_fence_fd >= 0) { + close(state->primary_in_fence_fd); + } + if (state->out_fence_fd >= 0) { + // TODO: error handling + wlr_drm_syncobj_timeline_import_sync_file(state->base->signal_timeline, + state->base->signal_point, state->out_fence_fd); + close(state->out_fence_fd); + } } void drm_atomic_connector_rollback_commit(struct wlr_drm_connector_state *state) { @@ -316,6 +337,12 @@ void drm_atomic_connector_rollback_commit(struct wlr_drm_connector_state *state) rollback_blob(drm, &crtc->gamma_lut, state->gamma_lut); destroy_blob(drm, state->fb_damage_clips); + if (state->primary_in_fence_fd >= 0) { + close(state->primary_in_fence_fd); + } + if (state->out_fence_fd >= 0) { + close(state->out_fence_fd); + } } static void plane_disable(struct atomic *atom, struct wlr_drm_plane *plane) { @@ -353,12 +380,37 @@ static void set_plane_props(struct atomic *atom, struct wlr_drm_backend *drm, atomic_add(atom, id, props->crtc_y, (uint64_t)y); } -static bool supports_cursor_hotspots(const struct wlr_drm_plane* plane) { +static bool supports_cursor_hotspots(const struct wlr_drm_plane *plane) { return plane->props.hotspot_x && plane->props.hotspot_y; } +static void set_plane_in_fence_fd(struct atomic *atom, + struct wlr_drm_plane *plane, int sync_file_fd) { + if (!plane->props.in_fence_fd) { + wlr_log(WLR_ERROR, "Plane %"PRIu32 " is missing the IN_FENCE_FD property", + plane->id); + atom->failed = true; + return; + } + + atomic_add(atom, plane->id, plane->props.in_fence_fd, sync_file_fd); +} + +static void set_crtc_out_fence_ptr(struct atomic *atom, struct wlr_drm_crtc *crtc, + int *fd_ptr) { + if (!crtc->props.out_fence_ptr) { + wlr_log(WLR_ERROR, + "CRTC %"PRIu32" is missing the OUT_FENCE_PTR property", + crtc->id); + atom->failed = true; + return; + } + + atomic_add(atom, crtc->id, crtc->props.out_fence_ptr, (uintptr_t)fd_ptr); +} + static void atomic_connector_add(struct atomic *atom, - const struct wlr_drm_connector_state *state, bool modeset) { + struct wlr_drm_connector_state *state, bool modeset) { struct wlr_drm_connector *conn = state->connector; struct wlr_drm_backend *drm = conn->backend; struct wlr_drm_crtc *crtc = conn->crtc; @@ -391,6 +443,12 @@ static void atomic_connector_add(struct atomic *atom, atomic_add(atom, crtc->primary->id, crtc->primary->props.fb_damage_clips, state->fb_damage_clips); } + if (state->primary_in_fence_fd >= 0) { + set_plane_in_fence_fd(atom, crtc->primary, state->primary_in_fence_fd); + } + if (state->base->committed & WLR_OUTPUT_STATE_SIGNAL_TIMELINE) { + set_crtc_out_fence_ptr(atom, crtc, &state->out_fence_fd); + } if (crtc->cursor) { if (drm_connector_is_cursor_visible(conn)) { set_plane_props(atom, drm, crtc->cursor, state->cursor_fb, diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 5756f2897..4104f87c7 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -42,7 +42,9 @@ static const uint32_t COMMIT_OUTPUT_STATE = WLR_OUTPUT_STATE_ENABLED | WLR_OUTPUT_STATE_GAMMA_LUT | WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED | - WLR_OUTPUT_STATE_LAYERS; + WLR_OUTPUT_STATE_LAYERS | + WLR_OUTPUT_STATE_WAIT_TIMELINE | + WLR_OUTPUT_STATE_SIGNAL_TIMELINE; static const uint32_t SUPPORTED_OUTPUT_STATE = WLR_OUTPUT_STATE_BACKEND_OPTIONAL | COMMIT_OUTPUT_STATE; @@ -630,6 +632,8 @@ static void drm_connector_state_init(struct wlr_drm_connector_state *state, .connector = conn, .base = base, .active = output_pending_enabled(&conn->output, base), + .primary_in_fence_fd = -1, + .out_fence_fd = -1, }; struct wlr_output_mode *mode = conn->output.current_mode; @@ -1619,6 +1623,10 @@ static bool connect_drm_connector(struct wlr_drm_connector *wlr_conn, output->non_desktop = non_desktop; } + // TODO: support sync timelines in multi-GPU mode + // TODO: support sync timelines with libliftoff + output->timeline = drm->parent == NULL && drm->iface == &atomic_iface; + memset(wlr_conn->max_bpc_bounds, 0, sizeof(wlr_conn->max_bpc_bounds)); if (wlr_conn->props.max_bpc != 0) { if (!introspect_drm_prop_range(drm->fd, wlr_conn->props.max_bpc, diff --git a/backend/drm/properties.c b/backend/drm/properties.c index d78bfaf64..05af16738 100644 --- a/backend/drm/properties.c +++ b/backend/drm/properties.c @@ -40,6 +40,7 @@ static const struct prop_info crtc_info[] = { { "GAMMA_LUT", INDEX(gamma_lut) }, { "GAMMA_LUT_SIZE", INDEX(gamma_lut_size) }, { "MODE_ID", INDEX(mode_id) }, + { "OUT_FENCE_PTR", INDEX(out_fence_ptr) }, { "VRR_ENABLED", INDEX(vrr_enabled) }, #undef INDEX }; @@ -55,6 +56,7 @@ static const struct prop_info plane_info[] = { { "FB_ID", INDEX(fb_id) }, { "HOTSPOT_X", INDEX(hotspot_x) }, { "HOTSPOT_Y", INDEX(hotspot_y) }, + { "IN_FENCE_FD", INDEX(in_fence_fd) }, { "IN_FORMATS", INDEX(in_formats) }, { "SIZE_HINTS", INDEX(size_hints) }, { "SRC_H", INDEX(src_h) }, diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index 1106e9f56..18f487420 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -145,6 +145,7 @@ struct wlr_drm_connector_state { uint32_t mode_id; uint32_t gamma_lut; uint32_t fb_damage_clips; + int primary_in_fence_fd, out_fence_fd; bool vrr_enabled; }; diff --git a/include/backend/drm/properties.h b/include/backend/drm/properties.h index 570737021..421eb4275 100644 --- a/include/backend/drm/properties.h +++ b/include/backend/drm/properties.h @@ -38,6 +38,7 @@ struct wlr_drm_crtc_props { uint32_t active; uint32_t mode_id; + uint32_t out_fence_ptr; }; struct wlr_drm_plane_props { @@ -61,6 +62,7 @@ struct wlr_drm_plane_props { uint32_t fb_damage_clips; uint32_t hotspot_x; uint32_t hotspot_y; + uint32_t in_fence_fd; }; bool get_drm_connector_props(int fd, uint32_t id, From 5552de65f8b1342148bce1c1f90d49b0940d528b Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 21 Oct 2021 15:23:20 +0200 Subject: [PATCH 029/519] render/drm_syncobj: add wlr_drm_syncobj_timeline_transfer() --- include/wlr/render/drm_syncobj.h | 7 +++++++ render/drm_syncobj.c | 13 +++++++++++++ 2 files changed, 20 insertions(+) diff --git a/include/wlr/render/drm_syncobj.h b/include/wlr/render/drm_syncobj.h index be3dce2d7..232c13a00 100644 --- a/include/wlr/render/drm_syncobj.h +++ b/include/wlr/render/drm_syncobj.h @@ -62,6 +62,13 @@ struct wlr_drm_syncobj_timeline *wlr_drm_syncobj_timeline_ref(struct wlr_drm_syn * Unreference a synchronization timeline. */ void wlr_drm_syncobj_timeline_unref(struct wlr_drm_syncobj_timeline *timeline); +/** + * Transfer a point from a timeline to another. + * + * Both timelines must have been created with the same DRM FD. + */ +bool wlr_drm_syncobj_timeline_transfer(struct wlr_drm_syncobj_timeline *dst, + uint64_t dst_point, struct wlr_drm_syncobj_timeline *src, uint64_t src_point); /** * Check if a timeline point has been signalled or has materialized. * diff --git a/render/drm_syncobj.c b/render/drm_syncobj.c index af3e79fcd..a612e20ff 100644 --- a/render/drm_syncobj.c +++ b/render/drm_syncobj.c @@ -67,6 +67,19 @@ void wlr_drm_syncobj_timeline_unref(struct wlr_drm_syncobj_timeline *timeline) { free(timeline); } +bool wlr_drm_syncobj_timeline_transfer(struct wlr_drm_syncobj_timeline *dst, + uint64_t dst_point, struct wlr_drm_syncobj_timeline *src, uint64_t src_point) { + assert(dst->drm_fd == src->drm_fd); + + if (drmSyncobjTransfer(dst->drm_fd, dst->handle, dst_point, + src->handle, src_point, 0) != 0) { + wlr_log_errno(WLR_ERROR, "drmSyncobjTransfer failed"); + return false; + } + + return true; +} + int wlr_drm_syncobj_timeline_export_sync_file(struct wlr_drm_syncobj_timeline *timeline, uint64_t src_point) { int sync_file_fd = -1; From edb867bc051878eccbca4a46ee2d5db0b5cdf393 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 27 Feb 2024 18:38:12 +0100 Subject: [PATCH 030/519] render/drm_syncobj: add wlr_drm_syncobj_timeline_export() --- include/wlr/render/drm_syncobj.h | 4 ++++ render/drm_syncobj.c | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/include/wlr/render/drm_syncobj.h b/include/wlr/render/drm_syncobj.h index 232c13a00..bf406d871 100644 --- a/include/wlr/render/drm_syncobj.h +++ b/include/wlr/render/drm_syncobj.h @@ -62,6 +62,10 @@ struct wlr_drm_syncobj_timeline *wlr_drm_syncobj_timeline_ref(struct wlr_drm_syn * Unreference a synchronization timeline. */ void wlr_drm_syncobj_timeline_unref(struct wlr_drm_syncobj_timeline *timeline); +/** + * Export a drm_syncobj FD from a timeline. + */ +int wlr_drm_syncobj_timeline_export(struct wlr_drm_syncobj_timeline *timeline); /** * Transfer a point from a timeline to another. * diff --git a/render/drm_syncobj.c b/render/drm_syncobj.c index a612e20ff..f7120df30 100644 --- a/render/drm_syncobj.c +++ b/render/drm_syncobj.c @@ -67,6 +67,15 @@ void wlr_drm_syncobj_timeline_unref(struct wlr_drm_syncobj_timeline *timeline) { free(timeline); } +int wlr_drm_syncobj_timeline_export(struct wlr_drm_syncobj_timeline *timeline) { + int drm_syncobj_fd = -1; + if (drmSyncobjHandleToFD(timeline->drm_fd, timeline->handle, &drm_syncobj_fd) != 0) { + wlr_log_errno(WLR_ERROR, "drmSyncobjHandleToFD failed"); + return -1; + } + return drm_syncobj_fd; +} + bool wlr_drm_syncobj_timeline_transfer(struct wlr_drm_syncobj_timeline *dst, uint64_t dst_point, struct wlr_drm_syncobj_timeline *src, uint64_t src_point) { assert(dst->drm_fd == src->drm_fd); From 48f0902a363de24bb9e759331d4518b7eeb30cf3 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 5 Aug 2024 19:28:03 +0200 Subject: [PATCH 031/519] scene: add wlr_scene_buffer_set_buffer_with_options() This is an extensible version of wlr_scene_buffer_set_buffer(). --- include/wlr/types/wlr_scene.h | 18 ++++++++++++++++++ types/scene/wlr_scene.c | 22 ++++++++++++++++++---- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index d1fbca7de..7058e01b6 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -404,6 +404,24 @@ void wlr_scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer, void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buffer, struct wlr_buffer *buffer, const pixman_region32_t *region); +/** + * Options for wlr_scene_buffer_set_buffer_with_options(). + */ +struct wlr_scene_buffer_set_buffer_options { + // The damage region is in buffer-local coordinates. If the region is NULL, + // the whole buffer node will be damaged. + const pixman_region32_t *damage; +}; + +/** + * Sets the buffer's backing buffer. + * + * If the buffer is NULL, the buffer node will not be displayed. If options is + * NULL, empty options are used. + */ +void wlr_scene_buffer_set_buffer_with_options(struct wlr_scene_buffer *scene_buffer, + struct wlr_buffer *buffer, const struct wlr_scene_buffer_set_buffer_options *options); + /** * Sets the buffer's opaque region. This is an optimization hint used to * determine if buffers which reside under this one need to be rendered or not. diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index c38584db9..b8e884625 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -686,12 +686,17 @@ struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent, return scene_buffer; } -void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buffer, - struct wlr_buffer *buffer, const pixman_region32_t *damage) { +void wlr_scene_buffer_set_buffer_with_options(struct wlr_scene_buffer *scene_buffer, + struct wlr_buffer *buffer, const struct wlr_scene_buffer_set_buffer_options *options) { + const struct wlr_scene_buffer_set_buffer_options default_options = {0}; + if (options == NULL) { + options = &default_options; + } + // specifying a region for a NULL buffer doesn't make sense. We need to know // about the buffer to scale the buffer local coordinates down to scene // coordinates. - assert(buffer || !damage); + assert(buffer || !options->damage); bool mapped = buffer != NULL; bool prev_mapped = scene_buffer->buffer != NULL || scene_buffer->texture != NULL; @@ -727,6 +732,7 @@ void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buff pixman_region32_t fallback_damage; pixman_region32_init_rect(&fallback_damage, 0, 0, buffer->width, buffer->height); + const pixman_region32_t *damage = options->damage; if (!damage) { damage = &fallback_damage; } @@ -810,9 +816,17 @@ void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buff pixman_region32_fini(&fallback_damage); } +void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buffer, + struct wlr_buffer *buffer, const pixman_region32_t *damage) { + const struct wlr_scene_buffer_set_buffer_options options = { + .damage = damage, + }; + wlr_scene_buffer_set_buffer_with_options(scene_buffer, buffer, &options); +} + void wlr_scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer, struct wlr_buffer *buffer) { - wlr_scene_buffer_set_buffer_with_damage(scene_buffer, buffer, NULL); + wlr_scene_buffer_set_buffer_with_options(scene_buffer, buffer, NULL); } void wlr_scene_buffer_set_opaque_region(struct wlr_scene_buffer *scene_buffer, From c7035da5e27b787b57304de11b2e0fdcefaef02d Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 3 Apr 2024 14:32:30 +0200 Subject: [PATCH 032/519] scene: add timeline point to wlr_scene_buffer_set_buffer() options --- include/wlr/types/wlr_scene.h | 7 +++++++ types/scene/wlr_scene.c | 21 +++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 7058e01b6..302315e6d 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -191,6 +191,9 @@ struct wlr_scene_buffer { int buffer_width, buffer_height; bool buffer_is_opaque; + struct wlr_drm_syncobj_timeline *wait_timeline; + uint64_t wait_point; + struct wl_listener buffer_release; struct wl_listener renderer_destroy; }; @@ -411,6 +414,10 @@ struct wlr_scene_buffer_set_buffer_options { // The damage region is in buffer-local coordinates. If the region is NULL, // the whole buffer node will be damaged. const pixman_region32_t *damage; + + // Wait for a timeline synchronization point before reading from the buffer. + struct wlr_drm_syncobj_timeline *wait_timeline; + uint64_t wait_point; }; /** diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index b8e884625..948b778bf 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -117,6 +118,7 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) { scene_buffer_set_buffer(scene_buffer, NULL); scene_buffer_set_texture(scene_buffer, NULL); pixman_region32_fini(&scene_buffer->opaque_region); + wlr_drm_syncobj_timeline_unref(scene_buffer->wait_timeline); } else if (node->type == WLR_SCENE_NODE_TREE) { struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); @@ -661,6 +663,18 @@ static void scene_buffer_set_texture(struct wlr_scene_buffer *scene_buffer, } } +static void scene_buffer_set_wait_timeline(struct wlr_scene_buffer *scene_buffer, + struct wlr_drm_syncobj_timeline *timeline, uint64_t point) { + wlr_drm_syncobj_timeline_unref(scene_buffer->wait_timeline); + if (timeline != NULL) { + scene_buffer->wait_timeline = wlr_drm_syncobj_timeline_ref(timeline); + scene_buffer->wait_point = point; + } else { + scene_buffer->wait_timeline = NULL; + scene_buffer->wait_point = 0; + } +} + struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent, struct wlr_buffer *buffer) { struct wlr_scene_buffer *scene_buffer = calloc(1, sizeof(*scene_buffer)); @@ -717,6 +731,8 @@ void wlr_scene_buffer_set_buffer_with_options(struct wlr_scene_buffer *scene_buf scene_buffer_set_buffer(scene_buffer, buffer); scene_buffer_set_texture(scene_buffer, NULL); + scene_buffer_set_wait_timeline(scene_buffer, + options->wait_timeline, options->wait_point); if (update) { scene_node_update(&scene_buffer->node, NULL); @@ -1259,6 +1275,8 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren .blend_mode = !data->output->scene->calculate_visibility || pixman_region32_not_empty(&opaque) ? WLR_RENDER_BLEND_MODE_PREMULTIPLIED : WLR_RENDER_BLEND_MODE_NONE, + .wait_timeline = scene_buffer->wait_timeline, + .wait_point = scene_buffer->wait_point, }); struct wlr_scene_output_sample_event sample_event = { @@ -1687,6 +1705,9 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry, } wlr_output_state_set_buffer(&pending, buffer->buffer); + if (buffer->wait_timeline != NULL) { + wlr_output_state_set_wait_timeline(&pending, buffer->wait_timeline, buffer->wait_point); + } if (!wlr_output_test_state(scene_output->output, &pending)) { wlr_output_state_finish(&pending); From 850dd7a7929c9c4bb8ae98fc65846d820e1cc32c Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 10 May 2024 16:46:15 +0200 Subject: [PATCH 033/519] linux-drm-syncobj-v1: add helper to signal on buffer release --- include/wlr/types/wlr_linux_drm_syncobj_v1.h | 9 +++++ types/wlr_linux_drm_syncobj_v1.c | 37 ++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/include/wlr/types/wlr_linux_drm_syncobj_v1.h b/include/wlr/types/wlr_linux_drm_syncobj_v1.h index c6e0617b1..1a29a0d8f 100644 --- a/include/wlr/types/wlr_linux_drm_syncobj_v1.h +++ b/include/wlr/types/wlr_linux_drm_syncobj_v1.h @@ -43,4 +43,13 @@ struct wlr_linux_drm_syncobj_manager_v1 *wlr_linux_drm_syncobj_manager_v1_create struct wlr_linux_drm_syncobj_surface_v1_state *wlr_linux_drm_syncobj_v1_get_surface_state( struct wlr_surface *surface); +/** + * Signal the release point when wlr_buffer.events.release is emitted. + * + * Compositors unwilling to track fine-grained commit release can call this + * helper on surface commit. + */ +bool wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer( + struct wlr_linux_drm_syncobj_surface_v1_state *state, struct wlr_buffer *buffer); + #endif diff --git a/types/wlr_linux_drm_syncobj_v1.c b/types/wlr_linux_drm_syncobj_v1.c index 82ace2d78..bc2c79dc7 100644 --- a/types/wlr_linux_drm_syncobj_v1.c +++ b/types/wlr_linux_drm_syncobj_v1.c @@ -467,3 +467,40 @@ wlr_linux_drm_syncobj_v1_get_surface_state(struct wlr_surface *wlr_surface) { } return &surface->current; } + +struct release_signaller { + struct wlr_drm_syncobj_timeline *timeline; + uint64_t point; + struct wl_listener buffer_release; +}; + +static void release_signaller_handle_buffer_release(struct wl_listener *listener, void *data) { + struct release_signaller *signaller = wl_container_of(listener, signaller, buffer_release); + + if (drmSyncobjTimelineSignal(signaller->timeline->drm_fd, &signaller->timeline->handle, + &signaller->point, 1) != 0) { + wlr_log(WLR_ERROR, "drmSyncobjTimelineSignal() failed"); + } + + wlr_drm_syncobj_timeline_unref(signaller->timeline); + free(signaller); +} + +bool wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer( + struct wlr_linux_drm_syncobj_surface_v1_state *state, struct wlr_buffer *buffer) { + assert(buffer->n_locks > 0); + assert(state->release_timeline != NULL); + + struct release_signaller *signaller = calloc(1, sizeof(*signaller)); + if (signaller == NULL) { + return false; + } + + signaller->timeline = wlr_drm_syncobj_timeline_ref(state->release_timeline); + signaller->point = state->release_point; + + signaller->buffer_release.notify = release_signaller_handle_buffer_release; + wl_signal_add(&buffer->events.release, &signaller->buffer_release); + + return true; +} From 9e9636f675442ac98ee5d8b79f821e4c79912a7a Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 3 Apr 2024 14:37:12 +0200 Subject: [PATCH 034/519] scene: add support for linux-drm-syncobj-v1 --- types/scene/surface.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/types/scene/surface.c b/types/scene/surface.c index 1905b4dfb..2aff5af37 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include "types/wlr_scene.h" @@ -164,8 +165,29 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { if (surface->buffer) { client_buffer_mark_next_can_damage(surface->buffer); - wlr_scene_buffer_set_buffer_with_damage(scene_buffer, - &surface->buffer->base, &surface->buffer_damage); + struct wlr_linux_drm_syncobj_surface_v1_state *syncobj_surface_state = + wlr_linux_drm_syncobj_v1_get_surface_state(surface); + + struct wlr_drm_syncobj_timeline *wait_timeline = NULL; + uint64_t wait_point = 0; + if (syncobj_surface_state != NULL) { + wait_timeline = syncobj_surface_state->acquire_timeline; + wait_point = syncobj_surface_state->acquire_point; + } + + struct wlr_scene_buffer_set_buffer_options options = { + .damage = &surface->buffer_damage, + .wait_timeline = wait_timeline, + .wait_point = wait_point, + }; + wlr_scene_buffer_set_buffer_with_options(scene_buffer, + &surface->buffer->base, &options); + + if (syncobj_surface_state != NULL && + (surface->current.committed & WLR_SURFACE_STATE_BUFFER)) { + wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer(syncobj_surface_state, + &surface->buffer->base); + } } else { wlr_scene_buffer_set_buffer(scene_buffer, NULL); } From 5f886351181ead1f862056ce622cb1ab9e75f915 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 3 Apr 2024 15:08:44 +0200 Subject: [PATCH 035/519] scene: add explicit synchronization for rendered buffers --- include/wlr/types/wlr_scene.h | 3 +++ types/scene/wlr_scene.c | 18 +++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 302315e6d..b5e2d8390 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -227,6 +227,9 @@ struct wlr_scene_output { struct wl_list damage_highlight_regions; struct wl_array render_list; + + struct wlr_drm_syncobj_timeline *in_timeline; + uint64_t in_point; }; struct wlr_scene_timer { diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 948b778bf..b201a9c79 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1439,6 +1439,14 @@ struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene, prev_output_link = ¤t_output->link; } + int drm_fd = wlr_backend_get_drm_fd(output->backend); + if (drm_fd >= 0 && output->timeline && output->renderer != NULL && output->renderer->features.timeline) { + scene_output->in_timeline = wlr_drm_syncobj_timeline_create(drm_fd); + if (scene_output->in_timeline == NULL) { + return NULL; + } + } + scene_output->index = prev_output_index + 1; assert(scene_output->index < 64); wl_list_insert(prev_output_link, &scene_output->link); @@ -1487,7 +1495,7 @@ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) { wl_list_remove(&scene_output->output_commit.link); wl_list_remove(&scene_output->output_damage.link); wl_list_remove(&scene_output->output_needs_frame.link); - + wlr_drm_syncobj_timeline_unref(scene_output->in_timeline); wl_array_release(&scene_output->render_list); free(scene_output); } @@ -1919,10 +1927,13 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, timer->pre_render_duration = timespec_to_nsec(&duration); } + scene_output->in_point++; struct wlr_render_pass *render_pass = wlr_renderer_begin_buffer_pass(output->renderer, buffer, &(struct wlr_buffer_pass_options){ .timer = timer ? timer->render_timer : NULL, .color_transform = options->color_transform, + .signal_timeline = scene_output->in_timeline, + .signal_point = scene_output->in_point, }); if (render_pass == NULL) { wlr_buffer_unlock(buffer); @@ -2029,6 +2040,11 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, wlr_output_state_set_buffer(state, buffer); wlr_buffer_unlock(buffer); + if (scene_output->in_timeline != NULL) { + wlr_output_state_set_wait_timeline(state, scene_output->in_timeline, + scene_output->in_point); + } + return true; } From 738bbf01ee1b3f12116d6de956cd70c30f3637dd Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 10 May 2024 18:32:04 +0200 Subject: [PATCH 036/519] cursor: add support for linux-drm-syncobj-v1 --- include/types/wlr_output.h | 3 ++- include/wlr/types/wlr_output.h | 2 ++ types/output/cursor.c | 21 ++++++++++++++++++--- types/wlr_cursor.c | 20 ++++++++++++++++++-- 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/include/types/wlr_output.h b/include/types/wlr_output.h index 09be35510..bd095d8f2 100644 --- a/include/types/wlr_output.h +++ b/include/types/wlr_output.h @@ -18,7 +18,8 @@ bool output_ensure_buffer(struct wlr_output *output, bool output_cursor_set_texture(struct wlr_output_cursor *cursor, struct wlr_texture *texture, bool own_texture, const struct wlr_fbox *src_box, int dst_width, int dst_height, enum wl_output_transform transform, - int32_t hotspot_x, int32_t hotspot_y); + int32_t hotspot_x, int32_t hotspot_y, struct wlr_drm_syncobj_timeline *wait_timeline, + uint64_t wait_point); void output_defer_present(struct wlr_output *output, struct wlr_output_event_present event); diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 1dae4b7db..f32e15ef5 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -45,6 +45,8 @@ struct wlr_output_cursor { int32_t hotspot_x, hotspot_y; struct wlr_texture *texture; bool own_texture; + struct wlr_drm_syncobj_timeline *wait_timeline; + uint64_t wait_point; struct wl_listener renderer_destroy; struct wl_list link; }; diff --git a/types/output/cursor.c b/types/output/cursor.c index 2bf785283..95793a39d 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -271,6 +272,8 @@ static struct wlr_buffer *render_cursor_buffer(struct wlr_output_cursor *cursor) .src_box = cursor->src_box, .dst_box = dst_box, .transform = transform, + .wait_timeline = cursor->wait_timeline, + .wait_point = cursor->wait_point, }); if (!wlr_render_pass_submit(pass)) { @@ -355,20 +358,22 @@ bool wlr_output_cursor_set_buffer(struct wlr_output_cursor *cursor, hotspot_y /= cursor->output->scale; return output_cursor_set_texture(cursor, texture, true, &src_box, - dst_width, dst_height, WL_OUTPUT_TRANSFORM_NORMAL, hotspot_x, hotspot_y); + dst_width, dst_height, WL_OUTPUT_TRANSFORM_NORMAL, hotspot_x, hotspot_y, + NULL, 0); } static void output_cursor_handle_renderer_destroy(struct wl_listener *listener, void *data) { struct wlr_output_cursor *cursor = wl_container_of(listener, cursor, renderer_destroy); output_cursor_set_texture(cursor, NULL, false, NULL, 0, 0, - WL_OUTPUT_TRANSFORM_NORMAL, 0, 0); + WL_OUTPUT_TRANSFORM_NORMAL, 0, 0, NULL, 0); } bool output_cursor_set_texture(struct wlr_output_cursor *cursor, struct wlr_texture *texture, bool own_texture, const struct wlr_fbox *src_box, int dst_width, int dst_height, enum wl_output_transform transform, - int32_t hotspot_x, int32_t hotspot_y) { + int32_t hotspot_x, int32_t hotspot_y, + struct wlr_drm_syncobj_timeline *wait_timeline, uint64_t wait_point) { struct wlr_output *output = cursor->output; output_cursor_reset(cursor); @@ -395,6 +400,15 @@ bool output_cursor_set_texture(struct wlr_output_cursor *cursor, cursor->texture = texture; cursor->own_texture = own_texture; + wlr_drm_syncobj_timeline_unref(cursor->wait_timeline); + if (wait_timeline != NULL) { + cursor->wait_timeline = wlr_drm_syncobj_timeline_ref(wait_timeline); + cursor->wait_point = wait_point; + } else { + cursor->wait_timeline = NULL; + cursor->wait_point = 0; + } + wl_list_remove(&cursor->renderer_destroy.link); if (texture != NULL) { cursor->renderer_destroy.notify = output_cursor_handle_renderer_destroy; @@ -471,6 +485,7 @@ void wlr_output_cursor_destroy(struct wlr_output_cursor *cursor) { if (cursor->own_texture) { wlr_texture_destroy(cursor->texture); } + wlr_drm_syncobj_timeline_unref(cursor->wait_timeline); wl_list_remove(&cursor->link); free(cursor); } diff --git a/types/wlr_cursor.c b/types/wlr_cursor.c index 77ab2fb70..a2489fbd1 100644 --- a/types/wlr_cursor.c +++ b/types/wlr_cursor.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -534,7 +535,7 @@ static void cursor_output_cursor_update(struct wlr_cursor_output_cursor *output_ output_cursor_set_texture(output_cursor->output_cursor, texture, true, &src_box, dst_width, dst_height, WL_OUTPUT_TRANSFORM_NORMAL, - hotspot_x, hotspot_y); + hotspot_x, hotspot_y, NULL, 0); } else if (cur->state->surface != NULL) { struct wlr_surface *surface = cur->state->surface; @@ -547,9 +548,24 @@ static void cursor_output_cursor_update(struct wlr_cursor_output_cursor *output_ int dst_width = surface->current.width; int dst_height = surface->current.height; + struct wlr_linux_drm_syncobj_surface_v1_state *syncobj_surface_state = + wlr_linux_drm_syncobj_v1_get_surface_state(surface); + struct wlr_drm_syncobj_timeline *wait_timeline = NULL; + uint64_t wait_point = 0; + if (syncobj_surface_state != NULL) { + wait_timeline = syncobj_surface_state->acquire_timeline; + wait_point = syncobj_surface_state->acquire_point; + } + output_cursor_set_texture(output_cursor->output_cursor, texture, false, &src_box, dst_width, dst_height, surface->current.transform, - hotspot_x, hotspot_y); + hotspot_x, hotspot_y, wait_timeline, wait_point); + + if (syncobj_surface_state != NULL && surface->buffer != NULL && + (surface->current.committed & WLR_SURFACE_STATE_BUFFER)) { + wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer(syncobj_surface_state, + &surface->buffer->base); + } if (output_cursor->output_cursor->visible) { wlr_surface_send_enter(surface, output); From 775817e278fde86c8c41e3eee791798781adc0e2 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 4 Aug 2024 16:23:53 +0200 Subject: [PATCH 037/519] render: add WLR_RENDER_NO_EXPLICIT_SYNC env var This can be handy to figure out if a bug is due to explicit sync. --- docs/env_vars.md | 2 ++ render/wlr_renderer.c | 3 +++ 2 files changed, 5 insertions(+) diff --git a/docs/env_vars.md b/docs/env_vars.md index a2a86c84d..06e47ac59 100644 --- a/docs/env_vars.md +++ b/docs/env_vars.md @@ -12,6 +12,8 @@ wlroots reads these environment variables renderers: gles2, pixman, vulkan) * *WLR_RENDER_DRM_DEVICE*: specifies the DRM node to use for hardware-accelerated renderers. +* *WLR_RENDER_NO_EXPLICIT_SYNC*: set to 1 to disable explicit synchronization + support in renderers. * *WLR_EGL_NO_MODIFIERS*: set to 1 to disable format modifiers in EGL, this can be used to understand and work around driver bugs. diff --git a/render/wlr_renderer.c b/render/wlr_renderer.c index 6a28908c4..a580ab56d 100644 --- a/render/wlr_renderer.c +++ b/render/wlr_renderer.c @@ -274,6 +274,9 @@ out: if (own_drm_fd && drm_fd >= 0) { close(drm_fd); } + if (renderer != NULL && env_parse_bool("WLR_RENDER_NO_EXPLICIT_SYNC")) { + renderer->features.timeline = false; + } return renderer; } From 8730ca9661eaaab83954bca033745b65c60cf4f8 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Mon, 5 Aug 2024 18:28:20 +0300 Subject: [PATCH 038/519] seat/pointer: handle duplicate presses/releases correctly --- backend/libinput/pointer.c | 10 ---------- include/wlr/types/wlr_seat.h | 8 +++++++- types/seat/wlr_seat_pointer.c | 35 +++++++++++++++++++++++++++++------ 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/backend/libinput/pointer.c b/backend/libinput/pointer.c index 1fe3d4790..703ad7b33 100644 --- a/backend/libinput/pointer.c +++ b/backend/libinput/pointer.c @@ -61,22 +61,12 @@ void handle_pointer_button(struct libinput_event *event, .time_msec = usec_to_msec(libinput_event_pointer_get_time_usec(pevent)), .button = libinput_event_pointer_get_button(pevent), }; - // Ignore events which aren't a seat-wide state change. For instance, if - // the same button is pressed twice on the same seat, ignore the second - // press. - uint32_t seat_count = libinput_event_pointer_get_seat_button_count(pevent); switch (libinput_event_pointer_get_button_state(pevent)) { case LIBINPUT_BUTTON_STATE_PRESSED: wlr_event.state = WL_POINTER_BUTTON_STATE_PRESSED; - if (seat_count != 1) { - return; - } break; case LIBINPUT_BUTTON_STATE_RELEASED: wlr_event.state = WL_POINTER_BUTTON_STATE_RELEASED; - if (seat_count != 0) { - return; - } break; } wl_signal_emit_mutable(&pointer->events.button, &wlr_event); diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h index 24ed5c92f..c04cd5e06 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -172,6 +172,11 @@ struct wlr_seat_pointer_grab { #define WLR_POINTER_BUTTONS_CAP 16 +struct wlr_seat_pointer_button { + uint32_t button; + size_t n_pressed; +}; + struct wlr_seat_pointer_state { struct wlr_seat *seat; struct wlr_seat_client *focused_client; @@ -184,8 +189,9 @@ struct wlr_seat_pointer_state { bool sent_axis_source; enum wl_pointer_axis_source cached_axis_source; - uint32_t buttons[WLR_POINTER_BUTTONS_CAP]; + struct wlr_seat_pointer_button buttons[WLR_POINTER_BUTTONS_CAP]; size_t button_count; + uint32_t grab_button; uint32_t grab_serial; uint32_t grab_time; diff --git a/types/seat/wlr_seat_pointer.c b/types/seat/wlr_seat_pointer.c index 9618d52b4..15075f612 100644 --- a/types/seat/wlr_seat_pointer.c +++ b/types/seat/wlr_seat_pointer.c @@ -6,7 +6,6 @@ #include #include #include "types/wlr_seat.h" -#include "util/set.h" static void default_pointer_enter(struct wlr_seat_pointer_grab *grab, struct wlr_surface *surface, double sx, double sy) { @@ -463,14 +462,38 @@ uint32_t wlr_seat_pointer_notify_button(struct wlr_seat *wlr_seat, pointer_state->grab_button = button; pointer_state->grab_time = time; } - set_add(pointer_state->buttons, &pointer_state->button_count, - WLR_POINTER_BUTTONS_CAP, button); + for (size_t i = 0; i < pointer_state->button_count; i++) { + struct wlr_seat_pointer_button *pointer_button = &pointer_state->buttons[i]; + if (pointer_button->button == button) { + ++pointer_button->n_pressed; + return 0; + } + } + if (pointer_state->button_count == WLR_POINTER_BUTTONS_CAP) { + return 0; + } + pointer_state->buttons[pointer_state->button_count++] = (struct wlr_seat_pointer_button){ + .button = button, + .n_pressed = 1, + }; } else { - set_remove(pointer_state->buttons, &pointer_state->button_count, - WLR_POINTER_BUTTONS_CAP, button); + bool found = false; + for (size_t i = 0; i < pointer_state->button_count; i++) { + struct wlr_seat_pointer_button *pointer_button = &pointer_state->buttons[i]; + if (pointer_button->button == button) { + if (--pointer_button->n_pressed > 0) { + return 0; + } + *pointer_button = pointer_state->buttons[--pointer_state->button_count]; + found = true; + break; + } + } + if (!found) { + return 0; + } } - struct wlr_seat_pointer_grab *grab = pointer_state->grab; uint32_t serial = grab->interface->button(grab, time, button, state); From 4b4ca11f6f3a4508f993804e632898aedcf60a89 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sat, 3 Aug 2024 17:27:13 -0400 Subject: [PATCH 039/519] util/box: Introduce wlr_box_contains_box --- include/wlr/util/box.h | 8 ++++++++ util/box.c | 11 +++++++++++ 2 files changed, 19 insertions(+) diff --git a/include/wlr/util/box.h b/include/wlr/util/box.h index e866b1dfb..64dcbc5cf 100644 --- a/include/wlr/util/box.h +++ b/include/wlr/util/box.h @@ -71,6 +71,14 @@ bool wlr_box_intersection(struct wlr_box *dest, const struct wlr_box *box_a, */ bool wlr_box_contains_point(const struct wlr_box *box, double x, double y); +/** + * Verifies that a box is fully contained within another box. + * + * Returns true if the "smaller" box is fully contained within the "bigger" box. + * If either of the boxes are empty, false is returned. + */ +bool wlr_box_contains_box(const struct wlr_box *bigger, const struct wlr_box *smaller); + /** * Checks whether a box is empty or not. * diff --git a/util/box.c b/util/box.c index a615e2f3e..6a6becf33 100644 --- a/util/box.c +++ b/util/box.c @@ -79,6 +79,17 @@ bool wlr_box_contains_point(const struct wlr_box *box, double x, double y) { } } +bool wlr_box_contains_box(const struct wlr_box *bigger, const struct wlr_box *smaller) { + if (wlr_box_empty(bigger) || wlr_box_empty(smaller)) { + return false; + } + + return smaller->x >= bigger->x && + smaller->x + smaller->width <= bigger->x + bigger->width && + smaller->y >= bigger->y && + smaller->y + smaller->height <= bigger->y + bigger->height; +} + void wlr_box_transform(struct wlr_box *dest, const struct wlr_box *box, enum wl_output_transform transform, int width, int height) { struct wlr_box src = {0}; From 823a64bf7d3aa8f2163a7ada6f01a0f05dd83477 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 30 Jul 2024 19:27:01 -0400 Subject: [PATCH 040/519] wlr_scene: Store update box in update data We'll use it later --- types/scene/wlr_scene.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index b201a9c79..2e2c367e0 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -271,6 +271,7 @@ static void scene_node_opaque_region(struct wlr_scene_node *node, int x, int y, struct scene_update_data { pixman_region32_t *visible; pixman_region32_t *update_region; + struct wlr_box update_box; struct wl_list *outputs; bool calculate_visibility; }; @@ -514,23 +515,22 @@ static void scene_update_region(struct wlr_scene *scene, pixman_region32_init(&visible); pixman_region32_copy(&visible, update_region); + struct pixman_box32 *region_box = pixman_region32_extents(update_region); struct scene_update_data data = { .visible = &visible, .update_region = update_region, + .update_box = { + .x = region_box->x1, + .y = region_box->y1, + .width = region_box->x2 - region_box->x1, + .height = region_box->y2 - region_box->y1, + }, .outputs = &scene->outputs, .calculate_visibility = scene->calculate_visibility, }; - struct pixman_box32 *region_box = pixman_region32_extents(update_region); - struct wlr_box box = { - .x = region_box->x1, - .y = region_box->y1, - .width = region_box->x2 - region_box->x1, - .height = region_box->y2 - region_box->y1, - }; - // update node visibility and output enter/leave events - scene_nodes_in_box(&scene->tree.node, &box, scene_node_update_iterator, &data); + scene_nodes_in_box(&scene->tree.node, &data.update_box, scene_node_update_iterator, &data); pixman_region32_fini(&visible); } From 66d96d244c589840de6addc6eae1ebfb3101b12f Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sat, 3 Aug 2024 13:16:32 -0400 Subject: [PATCH 041/519] wlr_scene: Ensure scene_node_update is updating entire node. The old logic might not update the entire scene node when a node is disabled. It would only consider the damage last time (the damage was based on the visible region of the node). It's important that we update the entire node region because xwayland stacking will depend on this. --- types/scene/wlr_scene.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 2e2c367e0..dc1798d0a 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -540,13 +540,8 @@ static void scene_node_update(struct wlr_scene_node *node, struct wlr_scene *scene = scene_node_get_root(node); int x, y; - if (!wlr_scene_node_coords(node, &x, &y)) { - if (damage) { - scene_update_region(scene, damage); - scene_damage_outputs(scene, damage); - pixman_region32_fini(damage); - } - + bool enabled = wlr_scene_node_coords(node, &x, &y); + if (!enabled && !damage) { return; } @@ -565,7 +560,10 @@ static void scene_node_update(struct wlr_scene_node *node, scene_update_region(scene, &update_region); pixman_region32_fini(&update_region); - scene_node_visibility(node, damage); + if (enabled) { + scene_node_visibility(node, damage); + } + scene_damage_outputs(scene, damage); pixman_region32_fini(damage); } From 1133bc15ceb2c2bcb6df692acda6bfa39a292ab5 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 31 Jul 2024 14:09:33 -0400 Subject: [PATCH 042/519] wlr_scene: Transparently restack xwayland surfaces Scene will now automatically restack xwayland windows. Scene compositors should drop calls to wlr_xwayland_surface_restack() --- types/scene/wlr_scene.c | 46 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index dc1798d0a..623931ea9 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -20,6 +20,12 @@ #include "util/env.h" #include "util/time.h" +#include + +#if WLR_HAS_XWAYLAND +#include +#endif + #define HIGHLIGHT_DAMAGE_FADEOUT_TIME 250 struct wlr_scene_tree *wlr_scene_tree_from_node(struct wlr_scene_node *node) { @@ -274,6 +280,10 @@ struct scene_update_data { struct wlr_box update_box; struct wl_list *outputs; bool calculate_visibility; + +#if WLR_HAS_XWAYLAND + struct wlr_xwayland_surface *restack_above; +#endif }; static uint32_t region_area(pixman_region32_t *region) { @@ -446,6 +456,39 @@ static void update_node_update_outputs(struct wlr_scene_node *node, wl_signal_emit_mutable(&scene_buffer->events.outputs_update, &event); } +#if WLR_HAS_XWAYLAND +static void restack_xwayland_surface(struct wlr_scene_node *node, + struct wlr_box *box, struct scene_update_data *data) { + if (node->type != WLR_SCENE_NODE_BUFFER) { + return; + } + + struct wlr_scene_buffer *buffer_node = wlr_scene_buffer_from_node(node); + struct wlr_scene_surface *surface_node = wlr_scene_surface_try_from_buffer(buffer_node); + if (!surface_node) { + return; + } + + struct wlr_xwayland_surface *xwayland_surface = + wlr_xwayland_surface_try_from_wlr_surface(surface_node->surface); + if (!xwayland_surface || xwayland_surface->override_redirect) { + return; + } + + // ensure this node is entirely inside the update region. If not, we can't + // restack this node since we're not considering the whole thing. + if (wlr_box_contains_box(&data->update_box, box)) { + if (data->restack_above) { + wlr_xwayland_surface_restack(xwayland_surface, data->restack_above, XCB_STACK_MODE_BELOW); + } else { + wlr_xwayland_surface_restack(xwayland_surface, NULL, XCB_STACK_MODE_ABOVE); + } + } + + data->restack_above = xwayland_surface; +} +#endif + static bool scene_node_update_iterator(struct wlr_scene_node *node, int lx, int ly, void *_data) { struct scene_update_data *data = _data; @@ -467,6 +510,9 @@ static bool scene_node_update_iterator(struct wlr_scene_node *node, } update_node_update_outputs(node, data->outputs, NULL, NULL); +#if WLR_HAS_XWAYLAND + restack_xwayland_surface(node, &box, data); +#endif return false; } From 14446216f446d26ce42f043823af50915243deb6 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 7 Aug 2024 16:47:27 -0400 Subject: [PATCH 043/519] wlr_scene: Improve wlr_scene_surface_create documentation --- include/wlr/types/wlr_scene.h | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index b5e2d8390..c96fbf574 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -335,11 +335,29 @@ struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_tree *parent); /** * Add a node displaying a single surface to the scene-graph. * - * The child sub-surfaces are ignored. + * The child sub-surfaces are ignored. See wlr_scene_subsurface_tree_create() * - * wlr_surface_send_enter() and wlr_surface_send_leave() will be called - * automatically based on the position of the surface and outputs in - * the scene. + * Note that this helper does multiple things on behalf of the compositor. Some + * of these include protocol implementations where compositors just need to enable + * the protocols: + * - wp_viewporter + * - wp_presentation + * - wp_fractional_scale_v1 + * - wp_alpha_modifier_v1 + * - wp_linux_drm_syncobj_v1 + * - zwp_linux_dmabuf_v1 presentation feedback with wlr_scene_set_linux_dmabuf_v1() + * + * This helper will also transparently: + * - Send preferred buffer scale* + * - Send preferred buffer transform* + * - Restack xwayland surfaces. See wlr_xwayland_surface_restack()** + * - Send output enter/leave events. + * + * * Note that scale and transform sent to the surface will be based on the output + * which has the largest visible surface area. Intelligent visibility calculations + * influence this. + * ** xwayland stacking order is undefined when the xwayland surfaces do not + * intersect. */ struct wlr_scene_surface *wlr_scene_surface_create(struct wlr_scene_tree *parent, struct wlr_surface *surface); From 1c7e1bcc28b9b1948c697cde1c37311e829cd93f Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 8 Aug 2024 09:14:48 +0200 Subject: [PATCH 044/519] scene: use numbers instead of stars for doc refs I find these slightly more readable (because the whole comment has a stars column on the left side). --- include/wlr/types/wlr_scene.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index c96fbf574..8aa618d35 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -348,15 +348,15 @@ struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_tree *parent); * - zwp_linux_dmabuf_v1 presentation feedback with wlr_scene_set_linux_dmabuf_v1() * * This helper will also transparently: - * - Send preferred buffer scale* - * - Send preferred buffer transform* - * - Restack xwayland surfaces. See wlr_xwayland_surface_restack()** + * - Send preferred buffer scale¹ + * - Send preferred buffer transform¹ + * - Restack xwayland surfaces. See wlr_xwayland_surface_restack()² * - Send output enter/leave events. * - * * Note that scale and transform sent to the surface will be based on the output + * ¹ Note that scale and transform sent to the surface will be based on the output * which has the largest visible surface area. Intelligent visibility calculations * influence this. - * ** xwayland stacking order is undefined when the xwayland surfaces do not + * ² xwayland stacking order is undefined when the xwayland surfaces do not * intersect. */ struct wlr_scene_surface *wlr_scene_surface_create(struct wlr_scene_tree *parent, From df4a1d94e2f4fb03d1c5ddacfb14eca67c732ca2 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 8 Aug 2024 09:16:46 +0200 Subject: [PATCH 045/519] scene: fix typo in wlr_scene_surface_create() docs --- include/wlr/types/wlr_scene.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 8aa618d35..7f3217baa 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -341,7 +341,7 @@ struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_tree *parent); * of these include protocol implementations where compositors just need to enable * the protocols: * - wp_viewporter - * - wp_presentation + * - wp_presentation_time * - wp_fractional_scale_v1 * - wp_alpha_modifier_v1 * - wp_linux_drm_syncobj_v1 From 20997df4168ffc629661fc85ccb584a37f93007b Mon Sep 17 00:00:00 2001 From: Consolatis <40171-Consolatis@users.noreply.gitlab.freedesktop.org> Date: Fri, 17 Feb 2023 08:18:41 +0100 Subject: [PATCH 046/519] xwm: add request_close signal This is especially useful if the compositor wants to support X11 panels to control windows. Without the signal a compositor does have to hook into the user_event_handler callback but that would not change the _NET_SUPPORTED root window property. --- include/wlr/xwayland/xwayland.h | 1 + include/xwayland/xwm.h | 1 + xwayland/xwm.c | 14 ++++++++++++++ 3 files changed, 16 insertions(+) diff --git a/include/wlr/xwayland/xwayland.h b/include/wlr/xwayland/xwayland.h index d656036b1..b38cc74f2 100644 --- a/include/wlr/xwayland/xwayland.h +++ b/include/wlr/xwayland/xwayland.h @@ -171,6 +171,7 @@ struct wlr_xwayland_surface { struct wl_signal request_maximize; struct wl_signal request_fullscreen; struct wl_signal request_activate; + struct wl_signal request_close; struct wl_signal associate; struct wl_signal dissociate; diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h index 762fd4428..80d56e269 100644 --- a/include/xwayland/xwm.h +++ b/include/xwayland/xwm.h @@ -39,6 +39,7 @@ enum atom_name { WM_TAKE_FOCUS, WINDOW, NET_ACTIVE_WINDOW, + NET_CLOSE_WINDOW, NET_WM_MOVERESIZE, NET_SUPPORTING_WM_CHECK, NET_WM_STATE_FOCUSED, diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 1d0d77b45..d0fca4831 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -38,6 +38,7 @@ static const char *const atom_map[ATOM_LAST] = { [WM_TAKE_FOCUS] = "WM_TAKE_FOCUS", [WINDOW] = "WINDOW", [NET_ACTIVE_WINDOW] = "_NET_ACTIVE_WINDOW", + [NET_CLOSE_WINDOW] = "_NET_CLOSE_WINDOW", [NET_WM_MOVERESIZE] = "_NET_WM_MOVERESIZE", [NET_SUPPORTING_WM_CHECK] = "_NET_SUPPORTING_WM_CHECK", [NET_WM_STATE_FOCUSED] = "_NET_WM_STATE_FOCUSED", @@ -206,6 +207,7 @@ static struct wlr_xwayland_surface *xwayland_surface_create( wl_signal_init(&surface->events.request_maximize); wl_signal_init(&surface->events.request_fullscreen); wl_signal_init(&surface->events.request_activate); + wl_signal_init(&surface->events.request_close); wl_signal_init(&surface->events.associate); wl_signal_init(&surface->events.dissociate); wl_signal_init(&surface->events.set_class); @@ -1490,6 +1492,15 @@ static void xwm_handle_net_active_window_message(struct wlr_xwm *xwm, wl_signal_emit_mutable(&surface->events.request_activate, NULL); } +static void xwm_handle_net_close_window_message(struct wlr_xwm *xwm, + xcb_client_message_event_t *ev) { + struct wlr_xwayland_surface *surface = lookup_surface(xwm, ev->window); + if (surface == NULL) { + return; + } + wl_signal_emit_mutable(&surface->events.request_close, NULL); +} + static void pending_startup_id_destroy(struct pending_startup_id *pending) { wl_list_remove(&pending->link); free(pending->msg); @@ -1597,6 +1608,8 @@ static void xwm_handle_client_message(struct wlr_xwm *xwm, xwm_handle_wm_protocols_message(xwm, ev); } else if (ev->type == xwm->atoms[NET_ACTIVE_WINDOW]) { xwm_handle_net_active_window_message(xwm, ev); + } else if (ev->type == xwm->atoms[NET_CLOSE_WINDOW]) { + xwm_handle_net_close_window_message(xwm, ev); } else if (ev->type == xwm->atoms[NET_STARTUP_INFO] || ev->type == xwm->atoms[NET_STARTUP_INFO_BEGIN]) { xwm_handle_net_startup_info_message(xwm, ev); @@ -2269,6 +2282,7 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) { xcb_atom_t supported[] = { xwm->atoms[NET_WM_STATE], xwm->atoms[NET_ACTIVE_WINDOW], + xwm->atoms[NET_CLOSE_WINDOW], xwm->atoms[NET_WM_MOVERESIZE], xwm->atoms[NET_WM_STATE_FOCUSED], xwm->atoms[NET_WM_STATE_MODAL], From 0a388a14f1e1678d9b2c368fd14be7c9a9726a53 Mon Sep 17 00:00:00 2001 From: Consolatis <40171-Consolatis@users.noreply.gitlab.freedesktop.org> Date: Mon, 29 Apr 2024 10:57:08 +0200 Subject: [PATCH 047/519] xwm: supply wlr_xwayland pointer to user_event_handler callback Previously it was supplying a pointer to private struct wlr_xwm which was useless for compositors. The wlr_xwayland pointer in contrast does have a generic data field and thus can be used by compositors to attach their own pointer. Additionally change the return value from int to bool. --- include/wlr/xwayland/xwayland.h | 6 +++--- xwayland/xwm.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/wlr/xwayland/xwayland.h b/include/wlr/xwayland/xwayland.h index b38cc74f2..fb8bca08d 100644 --- a/include/wlr/xwayland/xwayland.h +++ b/include/wlr/xwayland/xwayland.h @@ -57,11 +57,11 @@ struct wlr_xwayland { } events; /** - * Add a custom event handler to xwayland. Return 1 if the event was - * handled or 0 to use the default wlr-xwayland handler. wlr-xwayland will + * Add a custom event handler to xwayland. Return true if the event was + * handled or false to use the default wlr-xwayland handler. wlr-xwayland will * free the event. */ - int (*user_event_handler)(struct wlr_xwm *xwm, xcb_generic_event_t *event); + bool (*user_event_handler)(struct wlr_xwayland *wlr_xwayland, xcb_generic_event_t *event); void *data; diff --git a/xwayland/xwm.c b/xwayland/xwm.c index d0fca4831..d16185d43 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -1747,7 +1747,7 @@ static int x11_event_handler(int fd, uint32_t mask, void *data) { count++; if (xwm->xwayland->user_event_handler && - xwm->xwayland->user_event_handler(xwm, event)) { + xwm->xwayland->user_event_handler(xwm->xwayland, event)) { free(event); continue; } From 08e779bd85b738d993007fa6a7f3f32bebc19649 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Sat, 10 Aug 2024 09:55:17 +0300 Subject: [PATCH 048/519] seat/pointer: reset pressed buttons on focus change This fixes two problems: - A surface could get unexpected release events for buttons pressed while other surface was focused; - Clearing focus while a button is pressed would lead to the button getting "stuck". Fixes: 8730ca9661eaaab83954bca033745b65c60cf4f8 --- types/seat/wlr_seat_pointer.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/types/seat/wlr_seat_pointer.c b/types/seat/wlr_seat_pointer.c index 15075f612..31113c811 100644 --- a/types/seat/wlr_seat_pointer.c +++ b/types/seat/wlr_seat_pointer.c @@ -179,6 +179,9 @@ void wlr_seat_pointer_enter(struct wlr_seat *wlr_seat, seat_client_send_pointer_leave_raw(focused_client, focused_surface); } + // The current surface doesn't know about pressed buttons + wlr_seat->pointer_state.button_count = 0; + // enter the current surface if (client != NULL && surface != NULL) { uint32_t serial = wlr_seat_client_next_serial(client); From 71cc47b859f2f472645eb3e11f316b29c22c6709 Mon Sep 17 00:00:00 2001 From: Alistair Buxton Date: Sat, 10 Aug 2024 15:54:23 +0000 Subject: [PATCH 049/519] Fix Meson version required for C23 support Attempting to build with Meson 1.3.2 (current version in Ubuntu 24.04 LTS) gives the following error: meson.build:1:0: ERROR: Unknown C std ['c23']. This is because C23 support was not added until Meson 1.4.0. See: https://github.com/mesonbuild/meson/blob/1.3.2/mesonbuild/compilers/c.py#L59 https://github.com/mesonbuild/meson/blob/1.4.0/mesonbuild/compilers/c.py#L49 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 956936d76..fcc562c87 100644 --- a/meson.build +++ b/meson.build @@ -5,7 +5,7 @@ project( license: 'MIT', meson_version: '>=1.3', default_options: [ - 'c_std=' + (meson.version().version_compare('>=1.3.0') ? 'c23,c11' : 'c11'), + 'c_std=' + (meson.version().version_compare('>=1.4.0') ? 'c23,c11' : 'c11'), 'warning_level=2', 'werror=true', ], From 6214144735b6b85fa1e191be3afe33d6bea0faee Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Wed, 14 Feb 2024 22:54:33 -0500 Subject: [PATCH 050/519] xwayland: add wlr_xwayland_surface_has_window_type() The infrastructure to read _NET_WM_WINDOW_TYPE already exists in wlroots (it's used for example in wlr_xwayland_or_surface_wants_focus()). But the window type isn't easily accessible to the compositor because the atoms to compare against are private to xwm.c. labwc has recently gone to a fair amount of effort (including opening a whole new xcb connection) just to get the needed window type atoms: https://github.com/labwc/labwc/commit/a04b394e591163505b66706ff9ebcadb26871345 It seems much cleaner to add the remaining few (3) atoms to wlroots and implement a shared function which can be used by any wlroots compositor. v2: naming updates --- include/wlr/xwayland/xwayland.h | 29 +++++++++++++++++++++++++++++ include/xwayland/xwm.h | 3 +++ xwayland/xwm.c | 31 +++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+) diff --git a/include/wlr/xwayland/xwayland.h b/include/wlr/xwayland/xwayland.h index fb8bca08d..c10ce4cd5 100644 --- a/include/wlr/xwayland/xwayland.h +++ b/include/wlr/xwayland/xwayland.h @@ -92,6 +92,27 @@ enum wlr_xwayland_icccm_input_model { WLR_ICCCM_INPUT_MODEL_GLOBAL = 3, }; +/** + * The type of window (_NET_WM_WINDOW_TYPE). See: + * https://specifications.freedesktop.org/wm-spec/latest/ + */ +enum wlr_xwayland_net_wm_window_type { + WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DESKTOP = 0, + WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DOCK, + WLR_XWAYLAND_NET_WM_WINDOW_TYPE_TOOLBAR, + WLR_XWAYLAND_NET_WM_WINDOW_TYPE_MENU, + WLR_XWAYLAND_NET_WM_WINDOW_TYPE_UTILITY, + WLR_XWAYLAND_NET_WM_WINDOW_TYPE_SPLASH, + WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DIALOG, + WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DROPDOWN_MENU, + WLR_XWAYLAND_NET_WM_WINDOW_TYPE_POPUP_MENU, + WLR_XWAYLAND_NET_WM_WINDOW_TYPE_TOOLTIP, + WLR_XWAYLAND_NET_WM_WINDOW_TYPE_NOTIFICATION, + WLR_XWAYLAND_NET_WM_WINDOW_TYPE_COMBO, + WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DND, + WLR_XWAYLAND_NET_WM_WINDOW_TYPE_NORMAL, +}; + /** * An Xwayland user interface component. It has an absolute position in * layout-local coordinates. @@ -293,6 +314,14 @@ void wlr_xwayland_surface_offer_focus(struct wlr_xwayland_surface *xsurface); void wlr_xwayland_surface_ping(struct wlr_xwayland_surface *surface); +/** + * Returns true if the surface has the given window type. + * Note: a surface may have multiple window types set. + */ +bool wlr_xwayland_surface_has_window_type( + const struct wlr_xwayland_surface *xsurface, + enum wlr_xwayland_net_wm_window_type window_type); + /** Metric to guess if an OR window should "receive" focus * * In the pure X setups, window managers usually straight up ignore override diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h index 80d56e269..d3ecf5603 100644 --- a/include/xwayland/xwm.h +++ b/include/xwayland/xwm.h @@ -74,6 +74,9 @@ enum atom_name { NET_WM_WINDOW_TYPE_NOTIFICATION, NET_WM_WINDOW_TYPE_SPLASH, NET_WM_WINDOW_TYPE_DESKTOP, + NET_WM_WINDOW_TYPE_DOCK, + NET_WM_WINDOW_TYPE_TOOLBAR, + NET_WM_WINDOW_TYPE_DIALOG, DND_SELECTION, DND_AWARE, DND_STATUS, diff --git a/xwayland/xwm.c b/xwayland/xwm.c index d16185d43..6b85829dc 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -73,6 +73,9 @@ static const char *const atom_map[ATOM_LAST] = { [NET_WM_WINDOW_TYPE_NOTIFICATION] = "_NET_WM_WINDOW_TYPE_NOTIFICATION", [NET_WM_WINDOW_TYPE_SPLASH] = "_NET_WM_WINDOW_TYPE_SPLASH", [NET_WM_WINDOW_TYPE_DESKTOP] = "_NET_WM_WINDOW_TYPE_DESKTOP", + [NET_WM_WINDOW_TYPE_DOCK] = "_NET_WM_WINDOW_TYPE_DOCK", + [NET_WM_WINDOW_TYPE_TOOLBAR] = "_NET_WM_WINDOW_TYPE_TOOLBAR", + [NET_WM_WINDOW_TYPE_DIALOG] = "_NET_WM_WINDOW_TYPE_DIALOG", [DND_SELECTION] = "XdndSelection", [DND_AWARE] = "XdndAware", [DND_STATUS] = "XdndStatus", @@ -2393,6 +2396,34 @@ void wlr_xwayland_surface_ping(struct wlr_xwayland_surface *surface) { surface->pinging = true; } +bool wlr_xwayland_surface_has_window_type( + const struct wlr_xwayland_surface *xsurface, + enum wlr_xwayland_net_wm_window_type window_type) { + static const enum atom_name atom_names[] = { + [WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DESKTOP] = NET_WM_WINDOW_TYPE_DESKTOP, + [WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DOCK] = NET_WM_WINDOW_TYPE_DOCK, + [WLR_XWAYLAND_NET_WM_WINDOW_TYPE_TOOLBAR] = NET_WM_WINDOW_TYPE_TOOLBAR, + [WLR_XWAYLAND_NET_WM_WINDOW_TYPE_MENU] = NET_WM_WINDOW_TYPE_MENU, + [WLR_XWAYLAND_NET_WM_WINDOW_TYPE_UTILITY] = NET_WM_WINDOW_TYPE_UTILITY, + [WLR_XWAYLAND_NET_WM_WINDOW_TYPE_SPLASH] = NET_WM_WINDOW_TYPE_SPLASH, + [WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DIALOG] = NET_WM_WINDOW_TYPE_DIALOG, + [WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DROPDOWN_MENU] = NET_WM_WINDOW_TYPE_DROPDOWN_MENU, + [WLR_XWAYLAND_NET_WM_WINDOW_TYPE_POPUP_MENU] = NET_WM_WINDOW_TYPE_POPUP_MENU, + [WLR_XWAYLAND_NET_WM_WINDOW_TYPE_TOOLTIP] = NET_WM_WINDOW_TYPE_TOOLTIP, + [WLR_XWAYLAND_NET_WM_WINDOW_TYPE_NOTIFICATION] = NET_WM_WINDOW_TYPE_NOTIFICATION, + [WLR_XWAYLAND_NET_WM_WINDOW_TYPE_COMBO] = NET_WM_WINDOW_TYPE_COMBO, + [WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DND] = NET_WM_WINDOW_TYPE_DND, + [WLR_XWAYLAND_NET_WM_WINDOW_TYPE_NORMAL] = NET_WM_WINDOW_TYPE_NORMAL, + }; + + if (window_type >= 0 && window_type < sizeof(atom_names) / sizeof(atom_names[0])) { + return xwm_atoms_contains(xsurface->xwm, xsurface->window_type, + xsurface->window_type_len, atom_names[window_type]); + } + + return false; +} + bool wlr_xwayland_surface_override_redirect_wants_focus( const struct wlr_xwayland_surface *xsurface) { static const enum atom_name needles[] = { From 3cae2a2c016dbd8cb58201106b14649a4f455694 Mon Sep 17 00:00:00 2001 From: project-repo Date: Sun, 11 Aug 2024 16:39:39 +0000 Subject: [PATCH 051/519] Fix memory leak in drm.c --- backend/drm/drm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 4104f87c7..154aa99d9 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -398,6 +398,7 @@ void finish_drm_resources(struct wlr_drm_backend *drm) { struct wlr_drm_plane *plane = &drm->planes[i]; drm_plane_finish_surface(plane); wlr_drm_format_set_finish(&plane->formats); + free(plane->cursor_sizes); } free(drm->planes); From e209fe2d0596a2e3db37c57aa2b17135fa666da3 Mon Sep 17 00:00:00 2001 From: project-repo Date: Sun, 11 Aug 2024 16:41:19 +0000 Subject: [PATCH 052/519] Fix memory leak in xwayland.c --- xwayland/xwayland.c | 1 + 1 file changed, 1 insertion(+) diff --git a/xwayland/xwayland.c b/xwayland/xwayland.c index c4ca9ae44..23c21b438 100644 --- a/xwayland/xwayland.c +++ b/xwayland/xwayland.c @@ -91,6 +91,7 @@ void wlr_xwayland_destroy(struct wlr_xwayland *xwayland) { } xwayland->server = NULL; wlr_xwayland_shell_v1_destroy(xwayland->shell_v1); + xwm_destroy(xwayland->xwm); free(xwayland); } From adf9d8b0bec7a9e25e1f124f1bcf326102f5b406 Mon Sep 17 00:00:00 2001 From: Consolatis <40171-Consolatis@users.noreply.gitlab.freedesktop.org> Date: Mon, 12 Aug 2024 22:06:01 +0200 Subject: [PATCH 053/519] ext-foreign-toplevel-list: use correct interface and add missing handler Without this patch, a client calling handle.destroy() will trigger an assert in libwayland due to a NULL pointer for the destroy handler. Also implement a missing .destroy handler for the manager itself and delay destruction of the manager resource from the .stop handler to the .destroy handler. --- types/wlr_ext_foreign_toplevel_list_v1.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/types/wlr_ext_foreign_toplevel_list_v1.c b/types/wlr_ext_foreign_toplevel_list_v1.c index 6c059e64e..22a126f84 100644 --- a/types/wlr_ext_foreign_toplevel_list_v1.c +++ b/types/wlr_ext_foreign_toplevel_list_v1.c @@ -12,14 +12,18 @@ #define FOREIGN_TOPLEVEL_LIST_V1_VERSION 1 -static const struct ext_foreign_toplevel_list_v1_interface toplevel_handle_impl; +static const struct ext_foreign_toplevel_handle_v1_interface toplevel_handle_impl; static void foreign_toplevel_handle_destroy(struct wl_client *client, struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &ext_foreign_toplevel_handle_v1_interface, + &toplevel_handle_impl)); + wl_resource_destroy(resource); } -static const struct ext_foreign_toplevel_list_v1_interface toplevel_handle_impl = { +static const struct ext_foreign_toplevel_handle_v1_interface toplevel_handle_impl = { .destroy = foreign_toplevel_handle_destroy, }; @@ -191,12 +195,23 @@ static void foreign_toplevel_list_handle_stop(struct wl_client *client, &foreign_toplevel_list_impl)); ext_foreign_toplevel_list_v1_send_finished(resource); + wl_list_remove(wl_resource_get_link(resource)); + wl_list_init(wl_resource_get_link(resource)); +} + +static void foreign_toplevel_list_handle_destroy(struct wl_client *client, + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &ext_foreign_toplevel_list_v1_interface, + &foreign_toplevel_list_impl)); + wl_resource_destroy(resource); } static const struct ext_foreign_toplevel_list_v1_interface foreign_toplevel_list_impl = { - .stop = foreign_toplevel_list_handle_stop + .stop = foreign_toplevel_list_handle_stop, + .destroy = foreign_toplevel_list_handle_destroy }; static void foreign_toplevel_list_resource_destroy( From e6dbe4580e19447abb80e7e4b7b75744dca6d1e5 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 12 Aug 2024 21:10:54 +0200 Subject: [PATCH 054/519] render/gles2: check for DRM_CAP_SYNCOBJ_TIMELINE Before advertising support for timelines, check for DRM_CAP_SYNCOBJ_TIMELINE. Without this, the user cannot create/import drm_syncobj timelines. References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4715#note_2523517 --- render/gles2/renderer.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c index 3eeb1ba9c..4cdbf3f6e 100644 --- a/render/gles2/renderer.c +++ b/render/gles2/renderer.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "render/egl.h" #include "render/gles2.h" #include "render/pixel_format.h" @@ -684,8 +685,12 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { get_gles2_shm_formats(renderer, &renderer->shm_texture_formats); - renderer->wlr_renderer.features.timeline = - egl->procs.eglDupNativeFenceFDANDROID && egl->procs.eglWaitSyncKHR; + int drm_fd = wlr_renderer_get_drm_fd(&renderer->wlr_renderer); + uint64_t cap_syncobj_timeline; + if (drm_fd >= 0 && drmGetCap(drm_fd, DRM_CAP_SYNCOBJ_TIMELINE, &cap_syncobj_timeline) == 0) { + renderer->wlr_renderer.features.timeline = egl->procs.eglDupNativeFenceFDANDROID && + egl->procs.eglWaitSyncKHR && cap_syncobj_timeline != 0; + } return &renderer->wlr_renderer; From 2c64f36e8886d1f26daeb2a4ee79f3f9dd3d4c85 Mon Sep 17 00:00:00 2001 From: zhoulei Date: Tue, 13 Aug 2024 11:23:47 +0800 Subject: [PATCH 055/519] xwayland/xwm: listen shell destroy signal Otherwise we got invaild write in wl_list_remove. Fixes: e209fe2d0 ("Fix memory leak in xwayland.c") Signed-off-by: zhoulei --- include/xwayland/xwm.h | 1 + xwayland/xwm.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h index d3ecf5603..364095399 100644 --- a/include/xwayland/xwm.h +++ b/include/xwayland/xwm.h @@ -140,6 +140,7 @@ struct wlr_xwm { struct wl_listener compositor_new_surface; struct wl_listener compositor_destroy; struct wl_listener shell_v1_new_surface; + struct wl_listener shell_v1_destroy; struct wl_listener seat_set_selection; struct wl_listener seat_set_primary_selection; struct wl_listener seat_start_drag; diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 6b85829dc..f25e4734b 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -1859,6 +1859,16 @@ static void handle_shell_v1_new_surface(struct wl_listener *listener, } } +static void handle_shell_v1_destroy(struct wl_listener *listener, + void *data) { + struct wlr_xwm *xwm = + wl_container_of(listener, xwm, shell_v1_destroy); + wl_list_remove(&xwm->shell_v1_new_surface.link); + wl_list_remove(&xwm->shell_v1_destroy.link); + wl_list_init(&xwm->shell_v1_new_surface.link); + wl_list_init(&xwm->shell_v1_destroy.link); +} + void wlr_xwayland_surface_activate(struct wlr_xwayland_surface *xsurface, bool activated) { struct wlr_xwayland_surface *focused = xsurface->xwm->focus_surface; @@ -1986,6 +1996,7 @@ void xwm_destroy(struct wlr_xwm *xwm) { wl_list_remove(&xwm->compositor_new_surface.link); wl_list_remove(&xwm->compositor_destroy.link); wl_list_remove(&xwm->shell_v1_new_surface.link); + wl_list_remove(&xwm->shell_v1_destroy.link); xcb_disconnect(xwm->xcb_conn); struct pending_startup_id *pending, *next; @@ -2331,6 +2342,9 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) { xwm->shell_v1_new_surface.notify = handle_shell_v1_new_surface; wl_signal_add(&xwayland->shell_v1->events.new_surface, &xwm->shell_v1_new_surface); + xwm->shell_v1_destroy.notify = handle_shell_v1_destroy; + wl_signal_add(&xwayland->shell_v1->events.destroy, + &xwm->shell_v1_destroy); xwm_create_wm_window(xwm); From 6c8eabcecdb08fa6451e277ace91245b0a6d0427 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Wed, 14 Aug 2024 15:49:09 +0300 Subject: [PATCH 056/519] xdg-foreign: clean up This commit removes extra wlr_xdg_toplevel_set_parent() calls, simplifies wlr_surface->wlr_xdg_toplevel conversion logic, makes related structures store wlr_xdg_toplevel objects directly instead of wlr_surface objects, and improves the code style. --- include/wlr/types/wlr_xdg_foreign_registry.h | 2 +- include/wlr/types/wlr_xdg_foreign_v1.h | 2 +- include/wlr/types/wlr_xdg_foreign_v2.h | 2 +- types/wlr_xdg_foreign_v1.c | 62 ++++++++----------- types/wlr_xdg_foreign_v2.c | 64 ++++++++------------ 5 files changed, 54 insertions(+), 78 deletions(-) diff --git a/include/wlr/types/wlr_xdg_foreign_registry.h b/include/wlr/types/wlr_xdg_foreign_registry.h index 54c91e4d7..c240c8f3a 100644 --- a/include/wlr/types/wlr_xdg_foreign_registry.h +++ b/include/wlr/types/wlr_xdg_foreign_registry.h @@ -33,7 +33,7 @@ struct wlr_xdg_foreign_exported { struct wl_list link; // wlr_xdg_foreign_registry.exported_surfaces struct wlr_xdg_foreign_registry *registry; - struct wlr_surface *surface; + struct wlr_xdg_toplevel *toplevel; char handle[WLR_XDG_FOREIGN_HANDLE_SIZE]; diff --git a/include/wlr/types/wlr_xdg_foreign_v1.h b/include/wlr/types/wlr_xdg_foreign_v1.h index bb26fcfca..7b31a61ab 100644 --- a/include/wlr/types/wlr_xdg_foreign_v1.h +++ b/include/wlr/types/wlr_xdg_foreign_v1.h @@ -50,7 +50,7 @@ struct wlr_xdg_imported_v1 { struct wlr_xdg_imported_child_v1 { struct wlr_xdg_imported_v1 *imported; - struct wlr_surface *surface; + struct wlr_xdg_toplevel *toplevel; struct wl_list link; // wlr_xdg_imported_v1.children diff --git a/include/wlr/types/wlr_xdg_foreign_v2.h b/include/wlr/types/wlr_xdg_foreign_v2.h index d82854b46..f5e14c700 100644 --- a/include/wlr/types/wlr_xdg_foreign_v2.h +++ b/include/wlr/types/wlr_xdg_foreign_v2.h @@ -50,7 +50,7 @@ struct wlr_xdg_imported_v2 { struct wlr_xdg_imported_child_v2 { struct wlr_xdg_imported_v2 *imported; - struct wlr_surface *surface; + struct wlr_xdg_toplevel *toplevel; struct wl_list link; // wlr_xdg_imported_v2.children diff --git a/types/wlr_xdg_foreign_v1.c b/types/wlr_xdg_foreign_v1.c index 12cd4fc09..9fb2b235f 100644 --- a/types/wlr_xdg_foreign_v1.c +++ b/types/wlr_xdg_foreign_v1.c @@ -1,4 +1,3 @@ - #include #include #include @@ -26,15 +25,17 @@ static void xdg_imported_handle_destroy(struct wl_client *client, wl_resource_destroy(resource); } -static struct wlr_xdg_toplevel *verify_is_toplevel(struct wl_resource *resource, +static struct wlr_xdg_toplevel *get_toplevel(struct wl_resource *resource, struct wlr_surface *surface) { - struct wlr_xdg_surface *xdg_surface = wlr_xdg_surface_try_from_wlr_surface(surface); - if (xdg_surface == NULL || xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) { + // Note: xdg_surface and xdg_toplevel are never inert, so if this fails, + // the surface isn't a toplevel + struct wlr_xdg_toplevel *toplevel = wlr_xdg_toplevel_try_from_wlr_surface(surface); + if (toplevel == NULL) { wl_resource_post_error(resource, -1, "surface must be an xdg_toplevel"); return NULL; } - return xdg_surface->toplevel; + return toplevel; } static void destroy_imported_child(struct wlr_xdg_imported_child_v1 *child) { @@ -51,7 +52,7 @@ static void handle_child_xdg_toplevel_destroy( destroy_imported_child(child); } -static void handle_xdg_toplevel_set_parent( +static void handle_child_xdg_toplevel_set_parent( struct wl_listener *listener, void *data) { struct wlr_xdg_imported_child_v1 *child = wl_container_of(listener, child, xdg_toplevel_set_parent); @@ -59,34 +60,29 @@ static void handle_xdg_toplevel_set_parent( } static void xdg_imported_handle_set_parent_of(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *child_resource) { + struct wl_resource *resource, struct wl_resource *child_resource) { struct wlr_xdg_imported_v1 *imported = xdg_imported_from_resource(resource); if (imported == NULL) { return; } - struct wlr_surface *wlr_surface = imported->exported->surface; - struct wlr_surface *wlr_surface_child = - wlr_surface_from_resource(child_resource); - struct wlr_xdg_toplevel *child_toplevel = - verify_is_toplevel(resource, wlr_surface_child); + struct wlr_xdg_toplevel *toplevel = imported->exported->toplevel; + struct wlr_surface *child_surface = wlr_surface_from_resource(child_resource); + + struct wlr_xdg_toplevel *child_toplevel = get_toplevel(resource, child_surface); if (!child_toplevel) { return; } - struct wlr_xdg_surface *surface = - wlr_xdg_surface_try_from_wlr_surface(wlr_surface); - - if (!surface->surface->mapped) { + if (!toplevel->base->surface->mapped) { wlr_xdg_toplevel_set_parent(child_toplevel, NULL); return; } struct wlr_xdg_imported_child_v1 *child; wl_list_for_each(child, &imported->children, link) { - if (child->surface == wlr_surface_child) { + if (child->toplevel == child_toplevel) { return; } } @@ -96,19 +92,19 @@ static void xdg_imported_handle_set_parent_of(struct wl_client *client, wl_client_post_no_memory(client); return; } - child->surface = wlr_surface_child; - child->xdg_toplevel_destroy.notify = handle_child_xdg_toplevel_destroy; - child->xdg_toplevel_set_parent.notify = handle_xdg_toplevel_set_parent; - if (!wlr_xdg_toplevel_set_parent(child_toplevel, surface->toplevel)) { - wl_resource_post_error(surface->toplevel->resource, + child->toplevel = child_toplevel; + child->xdg_toplevel_destroy.notify = handle_child_xdg_toplevel_destroy; + child->xdg_toplevel_set_parent.notify = handle_child_xdg_toplevel_set_parent; + + if (!wlr_xdg_toplevel_set_parent(child_toplevel, toplevel)) { + wl_resource_post_error(toplevel->resource, XDG_TOPLEVEL_ERROR_INVALID_PARENT, "a toplevel cannot be a parent of itself or its ancestor"); free(child); return; } - wlr_xdg_toplevel_set_parent(child_toplevel, surface->toplevel); wl_signal_add(&child_toplevel->events.destroy, &child->xdg_toplevel_destroy); wl_signal_add(&child_toplevel->events.set_parent, &child->xdg_toplevel_set_parent); @@ -152,10 +148,7 @@ static void destroy_imported(struct wlr_xdg_imported_v1 *imported) { imported->exported = NULL; struct wlr_xdg_imported_child_v1 *child, *child_tmp; wl_list_for_each_safe(child, child_tmp, &imported->children, link) { - struct wlr_xdg_surface *xdg_child = - wlr_xdg_surface_try_from_wlr_surface(child->surface); - assert(xdg_child != NULL); - wlr_xdg_toplevel_set_parent(xdg_child->toplevel, NULL); + wlr_xdg_toplevel_set_parent(child->toplevel, NULL); } wl_list_remove(&imported->exported_destroyed.link); @@ -194,15 +187,12 @@ static void handle_xdg_toplevel_destroy(struct wl_listener *listener, void *data } static void xdg_exporter_handle_export(struct wl_client *wl_client, - struct wl_resource *client_resource, - uint32_t id, - struct wl_resource *surface_resource) { + struct wl_resource *client_resource, uint32_t id, struct wl_resource *surface_resource) { struct wlr_xdg_foreign_v1 *foreign = xdg_foreign_from_exporter_resource(client_resource); struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); - struct wlr_xdg_toplevel *xdg_toplevel = - verify_is_toplevel(client_resource, surface); + struct wlr_xdg_toplevel *xdg_toplevel = get_toplevel(client_resource, surface); if (!xdg_toplevel) { return; } @@ -219,7 +209,7 @@ static void xdg_exporter_handle_export(struct wl_client *wl_client, return; } - exported->base.surface = surface; + exported->base.toplevel = xdg_toplevel; exported->resource = wl_resource_create(wl_client, &zxdg_exported_v1_interface, wl_resource_get_version(client_resource), id); if (exported->resource == NULL) { @@ -291,9 +281,7 @@ static void xdg_imported_handle_exported_destroy(struct wl_listener *listener, } static void xdg_importer_handle_import(struct wl_client *wl_client, - struct wl_resource *client_resource, - uint32_t id, - const char *handle) { + struct wl_resource *client_resource, uint32_t id, const char *handle) { struct wlr_xdg_foreign_v1 *foreign = xdg_foreign_from_importer_resource(client_resource); diff --git a/types/wlr_xdg_foreign_v2.c b/types/wlr_xdg_foreign_v2.c index 4cfbe77b6..eef93175e 100644 --- a/types/wlr_xdg_foreign_v2.c +++ b/types/wlr_xdg_foreign_v2.c @@ -26,20 +26,20 @@ static void xdg_imported_handle_destroy(struct wl_client *client, wl_resource_destroy(resource); } -static struct wlr_xdg_toplevel *verify_is_toplevel(struct wl_resource *resource, +static struct wlr_xdg_toplevel *get_toplevel(struct wl_resource *resource, struct wlr_surface *surface) { - // Note: the error codes are the same for zxdg_exporter_v2 and - // zxdg_importer_v2 - - struct wlr_xdg_surface *xdg_surface = wlr_xdg_surface_try_from_wlr_surface(surface); - if (xdg_surface == NULL || xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) { - wl_resource_post_error(resource, - ZXDG_EXPORTER_V2_ERROR_INVALID_SURFACE, + // Note: xdg_surface and xdg_toplevel are never inert, so if this fails, + // the surface isn't a toplevel + struct wlr_xdg_toplevel *toplevel = wlr_xdg_toplevel_try_from_wlr_surface(surface); + if (toplevel == NULL) { + // Note: the error codes are the same for zxdg_exporter_v2 and + // zxdg_importer_v2 + wl_resource_post_error(resource, ZXDG_EXPORTER_V2_ERROR_INVALID_SURFACE, "surface must be an xdg_toplevel"); return NULL; } - return xdg_surface->toplevel; + return toplevel; } static void destroy_imported_child(struct wlr_xdg_imported_child_v2 *child) { @@ -56,7 +56,7 @@ static void handle_child_xdg_toplevel_destroy( destroy_imported_child(child); } -static void handle_xdg_toplevel_set_parent( +static void handle_child_xdg_toplevel_set_parent( struct wl_listener *listener, void *data) { struct wlr_xdg_imported_child_v2 *child = wl_container_of(listener, child, xdg_toplevel_set_parent); @@ -64,32 +64,29 @@ static void handle_xdg_toplevel_set_parent( } static void xdg_imported_handle_set_parent_of(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *child_resource) { + struct wl_resource *resource, struct wl_resource *child_resource) { struct wlr_xdg_imported_v2 *imported = xdg_imported_from_resource(resource); if (imported == NULL) { return; } - struct wlr_surface *wlr_surface = imported->exported->surface; - struct wlr_surface *wlr_surface_child = - wlr_surface_from_resource(child_resource); - struct wlr_xdg_surface *surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface); - struct wlr_xdg_toplevel *child_toplevel = - verify_is_toplevel(resource, wlr_surface_child); + struct wlr_xdg_toplevel *toplevel = imported->exported->toplevel; + struct wlr_surface *child_surface = wlr_surface_from_resource(child_resource); + + struct wlr_xdg_toplevel *child_toplevel = get_toplevel(resource, child_surface); if (!child_toplevel) { return; } - if (!surface->surface->mapped) { + if (!toplevel->base->surface->mapped) { wlr_xdg_toplevel_set_parent(child_toplevel, NULL); return; } struct wlr_xdg_imported_child_v2 *child; wl_list_for_each(child, &imported->children, link) { - if (child->surface == wlr_surface_child) { + if (child->toplevel == child_toplevel) { return; } } @@ -99,19 +96,18 @@ static void xdg_imported_handle_set_parent_of(struct wl_client *client, wl_client_post_no_memory(client); return; } - child->surface = wlr_surface_child; + child->toplevel = child_toplevel; child->xdg_toplevel_destroy.notify = handle_child_xdg_toplevel_destroy; - child->xdg_toplevel_set_parent.notify = handle_xdg_toplevel_set_parent; + child->xdg_toplevel_set_parent.notify = handle_child_xdg_toplevel_set_parent; - if (!wlr_xdg_toplevel_set_parent(child_toplevel, surface->toplevel)) { - wl_resource_post_error(surface->toplevel->resource, + if (!wlr_xdg_toplevel_set_parent(child_toplevel, toplevel)) { + wl_resource_post_error(toplevel->resource, XDG_TOPLEVEL_ERROR_INVALID_PARENT, "a toplevel cannot be a parent of itself or its ancestor"); free(child); return; } - wlr_xdg_toplevel_set_parent(child_toplevel, surface->toplevel); wl_signal_add(&child_toplevel->events.destroy, &child->xdg_toplevel_destroy); wl_signal_add(&child_toplevel->events.set_parent, &child->xdg_toplevel_set_parent); @@ -155,10 +151,7 @@ static void destroy_imported(struct wlr_xdg_imported_v2 *imported) { imported->exported = NULL; struct wlr_xdg_imported_child_v2 *child, *child_tmp; wl_list_for_each_safe(child, child_tmp, &imported->children, link) { - struct wlr_xdg_surface *xdg_child = - wlr_xdg_surface_try_from_wlr_surface(child->surface); - assert(xdg_child != NULL); - wlr_xdg_toplevel_set_parent(xdg_child->toplevel, NULL); + wlr_xdg_toplevel_set_parent(child->toplevel, NULL); } wl_list_remove(&imported->exported_destroyed.link); @@ -197,15 +190,12 @@ static void handle_xdg_toplevel_destroy(struct wl_listener *listener, void *data } static void xdg_exporter_handle_export(struct wl_client *wl_client, - struct wl_resource *client_resource, - uint32_t id, - struct wl_resource *surface_resource) { + struct wl_resource *client_resource, uint32_t id, struct wl_resource *surface_resource) { struct wlr_xdg_foreign_v2 *foreign = xdg_foreign_from_exporter_resource(client_resource); struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); - struct wlr_xdg_toplevel *xdg_toplevel = - verify_is_toplevel(client_resource, surface); + struct wlr_xdg_toplevel *xdg_toplevel = get_toplevel(client_resource, surface); if (!xdg_toplevel) { return; } @@ -222,7 +212,7 @@ static void xdg_exporter_handle_export(struct wl_client *wl_client, return; } - exported->base.surface = surface; + exported->base.toplevel = xdg_toplevel; exported->resource = wl_resource_create(wl_client, &zxdg_exported_v2_interface, wl_resource_get_version(client_resource), id); if (exported->resource == NULL) { @@ -294,9 +284,7 @@ static void xdg_imported_handle_exported_destroy(struct wl_listener *listener, } static void xdg_importer_handle_import(struct wl_client *wl_client, - struct wl_resource *client_resource, - uint32_t id, - const char *handle) { + struct wl_resource *client_resource, uint32_t id, const char *handle) { struct wlr_xdg_foreign_v2 *foreign = xdg_foreign_from_importer_resource(client_resource); From c52e01e85f4cdeff4aba234993223a232b9cdfa4 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Wed, 14 Aug 2024 16:21:19 +0300 Subject: [PATCH 057/519] xdg-popup: don't set a role resource destroy handler wlr_xdg_surface tracks role resource destruction itself. --- types/xdg_shell/wlr_xdg_popup.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/types/xdg_shell/wlr_xdg_popup.c b/types/xdg_shell/wlr_xdg_popup.c index 082e06fbb..a718b4528 100644 --- a/types/xdg_shell/wlr_xdg_popup.c +++ b/types/xdg_shell/wlr_xdg_popup.c @@ -361,15 +361,6 @@ static const struct wlr_surface_synced_impl surface_synced_impl = { .state_size = sizeof(struct wlr_xdg_popup_state), }; -static void xdg_popup_handle_resource_destroy(struct wl_resource *resource) { - struct wlr_xdg_popup *popup = - wlr_xdg_popup_from_resource(resource); - if (popup == NULL) { - return; - } - wlr_xdg_popup_destroy(popup); -} - void create_xdg_popup(struct wlr_xdg_surface *surface, struct wlr_xdg_surface *parent, struct wlr_xdg_positioner *positioner, uint32_t id) { if (!wlr_xdg_positioner_is_complete(positioner)) { @@ -409,8 +400,7 @@ void create_xdg_popup(struct wlr_xdg_surface *surface, struct wlr_xdg_surface *p goto error_synced; } wl_resource_set_implementation(surface->popup->resource, - &xdg_popup_implementation, surface->popup, - xdg_popup_handle_resource_destroy); + &xdg_popup_implementation, surface->popup, NULL); surface->role = WLR_XDG_SURFACE_ROLE_POPUP; From 78dfa4f06d06e5442ad47e25ccd98522795a5896 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 13 Aug 2024 16:47:12 -0400 Subject: [PATCH 058/519] wlr_scene: Funnel all damage operations through scene_output_damage We want to add logic to this function later --- types/scene/wlr_scene.c | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 623931ea9..52fed109c 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -328,6 +328,22 @@ static void transform_output_box(struct wlr_box *box, const struct render_data * wlr_box_transform(box, box, transform, data->trans_width, data->trans_height); } +static void scene_output_damage(struct wlr_scene_output *scene_output, + const pixman_region32_t *region) { + if (wlr_damage_ring_add(&scene_output->damage_ring, region)) { + wlr_output_schedule_frame(scene_output->output); + } +} + +static void scene_output_damage_whole(struct wlr_scene_output *scene_output) { + struct wlr_damage_ring *ring = &scene_output->damage_ring; + + pixman_region32_t damage; + pixman_region32_init_rect(&damage, 0, 0, ring->width, ring->height); + scene_output_damage(scene_output, &damage); + pixman_region32_fini(&damage); +} + static void scene_damage_outputs(struct wlr_scene *scene, pixman_region32_t *damage) { if (!pixman_region32_not_empty(damage)) { return; @@ -341,9 +357,7 @@ static void scene_damage_outputs(struct wlr_scene *scene, pixman_region32_t *dam pixman_region32_translate(&output_damage, -scene_output->x, -scene_output->y); scale_output_damage(&output_damage, scene_output->output->scale); - if (wlr_damage_ring_add(&scene_output->damage_ring, &output_damage)) { - wlr_output_schedule_frame(scene_output->output); - } + scene_output_damage(scene_output, &output_damage); pixman_region32_fini(&output_damage); } } @@ -866,9 +880,7 @@ void wlr_scene_buffer_set_buffer_with_options(struct wlr_scene_buffer *scene_buf pixman_region32_translate(&output_damage, (int)round((lx - scene_output->x) * output_scale), (int)round((ly - scene_output->y) * output_scale)); - if (wlr_damage_ring_add(&scene_output->damage_ring, &output_damage)) { - wlr_output_schedule_frame(scene_output->output); - } + scene_output_damage(scene_output, &output_damage); pixman_region32_fini(&output_damage); } @@ -1300,7 +1312,7 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren struct wlr_texture *texture = scene_buffer_get_texture(scene_buffer, data->output->output->renderer); if (texture == NULL) { - wlr_damage_ring_add(&data->output->damage_ring, &render_region); + scene_output_damage(data->output, &render_region); break; } @@ -1389,8 +1401,7 @@ static void scene_node_output_update(struct wlr_scene_node *node, static void scene_output_update_geometry(struct wlr_scene_output *scene_output, bool force_update) { - wlr_damage_ring_add_whole(&scene_output->damage_ring); - wlr_output_schedule_frame(scene_output->output); + scene_output_damage_whole(scene_output); scene_node_output_update(&scene_output->scene->tree.node, &scene_output->scene->outputs, NULL, force_update ? scene_output : NULL); @@ -1444,9 +1455,7 @@ static void scene_output_handle_damage(struct wl_listener *listener, void *data) struct wlr_scene_output *scene_output = wl_container_of(listener, scene_output, output_damage); struct wlr_output_event_damage *event = data; - if (wlr_damage_ring_add(&scene_output->damage_ring, event->damage)) { - wlr_output_schedule_frame(scene_output->output); - } + scene_output_damage(scene_output, event->damage); } static void scene_output_handle_needs_frame(struct wl_listener *listener, void *data) { @@ -1837,7 +1846,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, if (state->committed & WLR_OUTPUT_STATE_TRANSFORM) { if (render_data.transform != state->transform) { - wlr_damage_ring_add_whole(&scene_output->damage_ring); + scene_output_damage_whole(scene_output); } render_data.transform = state->transform; @@ -1845,7 +1854,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, if (state->committed & WLR_OUTPUT_STATE_SCALE) { if (render_data.scale != state->scale) { - wlr_damage_ring_add_whole(&scene_output->damage_ring); + scene_output_damage_whole(scene_output); } render_data.scale = state->scale; @@ -1879,7 +1888,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, render_data.trans_width, render_data.trans_height); if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_RERENDER) { - wlr_damage_ring_add_whole(&scene_output->damage_ring); + scene_output_damage_whole(scene_output); } struct timespec now; @@ -1916,7 +1925,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, } } - wlr_damage_ring_add(&scene_output->damage_ring, &acc_damage); + scene_output_damage(scene_output, &acc_damage); pixman_region32_fini(&acc_damage); } From 147c5c37e3fdf287818f3b1683f99d6d6a315798 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 14 Aug 2024 11:23:01 -0400 Subject: [PATCH 059/519] wlr_scene: Immediately apply pending output commit damage There were two problems with the old implementation: 1. wlr_scene_output_commit would bail early if a frame wasn't requested and there was no commit damage, however commit damage could never accumulate until rendering happens. The check was subtly wrong as a result. 2. Previously, we would fill the pending commit damage based on the current state of the damage ring. However, during direct scanout, the damage would accumulate which would mean we would submit damage from previous frames even if we didn't need to. --- types/scene/wlr_scene.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 52fed109c..140d1b147 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -332,6 +332,27 @@ static void scene_output_damage(struct wlr_scene_output *scene_output, const pixman_region32_t *region) { if (wlr_damage_ring_add(&scene_output->damage_ring, region)) { wlr_output_schedule_frame(scene_output->output); + + struct wlr_output *output = scene_output->output; + enum wl_output_transform transform = + wlr_output_transform_invert(scene_output->output->transform); + + int width = output->width; + int height = output->height; + if (transform & WL_OUTPUT_TRANSFORM_90) { + width = output->height; + height = output->width; + } + + pixman_region32_t frame_damage; + pixman_region32_init(&frame_damage); + wlr_region_transform(&frame_damage, region, transform, width, height); + + pixman_region32_union(&scene_output->pending_commit_damage, + &scene_output->pending_commit_damage, &frame_damage); + pixman_region32_intersect_rect(&scene_output->pending_commit_damage, + &scene_output->pending_commit_damage, 0, 0, output->width, output->height); + pixman_region32_fini(&frame_damage); } } @@ -1654,14 +1675,6 @@ static void output_state_apply_damage(const struct render_data *data, struct wlr_output_state *state) { struct wlr_scene_output *output = data->output; - pixman_region32_t frame_damage; - pixman_region32_init(&frame_damage); - pixman_region32_copy(&frame_damage, &output->damage_ring.current); - transform_output_damage(&frame_damage, data); - pixman_region32_union(&output->pending_commit_damage, - &output->pending_commit_damage, &frame_damage); - pixman_region32_fini(&frame_damage); - wlr_output_state_set_damage(state, &output->pending_commit_damage); } From 3e1358fec9caf5a0a698535ac942b1ee4fb8a378 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 17 Jul 2024 00:13:12 -0400 Subject: [PATCH 060/519] wlr_scene: Inline output_state_apply_damage --- types/scene/wlr_scene.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 140d1b147..d3be81977 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1671,13 +1671,6 @@ static bool construct_render_list_iterator(struct wlr_scene_node *node, return false; } -static void output_state_apply_damage(const struct render_data *data, - struct wlr_output_state *state) { - struct wlr_scene_output *output = data->output; - - wlr_output_state_set_damage(state, &output->pending_commit_damage); -} - static void scene_buffer_send_dmabuf_feedback(const struct wlr_scene *scene, struct wlr_scene_buffer *scene_buffer, const struct wlr_linux_dmabuf_feedback_v1_init_options *options) { @@ -1942,7 +1935,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, pixman_region32_fini(&acc_damage); } - output_state_apply_damage(&render_data, state); + wlr_output_state_set_damage(state, &scene_output->pending_commit_damage); // We only want to try direct scanout if: // - There is only one entry in the render list From 14e1987f5034907a673ed31f8fa18504c9a2edd3 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 13 Aug 2024 16:44:50 -0400 Subject: [PATCH 061/519] wlr_scene: Don't special case swapchain buffers This fixes direct scanout VRR. As direct scanout buffers are not part of the swapchain, we would mistakenly union instead of subtract the damage meaning it will just accumulate indefinitely. The reason for this existing in the first place is for compositors that might want to sidestep scene and commit their own buffers to the output. In this case, scene could theoretically acknowledge that and update the damage. Except, this really didn't work because WLR_OUTPUT_STATE_DAMAGE would need to be defined which is optional. This patch also properly acknowledges commits without damage. In the use case of a weird compositor that might want to sidestep scene, they can just trash the damage ring themselves. Fixes: #3871 --- types/scene/wlr_scene.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index d3be81977..d1d890184 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1447,22 +1447,13 @@ static void scene_output_handle_commit(struct wl_listener *listener, void *data) // if the output has been committed with a certain damage, we know that region // will be acknowledged by the backend so we don't need to keep track of it // anymore - if (state->committed & WLR_OUTPUT_STATE_DAMAGE) { - bool tracking_buffer = false; - struct wlr_damage_ring_buffer *buffer; - wl_list_for_each(buffer, &scene_output->damage_ring.buffers, link) { - if (buffer->buffer == state->buffer) { - tracking_buffer = true; - break; - } - } - - if (tracking_buffer) { + if (state->committed & WLR_OUTPUT_STATE_BUFFER) { + if (state->committed & WLR_OUTPUT_STATE_DAMAGE) { pixman_region32_subtract(&scene_output->pending_commit_damage, &scene_output->pending_commit_damage, &state->damage); } else { - pixman_region32_union(&scene_output->pending_commit_damage, - &scene_output->pending_commit_damage, &state->damage); + pixman_region32_fini(&scene_output->pending_commit_damage); + pixman_region32_init(&scene_output->pending_commit_damage); } } From 4f1104654fc57449b7b6c13967e400aa130ea3f9 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 14 Aug 2024 11:23:30 -0400 Subject: [PATCH 062/519] wlr_scene: Fix WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT when output is transformed --- types/scene/wlr_scene.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index d1d890184..b8bfe428f 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -2066,11 +2066,18 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, int64_t time_diff_ms = timespec_to_msec(&time_diff); float alpha = 1.0 - (double)time_diff_ms / HIGHLIGHT_DAMAGE_FADEOUT_TIME; + pixman_region32_t clip; + pixman_region32_init(&clip); + pixman_region32_copy(&clip, &damage->region); + transform_output_damage(&clip, &render_data); + wlr_render_pass_add_rect(render_pass, &(struct wlr_render_rect_options){ .box = { .width = buffer->width, .height = buffer->height }, .color = { .r = alpha * 0.5, .g = 0, .b = 0, .a = alpha * 0.5 }, - .clip = &damage->region, + .clip = &clip, }); + + pixman_region32_fini(&clip); } } From 70c99460ca66cc257094fb1e34e59b9ce6f9cb5f Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Thu, 8 Aug 2024 17:55:26 +0300 Subject: [PATCH 063/519] pointer-constraints: don't init/finish current/pending states wlr_surface_synced does it automatically. Reported-by: llyyr --- types/wlr_pointer_constraints_v1.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/types/wlr_pointer_constraints_v1.c b/types/wlr_pointer_constraints_v1.c index f3c8fb74b..078037293 100644 --- a/types/wlr_pointer_constraints_v1.c +++ b/types/wlr_pointer_constraints_v1.c @@ -53,8 +53,6 @@ static void pointer_constraint_destroy(struct wlr_pointer_constraint_v1 *constra wl_list_remove(&constraint->surface_commit.link); wl_list_remove(&constraint->surface_destroy.link); wl_list_remove(&constraint->seat_destroy.link); - pixman_region32_fini(&constraint->current.region); - pixman_region32_fini(&constraint->pending.region); pixman_region32_fini(&constraint->region); free(constraint); } @@ -258,9 +256,6 @@ static void pointer_constraint_create(struct wl_client *client, pixman_region32_init(&constraint->region); - pixman_region32_init(&constraint->pending.region); - pixman_region32_init(&constraint->current.region); - pointer_constraint_set_region(constraint, region_resource); pointer_constraint_commit(constraint); From 235c8e922adcd2de7eab892c05a1b0b0efe75a9b Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 7 Aug 2024 23:51:45 -0400 Subject: [PATCH 064/519] Revert "wlr_scene: Ensure scene_node_update is updating entire node." This reverts commit 66d96d244c589840de6addc6eae1ebfb3101b12f. --- types/scene/wlr_scene.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index b8bfe428f..01643a6ce 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -621,8 +621,13 @@ static void scene_node_update(struct wlr_scene_node *node, struct wlr_scene *scene = scene_node_get_root(node); int x, y; - bool enabled = wlr_scene_node_coords(node, &x, &y); - if (!enabled && !damage) { + if (!wlr_scene_node_coords(node, &x, &y)) { + if (damage) { + scene_update_region(scene, damage); + scene_damage_outputs(scene, damage); + pixman_region32_fini(damage); + } + return; } @@ -641,10 +646,7 @@ static void scene_node_update(struct wlr_scene_node *node, scene_update_region(scene, &update_region); pixman_region32_fini(&update_region); - if (enabled) { - scene_node_visibility(node, damage); - } - + scene_node_visibility(node, damage); scene_damage_outputs(scene, damage); pixman_region32_fini(damage); } From 291df10fe52116ec09ec5a32e9ac69af5abe57ce Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 14 Aug 2024 11:53:05 -0400 Subject: [PATCH 065/519] wlr_scene: Extract function to get xwayland surface from node --- types/scene/wlr_scene.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 01643a6ce..5d62e8339 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -492,21 +492,32 @@ static void update_node_update_outputs(struct wlr_scene_node *node, } #if WLR_HAS_XWAYLAND -static void restack_xwayland_surface(struct wlr_scene_node *node, - struct wlr_box *box, struct scene_update_data *data) { +static struct wlr_xwayland_surface *scene_node_try_get_managed_xwayland_surface( + struct wlr_scene_node *node) { if (node->type != WLR_SCENE_NODE_BUFFER) { - return; + return NULL; } struct wlr_scene_buffer *buffer_node = wlr_scene_buffer_from_node(node); struct wlr_scene_surface *surface_node = wlr_scene_surface_try_from_buffer(buffer_node); if (!surface_node) { - return; + return NULL; } struct wlr_xwayland_surface *xwayland_surface = wlr_xwayland_surface_try_from_wlr_surface(surface_node->surface); if (!xwayland_surface || xwayland_surface->override_redirect) { + return NULL; + } + + return xwayland_surface; +} + +static void restack_xwayland_surface(struct wlr_scene_node *node, + struct wlr_box *box, struct scene_update_data *data) { + struct wlr_xwayland_surface *xwayland_surface = + scene_node_try_get_managed_xwayland_surface(node); + if (!xwayland_surface) { return; } From 2463a4723e2408f86273639e5d78798c1dc9dbe9 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 14 Aug 2024 11:53:20 -0400 Subject: [PATCH 066/519] wlr_scene: Ensure we restack all xwayland surfaces to the bottom when scene node is disabled --- types/scene/wlr_scene.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 5d62e8339..af9e243fa 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -533,6 +533,25 @@ static void restack_xwayland_surface(struct wlr_scene_node *node, data->restack_above = xwayland_surface; } + +static void restack_xwayland_surface_below(struct wlr_scene_node *node) { + if (node->type == WLR_SCENE_NODE_TREE) { + struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); + struct wlr_scene_node *child; + wl_list_for_each(child, &scene_tree->children, link) { + restack_xwayland_surface_below(child); + } + return; + } + + struct wlr_xwayland_surface *xwayland_surface = + scene_node_try_get_managed_xwayland_surface(node); + if (!xwayland_surface) { + return; + } + + wlr_xwayland_surface_restack(xwayland_surface, NULL, XCB_STACK_MODE_BELOW); +} #endif static bool scene_node_update_iterator(struct wlr_scene_node *node, @@ -633,6 +652,9 @@ static void scene_node_update(struct wlr_scene_node *node, int x, y; if (!wlr_scene_node_coords(node, &x, &y)) { +#if WLR_HAS_XWAYLAND + restack_xwayland_surface_below(node); +#endif if (damage) { scene_update_region(scene, damage); scene_damage_outputs(scene, damage); From 23202e192c2989621db564addd6a792d2c218d22 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 14 Aug 2024 12:51:15 -0400 Subject: [PATCH 067/519] wlr_scene: Introduce wlr_scene_output_needs_frame It seems that some scene compositors want to avoid wlr_scene_output_commit and use the lower lever wlr_scene_output_build_state. However, build state does not early return if a frame is not needed so compositors will implement the check themselves. Let's add a helper function that compositors can use to implement the check. Technically pending_commit_damage is a private interface, so this lets compositors not interface with private interfaces to implement the check. --- include/wlr/types/wlr_scene.h | 6 ++++++ types/scene/wlr_scene.c | 8 ++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 7f3217baa..df9e93b9c 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -529,6 +529,12 @@ struct wlr_scene_output_state_options { struct wlr_swapchain *swapchain; }; +/** + * Returns true if scene wants to render a new frame. False, if no new frame + * is needed and an output commit can be skipped for the current frame. + */ +bool wlr_scene_output_needs_frame(struct wlr_scene_output *scene_output); + /** * Render and commit an output. */ diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index af9e243fa..1829ce542 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1818,10 +1818,14 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry, return true; } +bool wlr_scene_output_needs_frame(struct wlr_scene_output *scene_output) { + return scene_output->output->needs_frame || pixman_region32_not_empty( + &scene_output->pending_commit_damage); +} + bool wlr_scene_output_commit(struct wlr_scene_output *scene_output, const struct wlr_scene_output_state_options *options) { - if (!scene_output->output->needs_frame && !pixman_region32_not_empty( - &scene_output->pending_commit_damage)) { + if (!wlr_scene_output_needs_frame(scene_output)) { return true; } From 515275ee7214bf91f8a758b660093eb4b932195a Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 14 Aug 2024 13:18:56 -0400 Subject: [PATCH 068/519] wlr_scene: Introduce wlr_scene_set_gamma_control_manager_v1 --- include/wlr/types/wlr_scene.h | 15 ++++++ types/scene/wlr_scene.c | 93 ++++++++++++++++++++++++++++++++++- 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index df9e93b9c..bcfbc72f9 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -42,6 +42,7 @@ struct wlr_scene_output_layout; struct wlr_presentation; struct wlr_linux_dmabuf_v1; +struct wlr_gamma_control_manager_v1; struct wlr_output_state; typedef bool (*wlr_scene_buffer_point_accepts_input_func_t)( @@ -100,10 +101,13 @@ struct wlr_scene { // May be NULL struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1; + struct wlr_gamma_control_manager_v1 *gamma_control_manager_v1; // private state struct wl_listener linux_dmabuf_v1_destroy; + struct wl_listener gamma_control_manager_v1_destroy; + struct wl_listener gamma_control_manager_v1_set_gamma; enum wlr_scene_debug_damage_option debug_damage_option; bool direct_scanout; @@ -220,6 +224,9 @@ struct wlr_scene_output { uint8_t index; bool prev_scanout; + bool gamma_lut_changed; + struct wlr_gamma_control_v1 *gamma_lut; + struct wl_listener output_commit; struct wl_listener output_damage; struct wl_listener output_needs_frame; @@ -326,6 +333,14 @@ struct wlr_scene *wlr_scene_create(void); void wlr_scene_set_linux_dmabuf_v1(struct wlr_scene *scene, struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1); +/** + * Handles gamma_control_v1 for all outputs in the scene. + * + * Asserts that a struct wlr_gamma_control_manager_v1 hasn't already been set + * for the scene. + */ +void wlr_scene_set_gamma_control_manager_v1(struct wlr_scene *scene, + struct wlr_gamma_control_manager_v1 *gamma_control); /** * Add a node displaying nothing but its children. diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 1829ce542..0211f965d 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -136,6 +137,8 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) { } wl_list_remove(&scene->linux_dmabuf_v1_destroy.link); + wl_list_remove(&scene->gamma_control_manager_v1_destroy.link); + wl_list_remove(&scene->gamma_control_manager_v1_set_gamma.link); } else { assert(node->parent); } @@ -169,6 +172,8 @@ struct wlr_scene *wlr_scene_create(void) { wl_list_init(&scene->outputs); wl_list_init(&scene->linux_dmabuf_v1_destroy.link); + wl_list_init(&scene->gamma_control_manager_v1_destroy.link); + wl_list_init(&scene->gamma_control_manager_v1_set_gamma.link); const char *debug_damage_options[] = { "none", @@ -1429,6 +1434,52 @@ void wlr_scene_set_linux_dmabuf_v1(struct wlr_scene *scene, wl_signal_add(&linux_dmabuf_v1->events.destroy, &scene->linux_dmabuf_v1_destroy); } +static void scene_handle_gamma_control_manager_v1_set_gamma(struct wl_listener *listener, + void *data) { + const struct wlr_gamma_control_manager_v1_set_gamma_event *event = data; + struct wlr_scene *scene = + wl_container_of(listener, scene, gamma_control_manager_v1_set_gamma); + struct wlr_scene_output *output = wlr_scene_get_scene_output(scene, event->output); + if (!output) { + // this scene might not own this output. + return; + } + + output->gamma_lut_changed = true; + output->gamma_lut = event->control; + wlr_output_schedule_frame(output->output); +} + +static void scene_handle_gamma_control_manager_v1_destroy(struct wl_listener *listener, + void *data) { + struct wlr_scene *scene = + wl_container_of(listener, scene, gamma_control_manager_v1_destroy); + wl_list_remove(&scene->gamma_control_manager_v1_destroy.link); + wl_list_init(&scene->gamma_control_manager_v1_destroy.link); + wl_list_remove(&scene->gamma_control_manager_v1_set_gamma.link); + wl_list_init(&scene->gamma_control_manager_v1_set_gamma.link); + scene->gamma_control_manager_v1 = NULL; + + struct wlr_scene_output *output; + wl_list_for_each(output, &scene->outputs, link) { + output->gamma_lut_changed = false; + output->gamma_lut = NULL; + } +} + +void wlr_scene_set_gamma_control_manager_v1(struct wlr_scene *scene, + struct wlr_gamma_control_manager_v1 *gamma_control) { + assert(scene->gamma_control_manager_v1 == NULL); + scene->gamma_control_manager_v1 = gamma_control; + + scene->gamma_control_manager_v1_destroy.notify = + scene_handle_gamma_control_manager_v1_destroy; + wl_signal_add(&gamma_control->events.destroy, &scene->gamma_control_manager_v1_destroy); + scene->gamma_control_manager_v1_set_gamma.notify = + scene_handle_gamma_control_manager_v1_set_gamma; + wl_signal_add(&gamma_control->events.set_gamma, &scene->gamma_control_manager_v1_set_gamma); +} + static void scene_output_handle_destroy(struct wlr_addon *addon) { struct wlr_scene_output *scene_output = wl_container_of(addon, scene_output, addon); @@ -1496,6 +1547,13 @@ static void scene_output_handle_commit(struct wl_listener *listener, void *data) !wl_list_empty(&scene_output->damage_highlight_regions)) { wlr_output_schedule_frame(scene_output->output); } + + // Next time the output is enabled, try to re-apply the gamma LUT + if (scene_output->scene->gamma_control_manager_v1 && + (state->committed & WLR_OUTPUT_STATE_ENABLED) && + !scene_output->output->enabled) { + scene_output->gamma_lut_changed = true; + } } static void scene_output_handle_damage(struct wl_listener *listener, void *data) { @@ -1820,7 +1878,7 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry, bool wlr_scene_output_needs_frame(struct wlr_scene_output *scene_output) { return scene_output->output->needs_frame || pixman_region32_not_empty( - &scene_output->pending_commit_damage); + &scene_output->pending_commit_damage) || scene_output->gamma_lut_changed; } bool wlr_scene_output_commit(struct wlr_scene_output *scene_output, @@ -1846,6 +1904,35 @@ out: return ok; } +static void scene_output_state_attempt_gamma(struct wlr_scene_output *scene_output, + struct wlr_output_state *state) { + if (!scene_output->gamma_lut_changed) { + return; + } + + struct wlr_output_state gamma_pending = {0}; + if (!wlr_output_state_copy(&gamma_pending, state)) { + return; + } + + if (!wlr_gamma_control_v1_apply(scene_output->gamma_lut, &gamma_pending)) { + wlr_output_state_finish(&gamma_pending); + return; + } + + scene_output->gamma_lut_changed = false; + if (!wlr_output_test_state(scene_output->output, &gamma_pending)) { + wlr_gamma_control_v1_send_failed_and_destroy(scene_output->gamma_lut); + + scene_output->gamma_lut = NULL; + wlr_output_state_finish(&gamma_pending); + return; + } + + wlr_output_state_copy(state, &gamma_pending); + wlr_output_state_finish(&gamma_pending); +} + bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, struct wlr_output_state *state, const struct wlr_scene_output_state_options *options) { struct wlr_scene_output_state_options default_options = {0}; @@ -1982,6 +2069,8 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, } if (scanout) { + scene_output_state_attempt_gamma(scene_output, state); + if (timer) { struct timespec end_time, duration; clock_gettime(CLOCK_MONOTONIC, &end_time); @@ -2141,6 +2230,8 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, scene_output->in_point); } + scene_output_state_attempt_gamma(scene_output, state); + return true; } From a1298580cc4ba5fab25be4d043e4f5879c4eb9ba Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Wed, 14 Aug 2024 18:22:41 +0300 Subject: [PATCH 069/519] compositor: add surface role map hook --- include/wlr/types/wlr_compositor.h | 7 +++++++ types/wlr_compositor.c | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/include/wlr/types/wlr_compositor.h b/include/wlr/types/wlr_compositor.h index 174869f44..d0d25b7aa 100644 --- a/include/wlr/types/wlr_compositor.h +++ b/include/wlr/types/wlr_compositor.h @@ -97,6 +97,13 @@ struct wlr_surface_role { * such object exists. */ void (*commit)(struct wlr_surface *surface); + /** + * Called when the surface is mapped. May be NULL. + * + * If the role is represented by an object, this is only called if + * such object exists. + */ + void (*map)(struct wlr_surface *surface); /** * Called when the surface is unmapped. May be NULL. * diff --git a/types/wlr_compositor.c b/types/wlr_compositor.c index 9aee912e0..081af3539 100644 --- a/types/wlr_compositor.c +++ b/types/wlr_compositor.c @@ -828,6 +828,11 @@ void wlr_surface_map(struct wlr_surface *surface) { subsurface_consider_map(subsurface); } + if (surface->role != NULL && surface->role->map != NULL && + (surface->role_resource != NULL || surface->role->no_object)) { + surface->role->map(surface); + } + wl_signal_emit_mutable(&surface->events.map, NULL); } From 5c98d1a04a1439bf40c6e516086cfaff2d67f135 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Wed, 14 Aug 2024 18:25:03 +0300 Subject: [PATCH 070/519] xdg-surface: fix window geometry handling It was completely wrong: according to the protocol, the effective geometry is only updated on commit time if there pending state has new state from xdg_surface.set_window_geometry or xdg_surface.set_window_geometry has never been sent at all. This commit adds wlr_xdg_surface.geometry which correctly matches the effective window geometry and removes now-useless wlr_xdg_surface_get_geometry(). --- include/wlr/types/wlr_xdg_shell.h | 22 ++++++------ tinywl/tinywl.c | 18 +++++----- types/scene/xdg_shell.c | 4 +-- types/wlr_layer_shell_v1.c | 8 ++--- types/xdg_shell/wlr_xdg_popup.c | 4 +-- types/xdg_shell/wlr_xdg_surface.c | 58 ++++++++++++++++++++----------- 6 files changed, 62 insertions(+), 52 deletions(-) diff --git a/include/wlr/types/wlr_xdg_shell.h b/include/wlr/types/wlr_xdg_shell.h index d54c5366b..940f35ec6 100644 --- a/include/wlr/types/wlr_xdg_shell.h +++ b/include/wlr/types/wlr_xdg_shell.h @@ -228,9 +228,16 @@ struct wlr_xdg_surface_configure { }; }; +enum wlr_xdg_surface_state_field { + WLR_XDG_SURFACE_STATE_WINDOW_GEOMETRY = 1 << 0, +}; + struct wlr_xdg_surface_state { - uint32_t configure_serial; + uint32_t committed; // enum wlr_xdg_surface_state_field + struct wlr_box geometry; + + uint32_t configure_serial; }; /** @@ -274,6 +281,8 @@ struct wlr_xdg_surface { // Whether the latest commit is an initial commit bool initial_commit; + struct wlr_box geometry; + struct { struct wl_signal destroy; struct wl_signal ping_timeout; @@ -523,17 +532,6 @@ struct wlr_xdg_toplevel *wlr_xdg_toplevel_try_from_wlr_surface(struct wlr_surfac */ struct wlr_xdg_popup *wlr_xdg_popup_try_from_wlr_surface(struct wlr_surface *surface); -/** - * Get the surface geometry. - * - * This is either the geometry as set by the client, or defaulted to the bounds - * of the surface + the subsurfaces (as specified by the protocol). - * - * The x and y value can be < 0. - */ -void wlr_xdg_surface_get_geometry(struct wlr_xdg_surface *surface, - struct wlr_box *box); - /** * Call `iterator` on each mapped surface and popup in the xdg-surface tree * (whether or not this xdg-surface is mapped), with the surface's position diff --git a/tinywl/tinywl.c b/tinywl/tinywl.c index 6c043ed51..edc6269b3 100644 --- a/tinywl/tinywl.c +++ b/tinywl/tinywl.c @@ -427,10 +427,9 @@ static void process_cursor_resize(struct tinywl_server *server, uint32_t time) { } } - struct wlr_box geo_box; - wlr_xdg_surface_get_geometry(toplevel->xdg_toplevel->base, &geo_box); + struct wlr_box *geo_box = &toplevel->xdg_toplevel->base->geometry; wlr_scene_node_set_position(&toplevel->scene_tree->node, - new_left - geo_box.x, new_top - geo_box.y); + new_left - geo_box->x, new_top - geo_box->y); int new_width = new_right - new_left; int new_height = new_bottom - new_top; @@ -727,17 +726,16 @@ static void begin_interactive(struct tinywl_toplevel *toplevel, server->grab_x = server->cursor->x - toplevel->scene_tree->node.x; server->grab_y = server->cursor->y - toplevel->scene_tree->node.y; } else { - struct wlr_box geo_box; - wlr_xdg_surface_get_geometry(toplevel->xdg_toplevel->base, &geo_box); + struct wlr_box *geo_box = &toplevel->xdg_toplevel->base->geometry; - double border_x = (toplevel->scene_tree->node.x + geo_box.x) + - ((edges & WLR_EDGE_RIGHT) ? geo_box.width : 0); - double border_y = (toplevel->scene_tree->node.y + geo_box.y) + - ((edges & WLR_EDGE_BOTTOM) ? geo_box.height : 0); + double border_x = (toplevel->scene_tree->node.x + geo_box->x) + + ((edges & WLR_EDGE_RIGHT) ? geo_box->width : 0); + double border_y = (toplevel->scene_tree->node.y + geo_box->y) + + ((edges & WLR_EDGE_BOTTOM) ? geo_box->height : 0); server->grab_x = server->cursor->x - border_x; server->grab_y = server->cursor->y - border_y; - server->grab_geobox = geo_box; + server->grab_geobox = *geo_box; server->grab_geobox.x += toplevel->scene_tree->node.x; server->grab_geobox.y += toplevel->scene_tree->node.y; diff --git a/types/scene/xdg_shell.c b/types/scene/xdg_shell.c index ed792633d..73d0acb79 100644 --- a/types/scene/xdg_shell.c +++ b/types/scene/xdg_shell.c @@ -34,10 +34,8 @@ static void scene_xdg_surface_update_position( struct wlr_scene_xdg_surface *scene_xdg_surface) { struct wlr_xdg_surface *xdg_surface = scene_xdg_surface->xdg_surface; - struct wlr_box geo = {0}; - wlr_xdg_surface_get_geometry(xdg_surface, &geo); wlr_scene_node_set_position(&scene_xdg_surface->surface_tree->node, - -geo.x, -geo.y); + -xdg_surface->geometry.x, -xdg_surface->geometry.y); if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { struct wlr_xdg_popup *popup = xdg_surface->popup; diff --git a/types/wlr_layer_shell_v1.c b/types/wlr_layer_shell_v1.c index a59f1104a..2275ed065 100644 --- a/types/wlr_layer_shell_v1.c +++ b/types/wlr_layer_shell_v1.c @@ -574,8 +574,8 @@ void wlr_layer_surface_v1_for_each_popup_surface(struct wlr_layer_surface_v1 *su } double popup_sx, popup_sy; - popup_sx = popup->current.geometry.x - popup->base->current.geometry.x; - popup_sy = popup->current.geometry.y - popup->base->current.geometry.y; + popup_sx = popup->current.geometry.x - popup->base->geometry.x; + popup_sy = popup->current.geometry.y - popup->base->geometry.y; struct layer_surface_iterator_data data = { .user_iterator = iterator, @@ -609,8 +609,8 @@ struct wlr_surface *wlr_layer_surface_v1_popup_surface_at( } double popup_sx, popup_sy; - popup_sx = popup->current.geometry.x - popup->base->current.geometry.x; - popup_sy = popup->current.geometry.y - popup->base->current.geometry.y; + popup_sx = popup->current.geometry.x - popup->base->geometry.x; + popup_sy = popup->current.geometry.y - popup->base->geometry.y; struct wlr_surface *sub = wlr_xdg_surface_surface_at( popup->base, sx - popup_sx, sy - popup_sy, sub_x, sub_y); diff --git a/types/xdg_shell/wlr_xdg_popup.c b/types/xdg_shell/wlr_xdg_popup.c index a718b4528..df1e75de3 100644 --- a/types/xdg_shell/wlr_xdg_popup.c +++ b/types/xdg_shell/wlr_xdg_popup.c @@ -498,8 +498,8 @@ void wlr_xdg_popup_get_toplevel_coords(struct wlr_xdg_popup *popup, popup_sy += xdg_surface->popup->current.geometry.y; parent = xdg_surface->popup->parent; } else { - popup_sx += xdg_surface->current.geometry.x; - popup_sy += xdg_surface->current.geometry.y; + popup_sx += xdg_surface->geometry.x; + popup_sy += xdg_surface->geometry.y; break; } } diff --git a/types/xdg_shell/wlr_xdg_surface.c b/types/xdg_shell/wlr_xdg_surface.c index 9842a563c..97daa0d30 100644 --- a/types/xdg_shell/wlr_xdg_surface.c +++ b/types/xdg_shell/wlr_xdg_surface.c @@ -219,12 +219,12 @@ static void xdg_surface_handle_set_window_geometry(struct wl_client *client, } if (width <= 0 || height <= 0) { - wl_resource_post_error(resource, - XDG_SURFACE_ERROR_INVALID_SIZE, + wl_resource_post_error(resource, XDG_SURFACE_ERROR_INVALID_SIZE, "Tried to set invalid xdg-surface geometry"); return; } + surface->pending.committed |= WLR_XDG_SURFACE_STATE_WINDOW_GEOMETRY; surface->pending.geometry.x = x; surface->pending.geometry.y = y; surface->pending.geometry.width = width; @@ -256,6 +256,21 @@ static const struct xdg_surface_interface xdg_surface_implementation = { .set_window_geometry = xdg_surface_handle_set_window_geometry, }; +// The window geometry is updated on commit, unless the commit is going to map +// the surface, in which case it's updated on map, so that subsurfaces are +// mapped and surface extents are computed correctly. +static void update_geometry(struct wlr_xdg_surface *surface) { + if (!wlr_box_empty(&surface->current.geometry)) { + if ((surface->current.committed & WLR_XDG_SURFACE_STATE_WINDOW_GEOMETRY) != 0) { + wlr_surface_get_extents(surface->surface, &surface->geometry); + wlr_box_intersection(&surface->geometry, + &surface->current.geometry, &surface->geometry); + } + } else { + wlr_surface_get_extents(surface->surface, &surface->geometry); + } +} + static void xdg_surface_role_client_commit(struct wlr_surface *wlr_surface) { struct wlr_xdg_surface *surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface); assert(surface != NULL); @@ -320,11 +335,20 @@ static void xdg_surface_role_commit(struct wlr_surface *wlr_surface) { break; } - if (wlr_surface_has_buffer(wlr_surface)) { + if (wlr_surface->mapped) { + update_geometry(surface); + } else if (wlr_surface_has_buffer(wlr_surface)) { wlr_surface_map(wlr_surface); } } +static void xdg_surface_role_map(struct wlr_surface *wlr_surface) { + struct wlr_xdg_surface *surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface); + assert(surface != NULL); + + update_geometry(surface); +} + static void xdg_surface_role_destroy(struct wlr_surface *wlr_surface) { struct wlr_xdg_surface *surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface); if (surface == NULL) { @@ -339,11 +363,19 @@ static const struct wlr_surface_role xdg_surface_role = { .name = "xdg_surface", .client_commit = xdg_surface_role_client_commit, .commit = xdg_surface_role_commit, + .map = xdg_surface_role_map, .destroy = xdg_surface_role_destroy, }; +static void surface_synced_move_state(void *_dst, void *_src) { + struct wlr_xdg_surface_state *dst = _dst, *src = _src; + *dst = *src; + src->committed = 0; +} + static const struct wlr_surface_synced_impl surface_synced_impl = { .state_size = sizeof(struct wlr_xdg_surface_state), + .move_state = surface_synced_move_state, }; struct wlr_xdg_surface *wlr_xdg_surface_try_from_wlr_surface( @@ -522,12 +554,8 @@ void wlr_xdg_popup_get_position(struct wlr_xdg_popup *popup, double *popup_sx, double *popup_sy) { struct wlr_xdg_surface *parent = wlr_xdg_surface_try_from_wlr_surface(popup->parent); assert(parent != NULL); - struct wlr_box parent_geo; - wlr_xdg_surface_get_geometry(parent, &parent_geo); - *popup_sx = parent_geo.x + popup->current.geometry.x - - popup->base->current.geometry.x; - *popup_sy = parent_geo.y + popup->current.geometry.y - - popup->base->current.geometry.y; + *popup_sx = parent->geometry.x + popup->current.geometry.x - popup->base->geometry.x; + *popup_sy = parent->geometry.y + popup->current.geometry.y - popup->base->geometry.y; } struct wlr_surface *wlr_xdg_surface_surface_at( @@ -611,15 +639,3 @@ void wlr_xdg_surface_for_each_popup_surface(struct wlr_xdg_surface *surface, wlr_surface_iterator_func_t iterator, void *user_data) { xdg_surface_for_each_popup_surface(surface, 0, 0, iterator, user_data); } - -void wlr_xdg_surface_get_geometry(struct wlr_xdg_surface *surface, - struct wlr_box *box) { - wlr_surface_get_extents(surface->surface, box); - - /* The client never set the geometry */ - if (wlr_box_empty(&surface->current.geometry)) { - return; - } - - wlr_box_intersection(box, &surface->current.geometry, box); -} From 4da4269d8f707dec3691e2ffaacc106db96780d1 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Wed, 14 Aug 2024 22:43:44 +0300 Subject: [PATCH 071/519] seat/pointer: reset pressed buttons on "grab-compatible" focus change Fixes: 08e779bd85b738d993007fa6a7f3f32bebc19649 --- types/seat/wlr_seat_pointer.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/types/seat/wlr_seat_pointer.c b/types/seat/wlr_seat_pointer.c index 31113c811..defe14e89 100644 --- a/types/seat/wlr_seat_pointer.c +++ b/types/seat/wlr_seat_pointer.c @@ -179,9 +179,6 @@ void wlr_seat_pointer_enter(struct wlr_seat *wlr_seat, seat_client_send_pointer_leave_raw(focused_client, focused_surface); } - // The current surface doesn't know about pressed buttons - wlr_seat->pointer_state.button_count = 0; - // enter the current surface if (client != NULL && surface != NULL) { uint32_t serial = wlr_seat_client_next_serial(client); @@ -433,18 +430,36 @@ void wlr_seat_pointer_end_grab(struct wlr_seat *wlr_seat) { } } +// Switching focus means the new surface doesn't know about the currently +// pressed buttons. This function allows to reset them. +static void reset_buttons(struct wlr_seat *wlr_seat) { + wlr_seat->pointer_state.button_count = 0; +} + void wlr_seat_pointer_notify_enter(struct wlr_seat *wlr_seat, struct wlr_surface *surface, double sx, double sy) { // NULL surfaces are prohibited in the grab-compatible API. Use // wlr_seat_pointer_notify_clear_focus() instead. assert(surface); struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab; + struct wlr_surface *focused_surface = wlr_seat->pointer_state.focused_surface; + grab->interface->enter(grab, surface, sx, sy); + + if (focused_surface != wlr_seat->pointer_state.focused_surface) { + reset_buttons(wlr_seat); + } } void wlr_seat_pointer_notify_clear_focus(struct wlr_seat *wlr_seat) { struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab; + struct wlr_surface *focused_surface = wlr_seat->pointer_state.focused_surface; + grab->interface->clear_focus(grab); + + if (focused_surface != wlr_seat->pointer_state.focused_surface) { + reset_buttons(wlr_seat); + } } void wlr_seat_pointer_notify_motion(struct wlr_seat *wlr_seat, uint32_t time, From baaec88e2f3d60a94610ea8c1cd067ffefe1099b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Wed, 14 Aug 2024 23:00:36 -0600 Subject: [PATCH 072/519] linux-drm-syncobj-v1: actually use the requested version --- types/wlr_linux_drm_syncobj_v1.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/types/wlr_linux_drm_syncobj_v1.c b/types/wlr_linux_drm_syncobj_v1.c index bc2c79dc7..c30274e01 100644 --- a/types/wlr_linux_drm_syncobj_v1.c +++ b/types/wlr_linux_drm_syncobj_v1.c @@ -424,6 +424,8 @@ static bool check_syncobj_eventfd(int drm_fd) { struct wlr_linux_drm_syncobj_manager_v1 *wlr_linux_drm_syncobj_manager_v1_create( struct wl_display *display, uint32_t version, int drm_fd) { + assert(version <= LINUX_DRM_SYNCOBJ_V1_VERSION); + if (!check_syncobj_eventfd(drm_fd)) { wlr_log(WLR_INFO, "DRM syncobj eventfd unavailable, disabling linux-drm-syncobj-v1"); return NULL; @@ -441,7 +443,7 @@ struct wlr_linux_drm_syncobj_manager_v1 *wlr_linux_drm_syncobj_manager_v1_create manager->global = wl_global_create(display, &wp_linux_drm_syncobj_manager_v1_interface, - LINUX_DRM_SYNCOBJ_V1_VERSION, manager, manager_bind); + version, manager, manager_bind); if (manager->global == NULL) { goto error_drm_fd; } From ee21deb4588286d486902cb1ac0c610881b7fb19 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Thu, 15 Aug 2024 11:29:31 +0300 Subject: [PATCH 073/519] linux-drm-syncobj: add missing decls in the header --- include/wlr/types/wlr_linux_drm_syncobj_v1.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/wlr/types/wlr_linux_drm_syncobj_v1.h b/include/wlr/types/wlr_linux_drm_syncobj_v1.h index 1a29a0d8f..02cdb42a8 100644 --- a/include/wlr/types/wlr_linux_drm_syncobj_v1.h +++ b/include/wlr/types/wlr_linux_drm_syncobj_v1.h @@ -12,6 +12,9 @@ #include #include +struct wlr_buffer; +struct wlr_surface; + struct wlr_linux_drm_syncobj_surface_v1_state { struct wlr_drm_syncobj_timeline *acquire_timeline; uint64_t acquire_point; From 3103ea3af9e1a6a8d6ad9a4e6d78bee73e5c29a5 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Thu, 15 Aug 2024 12:48:28 +0300 Subject: [PATCH 074/519] backend/wayland: process initial events from globals correctly Previous logic could lead wlr_wl_backend.drm_render_name being written to twice, causing a memory leak. This commit fixes the race condition. --- backend/wayland/backend.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/backend/wayland/backend.c b/backend/wayland/backend.c index c2b2f2faa..9a74e6919 100644 --- a/backend/wayland/backend.c +++ b/backend/wayland/backend.c @@ -178,7 +178,9 @@ static void linux_dmabuf_feedback_v1_handle_main_device(void *data, "falling back to primary node", name); } - feedback_data->backend->drm_render_name = strdup(name); + struct wlr_wl_backend *wl = feedback_data->backend; + assert(wl->drm_render_name == NULL); + wl->drm_render_name = strdup(name); drmFreeDevice(&device); } @@ -305,6 +307,7 @@ static char *get_render_name(const char *name) { static void legacy_drm_handle_device(void *data, struct wl_drm *drm, const char *name) { struct wlr_wl_backend *wl = data; + assert(wl->drm_render_name == NULL); wl->drm_render_name = get_render_name(name); } @@ -621,6 +624,8 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_event_loop *loop, goto error_registry; } + wl_display_roundtrip(wl->remote_display); // process initial event bursts + struct zwp_linux_dmabuf_feedback_v1 *linux_dmabuf_feedback_v1 = NULL; struct wlr_wl_linux_dmabuf_feedback_v1 feedback_data = { .backend = wl }; if (wl->zwp_linux_dmabuf_v1 != NULL && @@ -638,15 +643,17 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_event_loop *loop, if (wl->legacy_drm != NULL) { wl_drm_destroy(wl->legacy_drm); wl->legacy_drm = NULL; + + free(wl->drm_render_name); + wl->drm_render_name = NULL; } - } - wl_display_roundtrip(wl->remote_display); // get linux-dmabuf formats + wl_display_roundtrip(wl->remote_display); // get linux-dmabuf feedback events + + if (feedback_data.format_table != NULL) { + munmap(feedback_data.format_table, feedback_data.format_table_size); + } - if (feedback_data.format_table != NULL) { - munmap(feedback_data.format_table, feedback_data.format_table_size); - } - if (linux_dmabuf_feedback_v1 != NULL) { zwp_linux_dmabuf_feedback_v1_destroy(linux_dmabuf_feedback_v1); } From 08495d2596acf1d4b7fbc779e0666473b1da83e5 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sat, 10 Aug 2024 13:27:05 +0200 Subject: [PATCH 075/519] backend/drm: don't set vsync present flag if page flip was async --- backend/drm/drm.c | 7 +++++-- include/backend/drm/drm.h | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 154aa99d9..90d243059 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -610,6 +610,7 @@ static bool drm_commit(struct wlr_drm_backend *drm, if (page_flip == NULL) { return false; } + page_flip->async = (flags & DRM_MODE_PAGE_FLIP_ASYNC); } bool ok = drm->iface->commit(drm, state, page_flip, flags, test_only); @@ -2047,8 +2048,10 @@ static void handle_page_flip(int fd, unsigned seq, drm_fb_move(&layer->current_fb, &layer->queued_fb); } - uint32_t present_flags = WLR_OUTPUT_PRESENT_VSYNC | - WLR_OUTPUT_PRESENT_HW_CLOCK | WLR_OUTPUT_PRESENT_HW_COMPLETION; + uint32_t present_flags = WLR_OUTPUT_PRESENT_HW_CLOCK | WLR_OUTPUT_PRESENT_HW_COMPLETION; + if (!page_flip->async) { + present_flags |= WLR_OUTPUT_PRESENT_VSYNC; + } /* Don't report ZERO_COPY in multi-gpu situations, because we had to copy * data between the GPUs, even if we were using the direct scanout * interface. diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index 18f487420..33b729aae 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -165,6 +165,8 @@ struct wlr_drm_page_flip { struct wl_list link; // wlr_drm_connector.page_flips struct wlr_drm_page_flip_connector *connectors; size_t connectors_len; + // True if DRM_MODE_PAGE_FLIP_ASYNC was set + bool async; }; struct wlr_drm_page_flip_connector { From 310a5eb61c8792bbc00e1351c36b1aad5201b0f1 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Fri, 9 Aug 2024 15:27:17 +0300 Subject: [PATCH 076/519] backend/wayland: simplify wl_keyboard.{enter,leave} processing --- backend/wayland/seat.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/backend/wayland/seat.c b/backend/wayland/seat.c index b812b53a8..a66357bf8 100644 --- a/backend/wayland/seat.c +++ b/backend/wayland/seat.c @@ -27,12 +27,14 @@ static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { struct wlr_keyboard *keyboard = data; + int64_t time_msec = get_current_time_msec(); + uint32_t *keycode_ptr; wl_array_for_each(keycode_ptr, keys) { struct wlr_keyboard_key_event event = { .keycode = *keycode_ptr, .state = WL_KEYBOARD_KEY_STATE_PRESSED, - .time_msec = get_current_time_msec(), + .time_msec = time_msec, .update_state = false, }; wlr_keyboard_notify_key(keyboard, &event); @@ -43,18 +45,12 @@ static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface) { struct wlr_keyboard *keyboard = data; - size_t num_keycodes = keyboard->num_keycodes; - uint32_t pressed[num_keycodes + 1]; - memcpy(pressed, keyboard->keycodes, - num_keycodes * sizeof(uint32_t)); - - for (size_t i = 0; i < num_keycodes; ++i) { - uint32_t keycode = pressed[i]; - + int64_t time_msec = get_current_time_msec(); + while (keyboard->num_keycodes > 0) { struct wlr_keyboard_key_event event = { - .keycode = keycode, + .keycode = keyboard->keycodes[keyboard->num_keycodes - 1], .state = WL_KEYBOARD_KEY_STATE_RELEASED, - .time_msec = get_current_time_msec(), + .time_msec = time_msec, .update_state = false, }; wlr_keyboard_notify_key(keyboard, &event); From e88988e364103ac0acd0da2e7642d5bf7fefd83e Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Fri, 9 Aug 2024 15:27:51 +0300 Subject: [PATCH 077/519] keyboard: simplify releasing keys on finish --- types/wlr_keyboard.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/types/wlr_keyboard.c b/types/wlr_keyboard.c index 98978ee1d..366f4218a 100644 --- a/types/wlr_keyboard.c +++ b/types/wlr_keyboard.c @@ -153,12 +153,11 @@ static void keyboard_unset_keymap(struct wlr_keyboard *kb) { void wlr_keyboard_finish(struct wlr_keyboard *kb) { /* Release pressed keys */ - size_t orig_num_keycodes = kb->num_keycodes; - for (size_t i = 0; i < orig_num_keycodes; ++i) { - assert(kb->num_keycodes == orig_num_keycodes - i); + int64_t time_msec = get_current_time_msec(); + while (kb->num_keycodes > 0) { struct wlr_keyboard_key_event event = { - .time_msec = get_current_time_msec(), - .keycode = kb->keycodes[orig_num_keycodes - i - 1], + .time_msec = time_msec, + .keycode = kb->keycodes[kb->num_keycodes - 1], .update_state = false, .state = WL_KEYBOARD_KEY_STATE_RELEASED, }; From 270e6f4ebb46757427f46f515b70938c1baad9e2 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Tue, 11 Jun 2024 16:11:43 +0300 Subject: [PATCH 078/519] layer-shell: add v5 support --- include/wlr/types/wlr_layer_shell_v1.h | 2 ++ protocol/wlr-layer-shell-unstable-v1.xml | 21 ++++++++++-- types/wlr_layer_shell_v1.c | 43 +++++++++++++++++++++--- 3 files changed, 59 insertions(+), 7 deletions(-) diff --git a/include/wlr/types/wlr_layer_shell_v1.h b/include/wlr/types/wlr_layer_shell_v1.h index eaf6af428..c0043ee1c 100644 --- a/include/wlr/types/wlr_layer_shell_v1.h +++ b/include/wlr/types/wlr_layer_shell_v1.h @@ -49,6 +49,7 @@ enum wlr_layer_surface_v1_state_field { WLR_LAYER_SURFACE_V1_STATE_MARGIN = 1 << 3, WLR_LAYER_SURFACE_V1_STATE_KEYBOARD_INTERACTIVITY = 1 << 4, WLR_LAYER_SURFACE_V1_STATE_LAYER = 1 << 5, + WLR_LAYER_SURFACE_V1_STATE_EXCLUSIVE_EDGE = 1 << 6, }; struct wlr_layer_surface_v1_state { @@ -62,6 +63,7 @@ struct wlr_layer_surface_v1_state { enum zwlr_layer_surface_v1_keyboard_interactivity keyboard_interactive; uint32_t desired_width, desired_height; enum zwlr_layer_shell_v1_layer layer; + uint32_t exclusive_edge; uint32_t configure_serial; uint32_t actual_width, actual_height; diff --git a/protocol/wlr-layer-shell-unstable-v1.xml b/protocol/wlr-layer-shell-unstable-v1.xml index d62fd51e9..e9f27e4fd 100644 --- a/protocol/wlr-layer-shell-unstable-v1.xml +++ b/protocol/wlr-layer-shell-unstable-v1.xml @@ -25,7 +25,7 @@ THIS SOFTWARE. - + Clients can use this interface to assign the surface_layer role to wl_surfaces. Such surfaces are assigned to a "layer" of the output and @@ -100,7 +100,7 @@ - + An interface that may be implemented by a wl_surface, for surfaces that are designed to be rendered as a layer of a stacked desktop-like @@ -367,6 +367,7 @@ + @@ -386,5 +387,21 @@ + + + + + + Requests an edge for the exclusive zone to apply. The exclusive + edge will be automatically deduced from anchor points when possible, + but when the surface is anchored to a corner, it will be necessary + to set it explicitly to disambiguate, as it is not possible to deduce + which one of the two corner edges should be used. + + The edge must be one the surface is anchored to, otherwise the + invalid_exclusive_edge protocol error will be raised. + + + diff --git a/types/wlr_layer_shell_v1.c b/types/wlr_layer_shell_v1.c index 2275ed065..718c63b74 100644 --- a/types/wlr_layer_shell_v1.c +++ b/types/wlr_layer_shell_v1.c @@ -11,7 +11,7 @@ // Note: zwlr_layer_surface_v1 becomes inert on wlr_layer_surface_v1_destroy() -#define LAYER_SHELL_VERSION 4 +#define LAYER_SHELL_VERSION 5 static void resource_handle_destroy(struct wl_client *client, struct wl_resource *resource) { @@ -264,6 +264,7 @@ static void layer_surface_set_layer(struct wl_client *client, return; } if (layer > ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY) { + // XXX: this sends a zwlr_layer_shell_v1 error to a zwlr_layer_surface_v1 object wl_resource_post_error(surface->resource, ZWLR_LAYER_SHELL_V1_ERROR_INVALID_LAYER, "Invalid layer %" PRIu32, layer); @@ -278,6 +279,31 @@ static void layer_surface_set_layer(struct wl_client *client, surface->pending.layer = layer; } +static void layer_surface_set_exclusive_edge(struct wl_client *client, + struct wl_resource *surface_resource, uint32_t edge) { + struct wlr_layer_surface_v1 *surface = + wlr_layer_surface_v1_from_resource(surface_resource); + if (!surface) { + return; + } + switch (edge) { + case 0: + case ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP: + case ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM: + case ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT: + case ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT: + break; + default: + wl_resource_post_error(surface->resource, + ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_EXCLUSIVE_EDGE, + "invalid exclusive edge %" PRIu32, edge); + return; + } + + surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_EXCLUSIVE_EDGE; + surface->pending.exclusive_edge = edge; +} + static const struct zwlr_layer_surface_v1_interface layer_surface_implementation = { .destroy = resource_handle_destroy, .ack_configure = layer_surface_handle_ack_configure, @@ -288,6 +314,7 @@ static const struct zwlr_layer_surface_v1_interface layer_surface_implementation .set_keyboard_interactivity = layer_surface_handle_set_keyboard_interactivity, .get_popup = layer_surface_handle_get_popup, .set_layer = layer_surface_set_layer, + .set_exclusive_edge = layer_surface_set_exclusive_edge, }; uint32_t wlr_layer_surface_v1_configure(struct wlr_layer_surface_v1 *surface, @@ -336,10 +363,11 @@ static void layer_surface_role_client_commit(struct wlr_surface *wlr_surface) { return; } + uint32_t anchor = surface->pending.anchor; + const uint32_t horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; - if (surface->pending.desired_width == 0 && - (surface->pending.anchor & horiz) != horiz) { + if (surface->pending.desired_width == 0 && (anchor & horiz) != horiz) { wlr_surface_reject_pending(wlr_surface, surface->resource, ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SIZE, "width 0 requested without setting left and right anchors"); @@ -348,13 +376,18 @@ static void layer_surface_role_client_commit(struct wlr_surface *wlr_surface) { const uint32_t vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; - if (surface->pending.desired_height == 0 && - (surface->pending.anchor & vert) != vert) { + if (surface->pending.desired_height == 0 && (anchor & vert) != vert) { wlr_surface_reject_pending(wlr_surface, surface->resource, ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SIZE, "height 0 requested without setting top and bottom anchors"); return; } + + if ((anchor & surface->pending.exclusive_edge) != surface->pending.exclusive_edge) { + wlr_surface_reject_pending(wlr_surface, surface->resource, + ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_EXCLUSIVE_EDGE, + "exclusive edge is invalid given the surface anchors"); + } } static void layer_surface_role_commit(struct wlr_surface *wlr_surface) { From a0450d219fbc8a453876e70f29b9b5c2f76b0c64 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Tue, 11 Jun 2024 16:16:31 +0300 Subject: [PATCH 079/519] layer-shell: introduce wlr_layer_surface_v1_get_exclusive_edge() --- include/wlr/types/wlr_layer_shell_v1.h | 8 +++++++ types/wlr_layer_shell_v1.c | 30 ++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/include/wlr/types/wlr_layer_shell_v1.h b/include/wlr/types/wlr_layer_shell_v1.h index c0043ee1c..9be246e16 100644 --- a/include/wlr/types/wlr_layer_shell_v1.h +++ b/include/wlr/types/wlr_layer_shell_v1.h @@ -13,6 +13,7 @@ #include #include #include +#include #include "wlr-layer-shell-unstable-v1-protocol.h" /** @@ -187,4 +188,11 @@ struct wlr_surface *wlr_layer_surface_v1_popup_surface_at( struct wlr_layer_surface_v1 *wlr_layer_surface_v1_from_resource( struct wl_resource *resource); +/** + * Get the edge the exclusive zone must be applied to. + * + * Returns WLR_EDGE_NONE if the exclusive zone is nonpositive or must not be applied. + */ +enum wlr_edges wlr_layer_surface_v1_get_exclusive_edge(struct wlr_layer_surface_v1 *surface); + #endif diff --git a/types/wlr_layer_shell_v1.c b/types/wlr_layer_shell_v1.c index 718c63b74..6df4b7a8e 100644 --- a/types/wlr_layer_shell_v1.c +++ b/types/wlr_layer_shell_v1.c @@ -654,3 +654,33 @@ struct wlr_surface *wlr_layer_surface_v1_popup_surface_at( return NULL; } + +enum wlr_edges wlr_layer_surface_v1_get_exclusive_edge(struct wlr_layer_surface_v1 *surface) { + if (surface->current.exclusive_zone <= 0) { + return WLR_EDGE_NONE; + } + uint32_t anchor = surface->current.anchor; + if (surface->current.exclusive_edge != 0) { + anchor = surface->current.exclusive_edge; + } + switch (anchor) { + case ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP: + case ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP: + return WLR_EDGE_TOP; + case ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM: + case ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM: + return WLR_EDGE_BOTTOM; + case ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT: + case ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT: + return WLR_EDGE_LEFT; + case ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT: + case ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT: + return WLR_EDGE_RIGHT; + default: + return WLR_EDGE_NONE; + } +} From ccd4703207652f851bc5573cc826917497731bb9 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 19 Aug 2024 14:37:39 +0200 Subject: [PATCH 080/519] allocator/gbm: Log errno if gbm_bo_create fails --- render/allocator/gbm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/render/allocator/gbm.c b/render/allocator/gbm.c index baa0fb6eb..2e609b7ab 100644 --- a/render/allocator/gbm.c +++ b/render/allocator/gbm.c @@ -106,14 +106,14 @@ static struct wlr_gbm_buffer *create_buffer(struct wlr_gbm_allocator *alloc, fallback_modifier = DRM_FORMAT_MOD_LINEAR; } else if (!wlr_drm_format_has(format, DRM_FORMAT_MOD_INVALID)) { // If the format doesn't accept an implicit modifier, bail out. - wlr_log(WLR_ERROR, "gbm_bo_create_with_modifiers failed"); + wlr_log_errno(WLR_ERROR, "gbm_bo_create_with_modifiers failed"); return NULL; } bo = gbm_bo_create(gbm_device, width, height, format->format, usage); has_modifier = false; } if (bo == NULL) { - wlr_log(WLR_ERROR, "gbm_bo_create failed"); + wlr_log_errno(WLR_ERROR, "gbm_bo_create failed"); return NULL; } From 5df2b34d2b8cf99a19a81a43d2fea9dac65ed122 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 19 Aug 2024 14:59:44 +0200 Subject: [PATCH 081/519] allocator/gbm: Reset errno before gbm_bo_create Not all paths in GBM set errno properly on error. Reset it to zero before calling GBM to avoid accidentally printing a garbage error. --- render/allocator/gbm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/render/allocator/gbm.c b/render/allocator/gbm.c index 2e609b7ab..da035c668 100644 --- a/render/allocator/gbm.c +++ b/render/allocator/gbm.c @@ -96,6 +96,7 @@ static struct wlr_gbm_buffer *create_buffer(struct wlr_gbm_allocator *alloc, bool has_modifier = true; uint64_t fallback_modifier = DRM_FORMAT_MOD_INVALID; + errno = 0; struct gbm_bo *bo = gbm_bo_create_with_modifiers(gbm_device, width, height, format->format, format->modifiers, format->len); if (bo == NULL) { @@ -109,6 +110,7 @@ static struct wlr_gbm_buffer *create_buffer(struct wlr_gbm_allocator *alloc, wlr_log_errno(WLR_ERROR, "gbm_bo_create_with_modifiers failed"); return NULL; } + errno = 0; bo = gbm_bo_create(gbm_device, width, height, format->format, usage); has_modifier = false; } From 3048fb3fc62ab771550654f4191f92f87b9065b2 Mon Sep 17 00:00:00 2001 From: YaoBing Xiao Date: Mon, 19 Aug 2024 23:07:35 +0800 Subject: [PATCH 082/519] render/egl: Release devices before return --- render/egl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/render/egl.c b/render/egl.c index ce0c95d91..d38f394e6 100644 --- a/render/egl.c +++ b/render/egl.c @@ -476,6 +476,7 @@ static EGLDeviceEXT get_egl_device_from_drm_fd(struct wlr_egl *egl, if (!egl->procs.eglQueryDevicesEXT(nb_devices, devices, &nb_devices)) { wlr_log(WLR_ERROR, "Failed to query EGL devices"); + free(devices); return EGL_NO_DEVICE_EXT; } @@ -483,6 +484,7 @@ static EGLDeviceEXT get_egl_device_from_drm_fd(struct wlr_egl *egl, int ret = drmGetDevice(drm_fd, &device); if (ret < 0) { wlr_log(WLR_ERROR, "Failed to get DRM device: %s", strerror(-ret)); + free(devices); return EGL_NO_DEVICE_EXT; } From b4f077a596421eb14e487db5cf716a1abf7e4bba Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Mon, 19 Aug 2024 14:23:38 +0300 Subject: [PATCH 083/519] drm-syncobj-v1: remove buffer release listener on signaller destroy --- types/wlr_linux_drm_syncobj_v1.c | 1 + 1 file changed, 1 insertion(+) diff --git a/types/wlr_linux_drm_syncobj_v1.c b/types/wlr_linux_drm_syncobj_v1.c index c30274e01..c2e339a4e 100644 --- a/types/wlr_linux_drm_syncobj_v1.c +++ b/types/wlr_linux_drm_syncobj_v1.c @@ -485,6 +485,7 @@ static void release_signaller_handle_buffer_release(struct wl_listener *listener } wlr_drm_syncobj_timeline_unref(signaller->timeline); + wl_list_remove(&signaller->buffer_release.link); free(signaller); } From 3d2f09bace0bb5d0ddd93c78b087f4c17a76a8ba Mon Sep 17 00:00:00 2001 From: Dudemanguy Date: Mon, 19 Aug 2024 11:33:36 -0500 Subject: [PATCH 084/519] backend/drm: fix a use-after-free The page_flip can be destroyed, but it is unconditionally accessed later on when setting present_flags. Fix this by simply setting the present_flags before the page_flip gets destroyed. --- backend/drm/drm.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 90d243059..1ba8939af 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -2018,6 +2018,12 @@ static void handle_page_flip(int fd, unsigned seq, if (conn != NULL) { conn->pending_page_flip = NULL; } + + uint32_t present_flags = WLR_OUTPUT_PRESENT_HW_CLOCK | WLR_OUTPUT_PRESENT_HW_COMPLETION; + if (!page_flip->async) { + present_flags |= WLR_OUTPUT_PRESENT_VSYNC; + } + if (page_flip->connectors_len == 0) { drm_page_flip_destroy(page_flip); } @@ -2048,10 +2054,6 @@ static void handle_page_flip(int fd, unsigned seq, drm_fb_move(&layer->current_fb, &layer->queued_fb); } - uint32_t present_flags = WLR_OUTPUT_PRESENT_HW_CLOCK | WLR_OUTPUT_PRESENT_HW_COMPLETION; - if (!page_flip->async) { - present_flags |= WLR_OUTPUT_PRESENT_VSYNC; - } /* Don't report ZERO_COPY in multi-gpu situations, because we had to copy * data between the GPUs, even if we were using the direct scanout * interface. From 8582b45c9ed25dd9f4742f02a3c5725cc6db2e6f Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 17 Apr 2024 11:30:34 +0200 Subject: [PATCH 085/519] xdg-shell: document struct wlr_xdg_toplevel_configure --- include/wlr/types/wlr_xdg_shell.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/wlr/types/wlr_xdg_shell.h b/include/wlr/types/wlr_xdg_shell.h index 940f35ec6..854185bfc 100644 --- a/include/wlr/types/wlr_xdg_shell.h +++ b/include/wlr/types/wlr_xdg_shell.h @@ -153,14 +153,23 @@ enum wlr_xdg_toplevel_configure_field { WLR_XDG_TOPLEVEL_CONFIGURE_WM_CAPABILITIES = 1 << 1, }; +/** + * State set in an toplevel configure sequence. + */ struct wlr_xdg_toplevel_configure { + // Bitmask of optional fields which are set uint32_t fields; // enum wlr_xdg_toplevel_configure_field + + // The following fields must always be set to reflect the current state bool maximized, fullscreen, resizing, activated, suspended; uint32_t tiled; // enum wlr_edges int32_t width, height; + + // Only for WLR_XDG_TOPLEVEL_CONFIGURE_BOUNDS struct { int32_t width, height; } bounds; + // Only for WLR_XDG_TOPLEVEL_CONFIGURE_WM_CAPABILITIES uint32_t wm_capabilities; // enum wlr_xdg_toplevel_wm_capabilities }; From 098cb9b7a3d7c7a8b1633809e04c9bcf34a33784 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 17 Apr 2024 11:30:46 +0200 Subject: [PATCH 086/519] xdg-shell: add wlr_xdg_toplevel_configure() A struct wlr_xdg_toplevel_configure is passed in with the whole state. This makes it a lot clearer that the size and WM state are always sent to the client. --- include/wlr/types/wlr_xdg_shell.h | 6 ++++++ types/xdg_shell/wlr_xdg_toplevel.c | 24 ++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/include/wlr/types/wlr_xdg_shell.h b/include/wlr/types/wlr_xdg_shell.h index 854185bfc..900d504c0 100644 --- a/include/wlr/types/wlr_xdg_shell.h +++ b/include/wlr/types/wlr_xdg_shell.h @@ -374,6 +374,12 @@ struct wlr_xdg_positioner *wlr_xdg_positioner_from_resource( */ void wlr_xdg_surface_ping(struct wlr_xdg_surface *surface); +/** + * Configure the toplevel. Returns the associated configure serial. + */ +uint32_t wlr_xdg_toplevel_configure(struct wlr_xdg_toplevel *toplevel, + const struct wlr_xdg_toplevel_configure *configure); + /** * Request that this toplevel surface be the given size. Returns the associated * configure serial. diff --git a/types/xdg_shell/wlr_xdg_toplevel.c b/types/xdg_shell/wlr_xdg_toplevel.c index 1c4fa30c4..556dcf80c 100644 --- a/types/xdg_shell/wlr_xdg_toplevel.c +++ b/types/xdg_shell/wlr_xdg_toplevel.c @@ -545,6 +545,30 @@ void wlr_xdg_toplevel_send_close(struct wlr_xdg_toplevel *toplevel) { xdg_toplevel_send_close(toplevel->resource); } +uint32_t wlr_xdg_toplevel_configure(struct wlr_xdg_toplevel *toplevel, + const struct wlr_xdg_toplevel_configure *configure) { + toplevel->scheduled.width = configure->width; + toplevel->scheduled.height = configure->height; + toplevel->scheduled.maximized = configure->maximized; + toplevel->scheduled.fullscreen = configure->fullscreen; + toplevel->scheduled.resizing = configure->resizing; + toplevel->scheduled.activated = configure->activated; + toplevel->scheduled.suspended = configure->suspended; + toplevel->scheduled.tiled = configure->tiled; + + if (configure->fields & WLR_XDG_TOPLEVEL_CONFIGURE_BOUNDS) { + toplevel->scheduled.fields |= WLR_XDG_TOPLEVEL_CONFIGURE_BOUNDS; + toplevel->scheduled.bounds = configure->bounds; + } + + if (configure->fields & WLR_XDG_TOPLEVEL_CONFIGURE_WM_CAPABILITIES) { + toplevel->scheduled.fields |= WLR_XDG_TOPLEVEL_CONFIGURE_WM_CAPABILITIES; + toplevel->scheduled.wm_capabilities = configure->wm_capabilities; + } + + return wlr_xdg_surface_schedule_configure(toplevel->base); +} + uint32_t wlr_xdg_toplevel_set_size(struct wlr_xdg_toplevel *toplevel, int32_t width, int32_t height) { assert(width >= 0 && height >= 0); From 62cc96b3a4eee779f6dbd92f39ebdb828fec43aa Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Tue, 20 Aug 2024 19:25:20 +0300 Subject: [PATCH 087/519] scene: update output geom on commit after dropping pending damage Otherwise the whole output damage gets ignored. --- types/scene/wlr_scene.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 0211f965d..b3d9aadeb 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1520,16 +1520,6 @@ static void scene_output_handle_commit(struct wl_listener *listener, void *data) struct wlr_output_event_commit *event = data; const struct wlr_output_state *state = event->state; - bool force_update = state->committed & ( - WLR_OUTPUT_STATE_TRANSFORM | - WLR_OUTPUT_STATE_SCALE | - WLR_OUTPUT_STATE_SUBPIXEL); - - if (force_update || state->committed & (WLR_OUTPUT_STATE_MODE | - WLR_OUTPUT_STATE_ENABLED)) { - scene_output_update_geometry(scene_output, force_update); - } - // if the output has been committed with a certain damage, we know that region // will be acknowledged by the backend so we don't need to keep track of it // anymore @@ -1543,6 +1533,16 @@ static void scene_output_handle_commit(struct wl_listener *listener, void *data) } } + bool force_update = state->committed & ( + WLR_OUTPUT_STATE_TRANSFORM | + WLR_OUTPUT_STATE_SCALE | + WLR_OUTPUT_STATE_SUBPIXEL); + + if (force_update || state->committed & (WLR_OUTPUT_STATE_MODE | + WLR_OUTPUT_STATE_ENABLED)) { + scene_output_update_geometry(scene_output, force_update); + } + if (scene_output->scene->debug_damage_option == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT && !wl_list_empty(&scene_output->damage_highlight_regions)) { wlr_output_schedule_frame(scene_output->output); From eebaca8dbffc7c5c46e5878167ddee269493e073 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 20 Aug 2024 18:33:00 -0400 Subject: [PATCH 088/519] output/render: Drop buffer age from wlr_output_begin_render_pass For compositors wanting to damage track, `wlr_damage_ring_rotate_buffer()` should be used and the damage should be set on the passed state. --- examples/fullscreen-shell.c | 3 +-- examples/output-layers.c | 4 +--- examples/output-layout.c | 2 +- examples/pointer.c | 2 +- examples/rotation.c | 2 +- examples/simple.c | 2 +- examples/tablet.c | 2 +- examples/touch.c | 2 +- include/wlr/types/wlr_output.h | 3 +-- types/output/render.c | 5 ++--- 10 files changed, 11 insertions(+), 16 deletions(-) diff --git a/examples/fullscreen-shell.c b/examples/fullscreen-shell.c index c12f126e9..956d851af 100644 --- a/examples/fullscreen-shell.c +++ b/examples/fullscreen-shell.c @@ -90,8 +90,7 @@ static void output_handle_frame(struct wl_listener *listener, void *data) { struct wlr_output_state state; wlr_output_state_init(&state); - struct wlr_render_pass *pass = wlr_output_begin_render_pass(output->wlr_output, &state, NULL, - NULL); + struct wlr_render_pass *pass = wlr_output_begin_render_pass(output->wlr_output, &state, NULL); if (pass == NULL) { return; } diff --git a/examples/output-layers.c b/examples/output-layers.c index 1863d16e0..0535ff0b1 100644 --- a/examples/output-layers.c +++ b/examples/output-layers.c @@ -94,9 +94,7 @@ static void output_handle_frame(struct wl_listener *listener, void *data) { int width, height; wlr_output_effective_resolution(output->wlr_output, &width, &height); - struct wlr_render_pass *pass = wlr_output_begin_render_pass(output->wlr_output, &output_state, - NULL, NULL); - + struct wlr_render_pass *pass = wlr_output_begin_render_pass(output->wlr_output, &output_state, NULL); wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){ .box = { .width = width, .height = height }, .color = { 0.3, 0.3, 0.3, 1 }, diff --git a/examples/output-layout.c b/examples/output-layout.c index 3a3eeb24f..c481ddf80 100644 --- a/examples/output-layout.c +++ b/examples/output-layout.c @@ -114,7 +114,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) { struct wlr_output_state output_state; wlr_output_state_init(&output_state); - struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &output_state, NULL, NULL); + struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &output_state, NULL); wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){ .box = { .width = wlr_output->width, .height = wlr_output->height }, diff --git a/examples/pointer.c b/examples/pointer.c index fed37a5b3..f3ef5435e 100644 --- a/examples/pointer.c +++ b/examples/pointer.c @@ -101,7 +101,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) { struct wlr_output_state output_state; wlr_output_state_init(&output_state); - struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &output_state, NULL, NULL); + struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &output_state, NULL); wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){ .box = { .width = wlr_output->width, .height = wlr_output->height }, .color = { diff --git a/examples/rotation.c b/examples/rotation.c index 351973f61..18bfbd38d 100644 --- a/examples/rotation.c +++ b/examples/rotation.c @@ -59,7 +59,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) { struct wlr_output_state output_state; wlr_output_state_init(&output_state); - struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &output_state, NULL, NULL); + struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &output_state, NULL); wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){ .box = { .width = wlr_output->width, .height = wlr_output->height }, diff --git a/examples/simple.c b/examples/simple.c index 81ed2e0e9..d16708654 100644 --- a/examples/simple.c +++ b/examples/simple.c @@ -63,7 +63,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) { struct wlr_output_state state; wlr_output_state_init(&state); - struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &state, NULL, NULL); + struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &state, NULL); wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){ .box = { .width = wlr_output->width, .height = wlr_output->height }, .color = { diff --git a/examples/tablet.c b/examples/tablet.c index 8f0754df1..78666fff7 100644 --- a/examples/tablet.c +++ b/examples/tablet.c @@ -89,7 +89,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) { struct wlr_output_state output_state; wlr_output_state_init(&output_state); - struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &output_state, NULL, NULL); + struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &output_state, NULL); wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){ .box = { .width = wlr_output->width, .height = wlr_output->height }, diff --git a/examples/touch.c b/examples/touch.c index e1bd73818..b891837a4 100644 --- a/examples/touch.c +++ b/examples/touch.c @@ -76,7 +76,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) { struct wlr_output_state output_state; wlr_output_state_init(&output_state); - struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &output_state, NULL, NULL); + struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &output_state, NULL); wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){ .box = { .width = width, .height = height }, .color = { 0.25, 0.25, 0.25, 1 }, diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index f32e15ef5..bf80f585a 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -616,7 +616,6 @@ bool wlr_output_configure_primary_swapchain(struct wlr_output *output, * frames or -1 if unknown. This is useful for damage tracking. */ struct wlr_render_pass *wlr_output_begin_render_pass(struct wlr_output *output, - struct wlr_output_state *state, int *buffer_age, - struct wlr_buffer_pass_options *render_options); + struct wlr_output_state *state, struct wlr_buffer_pass_options *render_options); #endif diff --git a/types/output/render.c b/types/output/render.c index 709646a10..a70b79b71 100644 --- a/types/output/render.c +++ b/types/output/render.c @@ -199,13 +199,12 @@ bool output_pick_format(struct wlr_output *output, } struct wlr_render_pass *wlr_output_begin_render_pass(struct wlr_output *output, - struct wlr_output_state *state, int *buffer_age, - struct wlr_buffer_pass_options *render_options) { + struct wlr_output_state *state, struct wlr_buffer_pass_options *render_options) { if (!wlr_output_configure_primary_swapchain(output, state, &output->swapchain)) { return NULL; } - struct wlr_buffer *buffer = wlr_swapchain_acquire(output->swapchain, buffer_age); + struct wlr_buffer *buffer = wlr_swapchain_acquire(output->swapchain, NULL); if (buffer == NULL) { return NULL; } From bfcaa4bc44b16c68445b10edcc9d4b85f158a253 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 20 Aug 2024 18:33:59 -0400 Subject: [PATCH 089/519] swapchain, damage_ring: Drop buffer age --- backend/drm/renderer.c | 2 +- include/wlr/render/swapchain.h | 12 +------- include/wlr/types/wlr_damage_ring.h | 20 ------------- render/swapchain.c | 33 +++------------------ types/output/cursor.c | 3 +- types/output/output.c | 5 ---- types/output/render.c | 4 +-- types/output/swapchain.c | 2 +- types/scene/wlr_scene.c | 2 +- types/wlr_damage_ring.c | 44 ---------------------------- types/wlr_output_swapchain_manager.c | 2 +- types/wlr_screencopy_v1.c | 3 +- 12 files changed, 13 insertions(+), 119 deletions(-) diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c index e4aadc106..37569c182 100644 --- a/backend/drm/renderer.c +++ b/backend/drm/renderer.c @@ -89,7 +89,7 @@ struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf, return NULL; } - struct wlr_buffer *dst = wlr_swapchain_acquire(surf->swapchain, NULL); + struct wlr_buffer *dst = wlr_swapchain_acquire(surf->swapchain); if (!dst) { wlr_log(WLR_ERROR, "Failed to acquire multi-GPU swapchain buffer"); goto error_tex; diff --git a/include/wlr/render/swapchain.h b/include/wlr/render/swapchain.h index cb3aee9d5..2317f1b62 100644 --- a/include/wlr/render/swapchain.h +++ b/include/wlr/render/swapchain.h @@ -10,7 +10,6 @@ struct wlr_swapchain_slot { struct wlr_buffer *buffer; bool acquired; // waiting for release - int age; struct wl_listener release; }; @@ -36,21 +35,12 @@ void wlr_swapchain_destroy(struct wlr_swapchain *swapchain); * The returned buffer is locked. When the caller is done with it, they must * unlock it by calling wlr_buffer_unlock. */ -struct wlr_buffer *wlr_swapchain_acquire(struct wlr_swapchain *swapchain, - int *age); +struct wlr_buffer *wlr_swapchain_acquire(struct wlr_swapchain *swapchain); /** * Returns true if this buffer has been created by this swapchain, and false * otherwise. */ bool wlr_swapchain_has_buffer(struct wlr_swapchain *swapchain, struct wlr_buffer *buffer); -/** - * Mark the buffer as submitted for presentation. This needs to be called by - * swap chain users on frame boundaries. - * - * If the buffer hasn't been created via the swap chain, the call is ignored. - */ -void wlr_swapchain_set_buffer_submitted(struct wlr_swapchain *swapchain, - struct wlr_buffer *buffer); #endif diff --git a/include/wlr/types/wlr_damage_ring.h b/include/wlr/types/wlr_damage_ring.h index 203d919f3..3f469af60 100644 --- a/include/wlr/types/wlr_damage_ring.h +++ b/include/wlr/types/wlr_damage_ring.h @@ -15,9 +15,6 @@ #include #include -/* For triple buffering, a history of two frames is required. */ -#define WLR_DAMAGE_RING_PREVIOUS_LEN 2 - struct wlr_box; struct wlr_damage_ring_buffer { @@ -37,9 +34,6 @@ struct wlr_damage_ring { // private state - pixman_region32_t previous[WLR_DAMAGE_RING_PREVIOUS_LEN]; - size_t previous_idx; - struct wl_list buffers; // wlr_damage_ring_buffer.link }; @@ -79,20 +73,6 @@ bool wlr_damage_ring_add_box(struct wlr_damage_ring *ring, */ void wlr_damage_ring_add_whole(struct wlr_damage_ring *ring); -/** - * Rotate the damage ring. This needs to be called after using the accumulated - * damage, e.g. after rendering to an output's back buffer. - */ -void wlr_damage_ring_rotate(struct wlr_damage_ring *ring); - -/** - * Get accumulated damage, which is the difference between the current buffer - * and the buffer with age of buffer_age; in context of rendering, this is - * the region that needs to be redrawn. - */ -void wlr_damage_ring_get_buffer_damage(struct wlr_damage_ring *ring, - int buffer_age, pixman_region32_t *damage); - /** * Get accumulated buffer damage and rotate the damage ring. * diff --git a/render/swapchain.c b/render/swapchain.c index a50dae5c1..233d85eb0 100644 --- a/render/swapchain.c +++ b/render/swapchain.c @@ -65,7 +65,7 @@ static void slot_handle_release(struct wl_listener *listener, void *data) { } static struct wlr_buffer *slot_acquire(struct wlr_swapchain *swapchain, - struct wlr_swapchain_slot *slot, int *age) { + struct wlr_swapchain_slot *slot) { assert(!slot->acquired); assert(slot->buffer != NULL); @@ -74,15 +74,10 @@ static struct wlr_buffer *slot_acquire(struct wlr_swapchain *swapchain, slot->release.notify = slot_handle_release; wl_signal_add(&slot->buffer->events.release, &slot->release); - if (age != NULL) { - *age = slot->age; - } - return wlr_buffer_lock(slot->buffer); } -struct wlr_buffer *wlr_swapchain_acquire(struct wlr_swapchain *swapchain, - int *age) { +struct wlr_buffer *wlr_swapchain_acquire(struct wlr_swapchain *swapchain) { struct wlr_swapchain_slot *free_slot = NULL; for (size_t i = 0; i < WLR_SWAPCHAIN_CAP; i++) { struct wlr_swapchain_slot *slot = &swapchain->slots[i]; @@ -90,7 +85,7 @@ struct wlr_buffer *wlr_swapchain_acquire(struct wlr_swapchain *swapchain, continue; } if (slot->buffer != NULL) { - return slot_acquire(swapchain, slot, age); + return slot_acquire(swapchain, slot); } free_slot = slot; } @@ -110,7 +105,7 @@ struct wlr_buffer *wlr_swapchain_acquire(struct wlr_swapchain *swapchain, wlr_log(WLR_ERROR, "Failed to allocate buffer"); return NULL; } - return slot_acquire(swapchain, free_slot, age); + return slot_acquire(swapchain, free_slot); } bool wlr_swapchain_has_buffer(struct wlr_swapchain *swapchain, @@ -123,23 +118,3 @@ bool wlr_swapchain_has_buffer(struct wlr_swapchain *swapchain, } return false; } - -void wlr_swapchain_set_buffer_submitted(struct wlr_swapchain *swapchain, - struct wlr_buffer *buffer) { - assert(buffer != NULL); - - if (!wlr_swapchain_has_buffer(swapchain, buffer)) { - return; - } - - // See the algorithm described in: - // https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_buffer_age.txt - for (size_t i = 0; i < WLR_SWAPCHAIN_CAP; i++) { - struct wlr_swapchain_slot *slot = &swapchain->slots[i]; - if (slot->buffer == buffer) { - slot->age = 1; - } else if (slot->age > 0) { - slot->age++; - } - } -} diff --git a/types/output/cursor.c b/types/output/cursor.c index 95793a39d..7bc8d0d69 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -241,8 +241,7 @@ static struct wlr_buffer *render_cursor_buffer(struct wlr_output_cursor *cursor) } } - struct wlr_buffer *buffer = - wlr_swapchain_acquire(output->cursor_swapchain, NULL); + struct wlr_buffer *buffer = wlr_swapchain_acquire(output->cursor_swapchain); if (buffer == NULL) { return NULL; } diff --git a/types/output/output.c b/types/output/output.c index 1c9a00779..6755493fb 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -271,11 +271,6 @@ static void output_apply_state(struct wlr_output *output, } } - if ((state->committed & WLR_OUTPUT_STATE_BUFFER) && - output->swapchain != NULL) { - wlr_swapchain_set_buffer_submitted(output->swapchain, state->buffer); - } - bool mode_updated = false; if (state->committed & WLR_OUTPUT_STATE_MODE) { int width = 0, height = 0, refresh = 0; diff --git a/types/output/render.c b/types/output/render.c index a70b79b71..b71478a6f 100644 --- a/types/output/render.c +++ b/types/output/render.c @@ -55,7 +55,7 @@ static struct wlr_buffer *output_acquire_empty_buffer(struct wlr_output *output, return NULL; } - struct wlr_buffer *buffer = wlr_swapchain_acquire(output->swapchain, NULL); + struct wlr_buffer *buffer = wlr_swapchain_acquire(output->swapchain); if (buffer == NULL) { return NULL; } @@ -204,7 +204,7 @@ struct wlr_render_pass *wlr_output_begin_render_pass(struct wlr_output *output, return NULL; } - struct wlr_buffer *buffer = wlr_swapchain_acquire(output->swapchain, NULL); + struct wlr_buffer *buffer = wlr_swapchain_acquire(output->swapchain); if (buffer == NULL) { return NULL; } diff --git a/types/output/swapchain.c b/types/output/swapchain.c index 5a16610d8..01857a204 100644 --- a/types/output/swapchain.c +++ b/types/output/swapchain.c @@ -50,7 +50,7 @@ static struct wlr_swapchain *create_swapchain(struct wlr_output *output, static bool test_swapchain(struct wlr_output *output, struct wlr_swapchain *swapchain, const struct wlr_output_state *state) { - struct wlr_buffer *buffer = wlr_swapchain_acquire(swapchain, NULL); + struct wlr_buffer *buffer = wlr_swapchain_acquire(swapchain); if (buffer == NULL) { return false; } diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index b3d9aadeb..884c2e11c 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -2089,7 +2089,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, swapchain = output->swapchain; } - struct wlr_buffer *buffer = wlr_swapchain_acquire(swapchain, NULL); + struct wlr_buffer *buffer = wlr_swapchain_acquire(swapchain); if (buffer == NULL) { return false; } diff --git a/types/wlr_damage_ring.c b/types/wlr_damage_ring.c index 2934de8b8..840c872d4 100644 --- a/types/wlr_damage_ring.c +++ b/types/wlr_damage_ring.c @@ -15,10 +15,6 @@ void wlr_damage_ring_init(struct wlr_damage_ring *ring) { }; pixman_region32_init(&ring->current); - for (size_t i = 0; i < WLR_DAMAGE_RING_PREVIOUS_LEN; ++i) { - pixman_region32_init(&ring->previous[i]); - } - wl_list_init(&ring->buffers); } @@ -31,9 +27,6 @@ static void buffer_destroy(struct wlr_damage_ring_buffer *entry) { void wlr_damage_ring_finish(struct wlr_damage_ring *ring) { pixman_region32_fini(&ring->current); - for (size_t i = 0; i < WLR_DAMAGE_RING_PREVIOUS_LEN; ++i) { - pixman_region32_fini(&ring->previous[i]); - } struct wlr_damage_ring_buffer *entry, *tmp_entry; wl_list_for_each_safe(entry, tmp_entry, &ring->buffers, link) { buffer_destroy(entry); @@ -92,43 +85,6 @@ void wlr_damage_ring_add_whole(struct wlr_damage_ring *ring) { &ring->current, 0, 0, ring->width, ring->height); } -void wlr_damage_ring_rotate(struct wlr_damage_ring *ring) { - // modular decrement - ring->previous_idx = ring->previous_idx + - WLR_DAMAGE_RING_PREVIOUS_LEN - 1; - ring->previous_idx %= WLR_DAMAGE_RING_PREVIOUS_LEN; - - pixman_region32_copy(&ring->previous[ring->previous_idx], &ring->current); - pixman_region32_clear(&ring->current); -} - -void wlr_damage_ring_get_buffer_damage(struct wlr_damage_ring *ring, - int buffer_age, pixman_region32_t *damage) { - if (buffer_age <= 0 || buffer_age - 1 > WLR_DAMAGE_RING_PREVIOUS_LEN) { - pixman_region32_clear(damage); - pixman_region32_union_rect(damage, damage, - 0, 0, ring->width, ring->height); - } else { - pixman_region32_copy(damage, &ring->current); - - // Accumulate damage from old buffers - for (int i = 0; i < buffer_age - 1; ++i) { - int j = (ring->previous_idx + i) % WLR_DAMAGE_RING_PREVIOUS_LEN; - pixman_region32_union(damage, damage, &ring->previous[j]); - } - - // Check the number of rectangles - int n_rects = pixman_region32_n_rects(damage); - if (n_rects > WLR_DAMAGE_RING_MAX_RECTS) { - pixman_box32_t *extents = pixman_region32_extents(damage); - pixman_region32_union_rect(damage, damage, - extents->x1, extents->y1, - extents->x2 - extents->x1, - extents->y2 - extents->y1); - } - } -} - static void entry_squash_damage(struct wlr_damage_ring_buffer *entry) { pixman_region32_t *prev; if (entry->link.prev == &entry->ring->buffers) { diff --git a/types/wlr_output_swapchain_manager.c b/types/wlr_output_swapchain_manager.c index c2cca43c6..9df3a6524 100644 --- a/types/wlr_output_swapchain_manager.c +++ b/types/wlr_output_swapchain_manager.c @@ -143,7 +143,7 @@ static bool manager_output_prepare(struct wlr_output_swapchain_manager_output *m return false; } - struct wlr_buffer *buffer = wlr_swapchain_acquire(swapchain, NULL); + struct wlr_buffer *buffer = wlr_swapchain_acquire(swapchain); if (buffer == NULL) { return false; } diff --git a/types/wlr_screencopy_v1.c b/types/wlr_screencopy_v1.c index acd584d5d..c1fbb59ce 100644 --- a/types/wlr_screencopy_v1.c +++ b/types/wlr_screencopy_v1.c @@ -542,8 +542,7 @@ static void capture_output(struct wl_client *wl_client, goto error; } - int buffer_age; - struct wlr_buffer *buffer = wlr_swapchain_acquire(output->swapchain, &buffer_age); + struct wlr_buffer *buffer = wlr_swapchain_acquire(output->swapchain); if (buffer == NULL) { goto error; } From cf93d3173626c507cb40b2c5d730d64d683d6fe5 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Wed, 21 Aug 2024 13:33:38 +0300 Subject: [PATCH 090/519] scene: resize damage ring on geometry update --- types/scene/wlr_scene.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 884c2e11c..bc776fdd9 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1508,6 +1508,10 @@ static void scene_node_output_update(struct wlr_scene_node *node, static void scene_output_update_geometry(struct wlr_scene_output *scene_output, bool force_update) { + int ring_width, ring_height; + wlr_output_transformed_resolution(scene_output->output, &ring_width, &ring_height); + wlr_damage_ring_set_bounds(&scene_output->damage_ring, ring_width, ring_height); + scene_output_damage_whole(scene_output); scene_node_output_update(&scene_output->scene->tree.node, From 43554c1966bec7c85bf222d93ebc15eafbeb6b3b Mon Sep 17 00:00:00 2001 From: YaoBing Xiao Date: Tue, 20 Aug 2024 17:45:13 +0800 Subject: [PATCH 091/519] types/wlr_output: removing the useless pointer --- backend/drm/drm.c | 9 ++++----- backend/wayland/output.c | 9 ++++----- backend/x11/output.c | 5 +---- include/wlr/types/wlr_output.h | 2 +- types/output/output.c | 6 ++---- types/wlr_presentation_time.c | 4 ++-- 6 files changed, 14 insertions(+), 21 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 1ba8939af..6e3b5e99f 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -2062,16 +2062,15 @@ static void handle_page_flip(int fd, unsigned seq, present_flags |= WLR_OUTPUT_PRESENT_ZERO_COPY; } - struct timespec present_time = { - .tv_sec = tv_sec, - .tv_nsec = tv_usec * 1000, - }; struct wlr_output_event_present present_event = { /* The DRM backend guarantees that the presentation event will be for * the last submitted frame. */ .commit_seq = conn->output.commit_seq, .presented = drm->session->active, - .when = &present_time, + .when = { + .tv_sec = tv_sec, + .tv_nsec = tv_usec * 1000, + }, .seq = seq, .refresh = mhz_to_nsec(conn->refresh), .flags = present_flags, diff --git a/backend/wayland/output.c b/backend/wayland/output.c index 39803eae9..e672028b7 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -94,14 +94,13 @@ static void presentation_feedback_handle_presented(void *data, uint32_t seq_hi, uint32_t seq_lo, uint32_t flags) { struct wlr_wl_presentation_feedback *feedback = data; - struct timespec t = { - .tv_sec = ((uint64_t)tv_sec_hi << 32) | tv_sec_lo, - .tv_nsec = tv_nsec, - }; struct wlr_output_event_present event = { .commit_seq = feedback->commit_seq, .presented = true, - .when = &t, + .when = { + .tv_sec = ((uint64_t)tv_sec_hi << 32) | tv_sec_lo, + .tv_nsec = tv_nsec, + }, .seq = ((uint64_t)seq_hi << 32) | seq_lo, .refresh = refresh_ns, .flags = flags, diff --git a/backend/x11/output.c b/backend/x11/output.c index 51a8459d1..b400f3df9 100644 --- a/backend/x11/output.c +++ b/backend/x11/output.c @@ -747,9 +747,6 @@ void handle_x11_present_event(struct wlr_x11_backend *x11, output->last_msc = complete_notify->msc; - struct timespec t; - timespec_from_nsec(&t, complete_notify->ust * 1000); - uint32_t flags = 0; if (complete_notify->mode == XCB_PRESENT_COMPLETE_MODE_FLIP) { flags |= WLR_OUTPUT_PRESENT_ZERO_COPY; @@ -760,10 +757,10 @@ void handle_x11_present_event(struct wlr_x11_backend *x11, .output = &output->wlr_output, .commit_seq = complete_notify->serial, .presented = presented, - .when = &t, .seq = complete_notify->msc, .flags = flags, }; + timespec_from_nsec(&present_event.when, complete_notify->ust * 1000); wlr_output_send_present(&output->wlr_output, &present_event); wlr_output_send_frame(&output->wlr_output); diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index bf80f585a..6227f0476 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -263,7 +263,7 @@ struct wlr_output_event_present { // Whether the frame was presented at all. bool presented; // Time when the content update turned into light the first time. - struct timespec *when; + struct timespec when; // Vertical retrace counter. Zero if unavailable. unsigned seq; // Prediction of how many nanoseconds after `when` the very next output diff --git a/types/output/output.c b/types/output/output.c index 6755493fb..2dcd24229 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -794,14 +794,12 @@ void wlr_output_send_present(struct wlr_output *output, assert(event); event->output = output; - struct timespec now; - if (event->presented && event->when == NULL) { - if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) { + if (event->presented && (event->when.tv_sec == 0 && event->when.tv_nsec == 0)) { + if (clock_gettime(CLOCK_MONOTONIC, &event->when) != 0) { wlr_log_errno(WLR_ERROR, "failed to send output present event: " "failed to read clock"); return; } - event->when = &now; } wl_signal_emit_mutable(&output->events.present, event); diff --git a/types/wlr_presentation_time.c b/types/wlr_presentation_time.c index 35b960d02..94e379a54 100644 --- a/types/wlr_presentation_time.c +++ b/types/wlr_presentation_time.c @@ -239,8 +239,8 @@ void wlr_presentation_event_from_output(struct wlr_presentation_event *event, const struct wlr_output_event_present *output_event) { *event = (struct wlr_presentation_event){ .output = output_event->output, - .tv_sec = (uint64_t)output_event->when->tv_sec, - .tv_nsec = (uint32_t)output_event->when->tv_nsec, + .tv_sec = (uint64_t)output_event->when.tv_sec, + .tv_nsec = (uint32_t)output_event->when.tv_nsec, .refresh = (uint32_t)output_event->refresh, .seq = (uint64_t)output_event->seq, .flags = output_event->flags, From d2a5dbe1041550f2e6f9202b4d99828762f289fb Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 20 Aug 2024 19:46:38 +0200 Subject: [PATCH 092/519] backend/drm: use CRTCs in-order When lighting up a new connector, we'd use the last CRTC instead of the first one. This causes issues because drivers have the expectation that userspace will match CRTCs to connectors in-order [1]. The order has regressed a long time ago in 5b13b8a12c55 ("backend/drm: consider continue not using resources"). That commit was a fix to avoid moving a connector between CRTCs [2]. Revert that commit and use a different approach: even if we've found a solution, always try not using a CRTC in the hope that we'll find another solution with less CRTC replacements. [1]: https://lore.kernel.org/dri-devel/20240612141903.17219-2-ville.syrjala@linux.intel.com/ [2]: https://github.com/swaywm/wlroots/issues/1230 Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3098 --- backend/drm/util.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/backend/drm/util.c b/backend/drm/util.c index a14e5e1ef..7e52fc11e 100644 --- a/backend/drm/util.c +++ b/backend/drm/util.c @@ -170,12 +170,6 @@ static bool match_obj_(struct match_state *st, size_t skips, size_t score, size_ has_best = true; } } - if (st->orig[i] == UNMATCHED) { - st->res[i] = UNMATCHED; - if (match_obj_(st, skips, score, replaced, i + 1)) { - has_best = true; - } - } if (st->exit_early) { return true; } @@ -211,13 +205,13 @@ static bool match_obj_(struct match_state *st, size_t skips, size_t score, size_ } } - if (has_best) { - return true; - } - // Maybe this resource can't be matched st->res[i] = UNMATCHED; - return match_obj_(st, skips, score, replaced, i + 1); + if (match_obj_(st, skips, score, replaced, i + 1)) { + has_best = true; + } + + return has_best; } size_t match_obj(size_t num_objs, const uint32_t objs[static restrict num_objs], From 0bf642d2461e4a6735e0a0cb6588f8afbd11718e Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 21 Aug 2024 21:56:39 +0200 Subject: [PATCH 093/519] backend/drm: use more descriptive names for match_obj() This function is only used for connectors and CRTCs, so instead of the abstract "obj"/"resource" wording, just use the concrete names. --- backend/drm/drm.c | 2 +- backend/drm/util.c | 60 ++++++++++++++++++++------------------ include/backend/drm/util.h | 24 ++++++++------- 3 files changed, 45 insertions(+), 41 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 6e3b5e99f..3005d0b39 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -1354,7 +1354,7 @@ static void realloc_crtcs(struct wlr_drm_backend *drm, ++i; } - match_obj(num_connectors, connector_constraints, + match_connectors_with_crtcs(num_connectors, connector_constraints, drm->num_crtcs, previous_match, new_match); // Converts our crtc=>connector result into a connector=>crtc one. diff --git a/backend/drm/util.c b/backend/drm/util.c index 7e52fc11e..e4cd55d5c 100644 --- a/backend/drm/util.c +++ b/backend/drm/util.c @@ -112,9 +112,9 @@ static bool is_taken(size_t n, const uint32_t arr[static n], uint32_t key) { * passing 12 arguments to a function. */ struct match_state { - const size_t num_objs; - const uint32_t *restrict objs; - const size_t num_res; + const size_t num_conns; + const uint32_t *restrict conns; + const size_t num_crtcs; size_t score; size_t replaced; uint32_t *restrict res; @@ -127,23 +127,24 @@ struct match_state { * skips: The number of SKIP elements encountered so far. * score: The number of resources we've matched so far. * replaced: The number of changes from the original solution. - * i: The index of the current element. + * i: The index of the current CRTC. * * This tries to match a solution as close to st->orig as it can. * * Returns whether we've set a new best element with this solution. */ -static bool match_obj_(struct match_state *st, size_t skips, size_t score, size_t replaced, size_t i) { +static bool match_connectors_with_crtcs_(struct match_state *st, size_t skips, + size_t score, size_t replaced, size_t i) { // Finished - if (i >= st->num_res) { + if (i >= st->num_crtcs) { if (score > st->score || (score == st->score && replaced < st->replaced)) { st->score = score; st->replaced = replaced; - memcpy(st->best, st->res, sizeof(st->best[0]) * st->num_res); + memcpy(st->best, st->res, sizeof(st->best[0]) * st->num_crtcs); - st->exit_early = (st->score == st->num_res - skips - || st->score == st->num_objs) + st->exit_early = (st->score == st->num_crtcs - skips + || st->score == st->num_conns) && st->replaced == 0; return true; @@ -154,7 +155,7 @@ static bool match_obj_(struct match_state *st, size_t skips, size_t score, size_ if (st->orig[i] == SKIP) { st->res[i] = SKIP; - return match_obj_(st, skips + 1, score, replaced, i + 1); + return match_connectors_with_crtcs_(st, skips + 1, score, replaced, i + 1); } bool has_best = false; @@ -165,8 +166,8 @@ static bool match_obj_(struct match_state *st, size_t skips, size_t score, size_ */ if (st->orig[i] != UNMATCHED && !is_taken(i, st->res, st->orig[i])) { st->res[i] = st->orig[i]; - size_t obj_score = st->objs[st->res[i]] != 0 ? 1 : 0; - if (match_obj_(st, skips, score + obj_score, replaced, i + 1)) { + size_t crtc_score = st->conns[st->res[i]] != 0 ? 1 : 0; + if (match_connectors_with_crtcs_(st, skips, score + crtc_score, replaced, i + 1)) { has_best = true; } } @@ -178,14 +179,14 @@ static bool match_obj_(struct match_state *st, size_t skips, size_t score, size_ ++replaced; } - for (size_t candidate = 0; candidate < st->num_objs; ++candidate) { + for (size_t candidate = 0; candidate < st->num_conns; ++candidate) { // We tried this earlier if (candidate == st->orig[i]) { continue; } // Not compatible - if (!(st->objs[candidate] & (1 << i))) { + if (!(st->conns[candidate] & (1 << i))) { continue; } @@ -195,8 +196,8 @@ static bool match_obj_(struct match_state *st, size_t skips, size_t score, size_ } st->res[i] = candidate; - size_t obj_score = st->objs[candidate] != 0 ? 1 : 0; - if (match_obj_(st, skips, score + obj_score, replaced, i + 1)) { + size_t crtc_score = st->conns[candidate] != 0 ? 1 : 0; + if (match_connectors_with_crtcs_(st, skips, score + crtc_score, replaced, i + 1)) { has_best = true; } @@ -205,36 +206,37 @@ static bool match_obj_(struct match_state *st, size_t skips, size_t score, size_ } } - // Maybe this resource can't be matched + // Maybe this CRTC can't be matched st->res[i] = UNMATCHED; - if (match_obj_(st, skips, score, replaced, i + 1)) { + if (match_connectors_with_crtcs_(st, skips, score, replaced, i + 1)) { has_best = true; } return has_best; } -size_t match_obj(size_t num_objs, const uint32_t objs[static restrict num_objs], - size_t num_res, const uint32_t res[static restrict num_res], - uint32_t out[static restrict num_res]) { - uint32_t solution[num_res]; - for (size_t i = 0; i < num_res; ++i) { +size_t match_connectors_with_crtcs(size_t num_conns, + const uint32_t conns[static restrict num_conns], + size_t num_crtcs, const uint32_t prev_crtcs[static restrict num_crtcs], + uint32_t new_crtcs[static restrict num_crtcs]) { + uint32_t solution[num_crtcs]; + for (size_t i = 0; i < num_crtcs; ++i) { solution[i] = UNMATCHED; } struct match_state st = { - .num_objs = num_objs, - .num_res = num_res, + .num_conns = num_conns, + .num_crtcs = num_crtcs, .score = 0, .replaced = SIZE_MAX, - .objs = objs, + .conns = conns, .res = solution, - .best = out, - .orig = res, + .best = new_crtcs, + .orig = prev_crtcs, .exit_early = false, }; - match_obj_(&st, 0, 0, 0, 0); + match_connectors_with_crtcs_(&st, 0, 0, 0, 0); return st.score; } diff --git a/include/backend/drm/util.h b/include/backend/drm/util.h index 254f774da..c4d9467b5 100644 --- a/include/backend/drm/util.h +++ b/include/backend/drm/util.h @@ -18,26 +18,28 @@ const char *drm_connector_status_str(drmModeConnection status); void generate_cvt_mode(drmModeModeInfo *mode, int hdisplay, int vdisplay, float vrefresh); -// Part of match_obj +// Part of match_connectors_with_crtcs enum { UNMATCHED = (uint32_t)-1, SKIP = (uint32_t)-2, }; -/* - * Tries to match some DRM objects with some other DRM resource. - * e.g. Match CRTCs with Encoders, CRTCs with Planes. +/** + * Tries to match DRM connectors with DRM CRTCs. * - * objs contains a bit array which resources it can be matched with. - * e.g. Bit 0 set means can be matched with res[0] + * conns contains an array of bitmasks describing compatible CRTCs. For + * instance bit 0 set in an connector element means that it's compatible with + * CRTC 0. * - * res contains an index of which objs it is matched with or UNMATCHED. + * prev_crtcs contains connector indices each CRTC was previously matched with, + * or UNMATCHED. * - * This solution is left in out. + * new_crtcs is populated with the new connector indices. * Returns the total number of matched solutions. */ -size_t match_obj(size_t num_objs, const uint32_t objs[static restrict num_objs], - size_t num_res, const uint32_t res[static restrict num_res], - uint32_t out[static restrict num_res]); +size_t match_connectors_with_crtcs(size_t num_conns, + const uint32_t conns[static restrict num_conns], + size_t num_crtcs, const uint32_t prev_crtcs[static restrict num_crtcs], + uint32_t new_crtcs[static restrict num_crtcs]); #endif From 1e03719361b112ee259db3ed40f8012def6d7f2d Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 21 Aug 2024 22:02:51 +0200 Subject: [PATCH 094/519] backend/drm: drop match_connectors_with_crtcs() return value It's unused. --- backend/drm/util.c | 3 +-- include/backend/drm/util.h | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/backend/drm/util.c b/backend/drm/util.c index e4cd55d5c..abbc9c233 100644 --- a/backend/drm/util.c +++ b/backend/drm/util.c @@ -215,7 +215,7 @@ static bool match_connectors_with_crtcs_(struct match_state *st, size_t skips, return has_best; } -size_t match_connectors_with_crtcs(size_t num_conns, +void match_connectors_with_crtcs(size_t num_conns, const uint32_t conns[static restrict num_conns], size_t num_crtcs, const uint32_t prev_crtcs[static restrict num_crtcs], uint32_t new_crtcs[static restrict num_crtcs]) { @@ -237,7 +237,6 @@ size_t match_connectors_with_crtcs(size_t num_conns, }; match_connectors_with_crtcs_(&st, 0, 0, 0, 0); - return st.score; } void generate_cvt_mode(drmModeModeInfo *mode, int hdisplay, int vdisplay, diff --git a/include/backend/drm/util.h b/include/backend/drm/util.h index c4d9467b5..85a8f4722 100644 --- a/include/backend/drm/util.h +++ b/include/backend/drm/util.h @@ -35,9 +35,8 @@ enum { * or UNMATCHED. * * new_crtcs is populated with the new connector indices. - * Returns the total number of matched solutions. */ -size_t match_connectors_with_crtcs(size_t num_conns, +void match_connectors_with_crtcs(size_t num_conns, const uint32_t conns[static restrict num_conns], size_t num_crtcs, const uint32_t prev_crtcs[static restrict num_crtcs], uint32_t new_crtcs[static restrict num_crtcs]); From 5f3b99bbedf074c47a266a5a0ba9dc9f670e84d8 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 21 Aug 2024 22:12:22 +0200 Subject: [PATCH 095/519] backend/drm: rename i param in match_connectors_with_crtcs_() Use a more descriptive name to make it clear what kind of index this is. --- backend/drm/util.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/backend/drm/util.c b/backend/drm/util.c index abbc9c233..45ec207f0 100644 --- a/backend/drm/util.c +++ b/backend/drm/util.c @@ -127,16 +127,16 @@ struct match_state { * skips: The number of SKIP elements encountered so far. * score: The number of resources we've matched so far. * replaced: The number of changes from the original solution. - * i: The index of the current CRTC. + * crtc_index: The index of the current CRTC. * * This tries to match a solution as close to st->orig as it can. * * Returns whether we've set a new best element with this solution. */ static bool match_connectors_with_crtcs_(struct match_state *st, size_t skips, - size_t score, size_t replaced, size_t i) { + size_t score, size_t replaced, size_t crtc_index) { // Finished - if (i >= st->num_crtcs) { + if (crtc_index >= st->num_crtcs) { if (score > st->score || (score == st->score && replaced < st->replaced)) { st->score = score; @@ -153,9 +153,9 @@ static bool match_connectors_with_crtcs_(struct match_state *st, size_t skips, } } - if (st->orig[i] == SKIP) { - st->res[i] = SKIP; - return match_connectors_with_crtcs_(st, skips + 1, score, replaced, i + 1); + if (st->orig[crtc_index] == SKIP) { + st->res[crtc_index] = SKIP; + return match_connectors_with_crtcs_(st, skips + 1, score, replaced, crtc_index + 1); } bool has_best = false; @@ -164,10 +164,10 @@ static bool match_connectors_with_crtcs_(struct match_state *st, size_t skips, * Attempt to use the current solution first, to try and avoid * recalculating everything */ - if (st->orig[i] != UNMATCHED && !is_taken(i, st->res, st->orig[i])) { - st->res[i] = st->orig[i]; - size_t crtc_score = st->conns[st->res[i]] != 0 ? 1 : 0; - if (match_connectors_with_crtcs_(st, skips, score + crtc_score, replaced, i + 1)) { + if (st->orig[crtc_index] != UNMATCHED && !is_taken(crtc_index, st->res, st->orig[crtc_index])) { + st->res[crtc_index] = st->orig[crtc_index]; + size_t crtc_score = st->conns[st->res[crtc_index]] != 0 ? 1 : 0; + if (match_connectors_with_crtcs_(st, skips, score + crtc_score, replaced, crtc_index + 1)) { has_best = true; } } @@ -175,29 +175,29 @@ static bool match_connectors_with_crtcs_(struct match_state *st, size_t skips, return true; } - if (st->orig[i] != UNMATCHED) { + if (st->orig[crtc_index] != UNMATCHED) { ++replaced; } for (size_t candidate = 0; candidate < st->num_conns; ++candidate) { // We tried this earlier - if (candidate == st->orig[i]) { + if (candidate == st->orig[crtc_index]) { continue; } // Not compatible - if (!(st->conns[candidate] & (1 << i))) { + if (!(st->conns[candidate] & (1 << crtc_index))) { continue; } // Already taken - if (is_taken(i, st->res, candidate)) { + if (is_taken(crtc_index, st->res, candidate)) { continue; } - st->res[i] = candidate; + st->res[crtc_index] = candidate; size_t crtc_score = st->conns[candidate] != 0 ? 1 : 0; - if (match_connectors_with_crtcs_(st, skips, score + crtc_score, replaced, i + 1)) { + if (match_connectors_with_crtcs_(st, skips, score + crtc_score, replaced, crtc_index + 1)) { has_best = true; } @@ -207,8 +207,8 @@ static bool match_connectors_with_crtcs_(struct match_state *st, size_t skips, } // Maybe this CRTC can't be matched - st->res[i] = UNMATCHED; - if (match_connectors_with_crtcs_(st, skips, score, replaced, i + 1)) { + st->res[crtc_index] = UNMATCHED; + if (match_connectors_with_crtcs_(st, skips, score, replaced, crtc_index + 1)) { has_best = true; } From 54321088460d200116e29047323503067886d698 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 21 Aug 2024 22:18:23 +0200 Subject: [PATCH 096/519] backend/drm: drop SKIP in match_connectors_with_crtcs() It's unused. --- backend/drm/util.c | 17 ++++++----------- include/backend/drm/util.h | 5 +---- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/backend/drm/util.c b/backend/drm/util.c index 45ec207f0..6ba090c18 100644 --- a/backend/drm/util.c +++ b/backend/drm/util.c @@ -133,7 +133,7 @@ struct match_state { * * Returns whether we've set a new best element with this solution. */ -static bool match_connectors_with_crtcs_(struct match_state *st, size_t skips, +static bool match_connectors_with_crtcs_(struct match_state *st, size_t score, size_t replaced, size_t crtc_index) { // Finished if (crtc_index >= st->num_crtcs) { @@ -143,7 +143,7 @@ static bool match_connectors_with_crtcs_(struct match_state *st, size_t skips, st->replaced = replaced; memcpy(st->best, st->res, sizeof(st->best[0]) * st->num_crtcs); - st->exit_early = (st->score == st->num_crtcs - skips + st->exit_early = (st->score == st->num_crtcs || st->score == st->num_conns) && st->replaced == 0; @@ -153,11 +153,6 @@ static bool match_connectors_with_crtcs_(struct match_state *st, size_t skips, } } - if (st->orig[crtc_index] == SKIP) { - st->res[crtc_index] = SKIP; - return match_connectors_with_crtcs_(st, skips + 1, score, replaced, crtc_index + 1); - } - bool has_best = false; /* @@ -167,7 +162,7 @@ static bool match_connectors_with_crtcs_(struct match_state *st, size_t skips, if (st->orig[crtc_index] != UNMATCHED && !is_taken(crtc_index, st->res, st->orig[crtc_index])) { st->res[crtc_index] = st->orig[crtc_index]; size_t crtc_score = st->conns[st->res[crtc_index]] != 0 ? 1 : 0; - if (match_connectors_with_crtcs_(st, skips, score + crtc_score, replaced, crtc_index + 1)) { + if (match_connectors_with_crtcs_(st, score + crtc_score, replaced, crtc_index + 1)) { has_best = true; } } @@ -197,7 +192,7 @@ static bool match_connectors_with_crtcs_(struct match_state *st, size_t skips, st->res[crtc_index] = candidate; size_t crtc_score = st->conns[candidate] != 0 ? 1 : 0; - if (match_connectors_with_crtcs_(st, skips, score + crtc_score, replaced, crtc_index + 1)) { + if (match_connectors_with_crtcs_(st, score + crtc_score, replaced, crtc_index + 1)) { has_best = true; } @@ -208,7 +203,7 @@ static bool match_connectors_with_crtcs_(struct match_state *st, size_t skips, // Maybe this CRTC can't be matched st->res[crtc_index] = UNMATCHED; - if (match_connectors_with_crtcs_(st, skips, score, replaced, crtc_index + 1)) { + if (match_connectors_with_crtcs_(st, score, replaced, crtc_index + 1)) { has_best = true; } @@ -236,7 +231,7 @@ void match_connectors_with_crtcs(size_t num_conns, .exit_early = false, }; - match_connectors_with_crtcs_(&st, 0, 0, 0, 0); + match_connectors_with_crtcs_(&st, 0, 0, 0); } void generate_cvt_mode(drmModeModeInfo *mode, int hdisplay, int vdisplay, diff --git a/include/backend/drm/util.h b/include/backend/drm/util.h index 85a8f4722..9ba5f435e 100644 --- a/include/backend/drm/util.h +++ b/include/backend/drm/util.h @@ -19,10 +19,7 @@ void generate_cvt_mode(drmModeModeInfo *mode, int hdisplay, int vdisplay, float vrefresh); // Part of match_connectors_with_crtcs -enum { - UNMATCHED = (uint32_t)-1, - SKIP = (uint32_t)-2, -}; +#define UNMATCHED ((uint32_t)-1) /** * Tries to match DRM connectors with DRM CRTCs. From 52dce29e060b6f79db0e68b460a2be7546e80931 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 22 Aug 2024 18:17:34 +0200 Subject: [PATCH 097/519] render/vulkan: use non-coherent memory for read_pixels() The spec for VkMemoryPropertyFlagBits says: > device coherent accesses may be slower than equivalent accesses > without device coherence [...] it is generally inadvisable to > use device coherent or device uncached memory except when really > needed We don't really need coherent memory so let's not require it and invalidate the memory range after mapping instead. Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3868 --- render/vulkan/renderer.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index 32effb5a7..327f3b465 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -1224,7 +1224,6 @@ bool vulkan_read_pixels(struct wlr_vk_renderer *vk_renderer, int mem_type = vulkan_find_mem_type(vk_renderer->dev, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | - VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, mem_reqs.memoryTypeBits); if (mem_type < 0) { @@ -1361,6 +1360,19 @@ bool vulkan_read_pixels(struct wlr_vk_renderer *vk_renderer, return false; } + VkMappedMemoryRange mem_range = { + .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, + .memory = dst_img_memory, + .offset = 0, + .size = VK_WHOLE_SIZE, + }; + res = vkInvalidateMappedMemoryRanges(dev, 1, &mem_range); + if (res != VK_SUCCESS) { + wlr_vk_error("vkInvalidateMappedMemoryRanges", res); + vkUnmapMemory(dev, dst_img_memory); + return false; + } + const char *d = (const char *)v + img_sub_layout.offset; unsigned char *p = (unsigned char *)data + dst_y * stride; uint32_t bytes_per_pixel = pixel_format_info->bytes_per_block; @@ -1376,6 +1388,7 @@ bool vulkan_read_pixels(struct wlr_vk_renderer *vk_renderer, vkUnmapMemory(dev, dst_img_memory); // Don't need to free anything else, since memory and image are cached return true; + free_memory: vkFreeMemory(dev, dst_img_memory, NULL); destroy_image: From 52afedadea9f4009712af7e21d9ef514cdf04517 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Fri, 23 Aug 2024 16:53:32 -0400 Subject: [PATCH 098/519] wlr_scene: Assert wlr_scene_rect has nonnegative dimensions --- types/scene/wlr_scene.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index bc776fdd9..713bfdfd0 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -691,11 +691,13 @@ static void scene_node_update(struct wlr_scene_node *node, struct wlr_scene_rect *wlr_scene_rect_create(struct wlr_scene_tree *parent, int width, int height, const float color[static 4]) { + assert(parent); + assert(width >= 0 && height >= 0); + struct wlr_scene_rect *scene_rect = calloc(1, sizeof(*scene_rect)); if (scene_rect == NULL) { return NULL; } - assert(parent); scene_node_init(&scene_rect->node, WLR_SCENE_NODE_RECT, parent); scene_rect->width = width; @@ -712,6 +714,8 @@ void wlr_scene_rect_set_size(struct wlr_scene_rect *rect, int width, int height) return; } + assert(width >= 0 && height >= 0); + rect->width = width; rect->height = height; scene_node_update(&rect->node, NULL); From a5aae69b2a9ff7b346b5beb460fd856493580f69 Mon Sep 17 00:00:00 2001 From: YaoBing Xiao Date: Fri, 23 Aug 2024 15:25:01 +0800 Subject: [PATCH 099/519] backend/drm: remove unnecessary semicolons --- backend/drm/drm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 3005d0b39..1ffa34d5b 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -645,7 +645,7 @@ static void drm_connector_state_init(struct wlr_drm_connector_state *state, if (base->committed & WLR_OUTPUT_STATE_MODE) { switch (base->mode_type) { - case WLR_OUTPUT_STATE_MODE_FIXED:; + case WLR_OUTPUT_STATE_MODE_FIXED: mode = base->mode; break; case WLR_OUTPUT_STATE_MODE_CUSTOM: From fa2abbeefbd123ffe97b9ca08bb723486556fd8c Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 24 Aug 2024 11:07:58 +0200 Subject: [PATCH 100/519] render/color: return tranform in wlr_color_transform_ref() This is more consistent with the rest of the wlroots APIs and is more concise. --- include/wlr/render/color.h | 2 +- render/color.c | 3 ++- render/vulkan/pass.c | 3 +-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/wlr/render/color.h b/include/wlr/render/color.h index ddd4408b4..43d9154be 100644 --- a/include/wlr/render/color.h +++ b/include/wlr/render/color.h @@ -44,7 +44,7 @@ struct wlr_color_transform *wlr_color_transform_init_srgb(void); /** * Increase the reference count of the color transform by 1. */ -void wlr_color_transform_ref(struct wlr_color_transform *tr); +struct wlr_color_transform *wlr_color_transform_ref(struct wlr_color_transform *tr); /** * Reduce the reference count of the color transform by 1; freeing it and diff --git a/render/color.c b/render/color.c index 639b004c8..e68c2d50d 100644 --- a/render/color.c +++ b/render/color.c @@ -20,8 +20,9 @@ static void color_transform_destroy(struct wlr_color_transform *tr) { free(tr); } -void wlr_color_transform_ref(struct wlr_color_transform *tr) { +struct wlr_color_transform *wlr_color_transform_ref(struct wlr_color_transform *tr) { tr->ref_count += 1; + return tr; } void wlr_color_transform_unref(struct wlr_color_transform *tr) { diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 3697dcda6..5820cf9de 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -954,8 +954,7 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend pass->renderer = renderer; pass->srgb_pathway = using_srgb_pathway; if (options != NULL && options->color_transform != NULL) { - wlr_color_transform_ref(options->color_transform); - pass->color_transform = options->color_transform; + pass->color_transform = wlr_color_transform_ref(options->color_transform); } rect_union_init(&pass->updated_region); From 3187479c07c34a4de82c06a316a763a36a0499da Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sat, 24 Aug 2024 14:33:22 -0400 Subject: [PATCH 101/519] render/color: Invert ownership model of color_transform types. Color transform can have multiple types and these different types want to store different metadata. We previously stored this metadata directly on wlr_color_transform even for transforms that don't use it. Instead, let's take the prior art from wlr_scene where each scene node is built on a base node. Notice how wlr_color_transform_lut3d now has a `struct wlr_color_transform base`. This is advantageous in multiple ways: 1. We don't allocate memory for metadata that will never be used. 2. This is more type safe: Compositors can pass around a struct wlr_color_transform_lut3d if they know they only want to use a 3d_lut. 3. This is more scalable. As we add more transform types, we don't have to keep growing a monolithic struct. --- include/render/color.h | 32 ++++++++++++++++++++------------ render/color.c | 17 ++++++++++++++++- render/color_lcms2.c | 16 ++++++++-------- render/vulkan/pass.c | 12 ++++++++++-- 4 files changed, 54 insertions(+), 23 deletions(-) diff --git a/include/render/color.h b/include/render/color.h index e18f12eeb..4ab587be1 100644 --- a/include/render/color.h +++ b/include/render/color.h @@ -4,6 +4,18 @@ #include #include +enum wlr_color_transform_type { + COLOR_TRANSFORM_SRGB, + COLOR_TRANSFORM_LUT_3D, +}; + +struct wlr_color_transform { + int ref_count; + struct wlr_addon_set addons; // per-renderer helper state + + enum wlr_color_transform_type type; +}; + /** * The formula is approximated via a 3D look-up table. A 3D LUT is a * three-dimensional array where each element is an RGB triplet. The flat lut_3d @@ -19,21 +31,17 @@ * offset = 3 * (r_index + dim_len * g_index + dim_len * dim_len * b_index) */ struct wlr_color_transform_lut3d { + struct wlr_color_transform base; + float *lut_3d; size_t dim_len; }; -enum wlr_color_transform_type { - COLOR_TRANSFORM_SRGB, - COLOR_TRANSFORM_LUT_3D, -}; - -struct wlr_color_transform { - int ref_count; - struct wlr_addon_set addons; // per-renderer helper state - - enum wlr_color_transform_type type; - struct wlr_color_transform_lut3d lut3d; -}; +/** + * Gets a wlr_color_transform_lut3d from a generic wlr_color_transform. + * Asserts that the base type is COLOR_TRANSFORM_LUT_3D + */ +struct wlr_color_transform_lut3d *wlr_color_transform_lut3d_from_base( + struct wlr_color_transform *tr); #endif diff --git a/render/color.c b/render/color.c index e68c2d50d..9f8d76b06 100644 --- a/render/color.c +++ b/render/color.c @@ -15,7 +15,15 @@ struct wlr_color_transform *wlr_color_transform_init_srgb(void) { } static void color_transform_destroy(struct wlr_color_transform *tr) { - free(tr->lut3d.lut_3d); + switch (tr->type) { + case COLOR_TRANSFORM_SRGB: + break; + case COLOR_TRANSFORM_LUT_3D:; + struct wlr_color_transform_lut3d *lut3d = + wlr_color_transform_lut3d_from_base(tr); + free(lut3d->lut_3d); + break; + } wlr_addon_set_finish(&tr->addons); free(tr); } @@ -35,3 +43,10 @@ void wlr_color_transform_unref(struct wlr_color_transform *tr) { color_transform_destroy(tr); } } + +struct wlr_color_transform_lut3d *wlr_color_transform_lut3d_from_base( + struct wlr_color_transform *tr) { + assert(tr->type == COLOR_TRANSFORM_LUT_3D); + struct wlr_color_transform_lut3d *lut3d = wl_container_of(tr, lut3d, base); + return lut3d; +} diff --git a/render/color_lcms2.c b/render/color_lcms2.c index fae4fb6a8..c0f4db817 100644 --- a/render/color_lcms2.c +++ b/render/color_lcms2.c @@ -18,7 +18,7 @@ static void handle_lcms_error(cmsContext ctx, cmsUInt32Number code, const char * struct wlr_color_transform *wlr_color_transform_init_linear_to_icc( const void *data, size_t size) { - struct wlr_color_transform *tx = NULL; + struct wlr_color_transform_lut3d *tx = NULL; cmsContext ctx = cmsCreateContext(NULL, NULL); if (ctx == NULL) { @@ -95,15 +95,15 @@ struct wlr_color_transform *wlr_color_transform_init_linear_to_icc( } } - tx = calloc(1, sizeof(struct wlr_color_transform)); + tx = calloc(1, sizeof(struct wlr_color_transform_lut3d)); if (!tx) { goto out_lcms_tr; } - tx->type = COLOR_TRANSFORM_LUT_3D; - tx->lut3d.dim_len = dim_len; - tx->lut3d.lut_3d = lut_3d; - tx->ref_count = 1; - wlr_addon_set_init(&tx->addons); + tx->base.type = COLOR_TRANSFORM_LUT_3D; + tx->dim_len = dim_len; + tx->lut_3d = lut_3d; + tx->base.ref_count = 1; + wlr_addon_set_init(&tx->base.addons); out_lcms_tr: cmsDeleteTransform(lcms_tr); @@ -115,5 +115,5 @@ out_icc_profile: cmsCloseProfile(icc_profile); out_ctx: cmsDeleteContext(ctx); - return tx; + return &tx->base; } diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 5820cf9de..8e1807dfa 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -116,7 +116,14 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { .uv_off = { 0, 0 }, .uv_size = { 1, 1 }, }; - size_t dim = pass->color_transform ? pass->color_transform->lut3d.dim_len : 1; + + size_t dim = 1; + if (pass->color_transform && pass->color_transform->type == COLOR_TRANSFORM_LUT_3D) { + struct wlr_color_transform_lut3d *lut3d = + wlr_color_transform_lut3d_from_base(pass->color_transform); + dim = lut3d->dim_len; + } + struct wlr_vk_frag_output_pcr_data frag_pcr_data = { .lut_3d_offset = 0.5f / dim, .lut_3d_scale = (float)(dim - 1) / dim, @@ -894,7 +901,8 @@ static struct wlr_vk_color_transform *vk_color_transform_create( } if (transform->type == COLOR_TRANSFORM_LUT_3D) { - if (!create_3d_lut_image(renderer, &transform->lut3d, + if (!create_3d_lut_image(renderer, + wlr_color_transform_lut3d_from_base(transform), &vk_transform->lut_3d.image, &vk_transform->lut_3d.image_view, &vk_transform->lut_3d.memory, From d7223eae021f86eddcaaacb808598a393de9d114 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 5 Aug 2024 19:12:53 +0200 Subject: [PATCH 102/519] backend/drm: add explicit sync support to multi-GPU blits --- backend/drm/atomic.c | 6 +++--- backend/drm/drm.c | 31 +++++++++++++++++++++++++++---- backend/drm/renderer.c | 24 ++++++++++++++++++++++-- include/backend/drm/drm.h | 3 +++ include/backend/drm/renderer.h | 6 +++++- 5 files changed, 60 insertions(+), 10 deletions(-) diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index d3a91efbe..16b08f7a1 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -275,9 +275,9 @@ bool drm_atomic_connector_prepare(struct wlr_drm_connector_state *state, bool mo } int in_fence_fd = -1; - if (state->base->committed & WLR_OUTPUT_STATE_WAIT_TIMELINE) { - in_fence_fd = wlr_drm_syncobj_timeline_export_sync_file(state->base->wait_timeline, - state->base->wait_point); + if (state->wait_timeline != NULL) { + in_fence_fd = wlr_drm_syncobj_timeline_export_sync_file(state->wait_timeline, + state->wait_point); if (in_fence_fd < 0) { return false; } diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 1ffa34d5b..c81517a4d 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -693,6 +694,7 @@ static void drm_connector_state_init(struct wlr_drm_connector_state *state, static void drm_connector_state_finish(struct wlr_drm_connector_state *state) { drm_fb_clear(&state->primary_fb); drm_fb_clear(&state->cursor_fb); + wlr_drm_syncobj_timeline_unref(state->wait_timeline); } static bool drm_connector_state_update_primary_fb(struct wlr_drm_connector *conn, @@ -707,6 +709,14 @@ static bool drm_connector_state_update_primary_fb(struct wlr_drm_connector *conn struct wlr_drm_plane *plane = crtc->primary; struct wlr_buffer *source_buf = state->base->buffer; + struct wlr_drm_syncobj_timeline *wait_timeline = NULL; + uint64_t wait_point = 0; + if (state->base->committed & WLR_OUTPUT_STATE_WAIT_TIMELINE) { + wait_timeline = state->base->wait_timeline; + wait_point = state->base->wait_point; + } + assert(state->wait_timeline == NULL); + struct wlr_buffer *local_buf; if (drm->parent) { struct wlr_drm_format format = {0}; @@ -723,12 +733,23 @@ static bool drm_connector_state_update_primary_fb(struct wlr_drm_connector *conn return false; } - local_buf = drm_surface_blit(&plane->mgpu_surf, source_buf); + local_buf = drm_surface_blit(&plane->mgpu_surf, source_buf, + wait_timeline, wait_point); if (local_buf == NULL) { return false; } + + if (plane->mgpu_surf.timeline != NULL) { + state->wait_timeline = wlr_drm_syncobj_timeline_ref(plane->mgpu_surf.timeline); + state->wait_point = plane->mgpu_surf.point; + } } else { local_buf = wlr_buffer_lock(source_buf); + + if (wait_timeline != NULL) { + state->wait_timeline = wlr_drm_syncobj_timeline_ref(wait_timeline); + state->wait_point = wait_point; + } } bool ok = drm_fb_import(&state->primary_fb, drm, local_buf, @@ -1088,7 +1109,7 @@ static bool drm_connector_set_cursor(struct wlr_output *output, return false; } - local_buf = drm_surface_blit(&plane->mgpu_surf, buffer); + local_buf = drm_surface_blit(&plane->mgpu_surf, buffer, NULL, 0); if (local_buf == NULL) { return false; } @@ -1625,9 +1646,11 @@ static bool connect_drm_connector(struct wlr_drm_connector *wlr_conn, output->non_desktop = non_desktop; } - // TODO: support sync timelines in multi-GPU mode // TODO: support sync timelines with libliftoff - output->timeline = drm->parent == NULL && drm->iface == &atomic_iface; + output->timeline = drm->iface == &atomic_iface; + if (drm->parent) { + output->timeline = output->timeline && drm->mgpu_renderer.wlr_rend->features.timeline; + } memset(wlr_conn->max_bpc_bounds, 0, sizeof(wlr_conn->max_bpc_bounds)); if (wlr_conn->props.max_bpc != 0) { diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c index 37569c182..60acc57b9 100644 --- a/backend/drm/renderer.c +++ b/backend/drm/renderer.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -46,6 +47,7 @@ void finish_drm_surface(struct wlr_drm_surface *surf) { return; } + wlr_drm_syncobj_timeline_unref(surf->timeline); wlr_swapchain_destroy(surf->swapchain); *surf = (struct wlr_drm_surface){0}; @@ -68,13 +70,24 @@ bool init_drm_surface(struct wlr_drm_surface *surf, return false; } + int drm_fd = wlr_renderer_get_drm_fd(renderer->wlr_rend); + if (renderer->wlr_rend->features.timeline && drm_fd >= 0) { + surf->timeline = wlr_drm_syncobj_timeline_create(drm_fd); + if (surf->timeline == NULL) { + finish_drm_surface(surf); + wlr_log(WLR_ERROR, "Failed to create DRM syncobj timeline"); + return false; + } + } + surf->renderer = renderer; return true; } struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf, - struct wlr_buffer *buffer) { + struct wlr_buffer *buffer, + struct wlr_drm_syncobj_timeline *wait_timeline, uint64_t wait_point) { struct wlr_renderer *renderer = surf->renderer->wlr_rend; if (surf->swapchain->width != buffer->width || @@ -95,7 +108,12 @@ struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf, goto error_tex; } - struct wlr_render_pass *pass = wlr_renderer_begin_buffer_pass(renderer, dst, NULL); + surf->point++; + const struct wlr_buffer_pass_options pass_options = { + .signal_timeline = surf->timeline, + .signal_point = surf->point, + }; + struct wlr_render_pass *pass = wlr_renderer_begin_buffer_pass(renderer, dst, &pass_options); if (pass == NULL) { wlr_log(WLR_ERROR, "Failed to begin render pass with multi-GPU destination buffer"); goto error_dst; @@ -104,6 +122,8 @@ struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf, wlr_render_pass_add_texture(pass, &(struct wlr_render_texture_options){ .texture = tex, .blend_mode = WLR_RENDER_BLEND_MODE_NONE, + .wait_timeline = wait_timeline, + .wait_point = wait_point, }); if (!wlr_render_pass_submit(pass)) { wlr_log(WLR_ERROR, "Failed to submit multi-GPU render pass"); diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index 33b729aae..5b239a18e 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -141,6 +141,9 @@ struct wlr_drm_connector_state { struct wlr_drm_fb *primary_fb; struct wlr_drm_fb *cursor_fb; + struct wlr_drm_syncobj_timeline *wait_timeline; + uint64_t wait_point; + // used by atomic uint32_t mode_id; uint32_t gamma_lut; diff --git a/include/backend/drm/renderer.h b/include/backend/drm/renderer.h index f53f720bc..2cf98fdb9 100644 --- a/include/backend/drm/renderer.h +++ b/include/backend/drm/renderer.h @@ -20,6 +20,9 @@ struct wlr_drm_renderer { struct wlr_drm_surface { struct wlr_drm_renderer *renderer; struct wlr_swapchain *swapchain; + + struct wlr_drm_syncobj_timeline *timeline; + uint64_t point; }; bool init_drm_renderer(struct wlr_drm_backend *drm, @@ -32,7 +35,8 @@ bool init_drm_surface(struct wlr_drm_surface *surf, void finish_drm_surface(struct wlr_drm_surface *surf); struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf, - struct wlr_buffer *buffer); + struct wlr_buffer *buffer, + struct wlr_drm_syncobj_timeline *wait_timeline, uint64_t wait_point); bool drm_plane_pick_render_format(struct wlr_drm_plane *plane, struct wlr_drm_format *fmt, struct wlr_drm_renderer *renderer); From 0d6284eb62703ae2fdfb8d816455a0d3505f9d66 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 6 Aug 2024 19:55:37 +0200 Subject: [PATCH 103/519] backend/drm: add explicit sync support to libliftoff interface --- backend/drm/drm.c | 3 +-- backend/drm/libliftoff.c | 11 +++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index c81517a4d..1a1af3099 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -1646,8 +1646,7 @@ static bool connect_drm_connector(struct wlr_drm_connector *wlr_conn, output->non_desktop = non_desktop; } - // TODO: support sync timelines with libliftoff - output->timeline = drm->iface == &atomic_iface; + output->timeline = drm->iface != &legacy_iface; if (drm->parent) { output->timeline = output->timeline && drm->mgpu_renderer.wlr_rend->features.timeline; } diff --git a/backend/drm/libliftoff.c b/backend/drm/libliftoff.c index 85054476c..a87a48265 100644 --- a/backend/drm/libliftoff.c +++ b/backend/drm/libliftoff.c @@ -339,6 +339,17 @@ static bool add_connector(drmModeAtomicReq *req, liftoff_layer_set_property(crtc->liftoff_composition_layer, "FB_DAMAGE_CLIPS", state->fb_damage_clips); + if (state->primary_in_fence_fd >= 0) { + liftoff_layer_set_property(crtc->primary->liftoff_layer, + "IN_FENCE_FD", state->primary_in_fence_fd); + liftoff_layer_set_property(crtc->liftoff_composition_layer, + "IN_FENCE_FD", state->primary_in_fence_fd); + } + if (state->base->committed & WLR_OUTPUT_STATE_SIGNAL_TIMELINE) { + ok = ok && add_prop(req, crtc->id, crtc->props.out_fence_ptr, + (uintptr_t)&state->out_fence_fd); + } + if (state->base->committed & WLR_OUTPUT_STATE_LAYERS) { for (size_t i = 0; i < state->base->layers_len; i++) { const struct wlr_output_layer_state *layer_state = &state->base->layers[i]; From 0db4df4c8e2460573af7a309c2e9b0c3c53663af Mon Sep 17 00:00:00 2001 From: YaoBing Xiao Date: Mon, 26 Aug 2024 23:57:26 +0800 Subject: [PATCH 104/519] gles2/pass: remove duplicate variable declarations --- render/gles2/pass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/render/gles2/pass.c b/render/gles2/pass.c index 2942ab587..71baf2cec 100644 --- a/render/gles2/pass.c +++ b/render/gles2/pass.c @@ -52,7 +52,7 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { goto out; } - bool ok = wlr_drm_syncobj_timeline_import_sync_file(pass->signal_timeline, pass->signal_point, sync_file_fd); + ok = wlr_drm_syncobj_timeline_import_sync_file(pass->signal_timeline, pass->signal_point, sync_file_fd); close(sync_file_fd); if (!ok) { goto out; From beb9a9ad0a38867154b7606911c33ffa5ecf759f Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Wed, 28 Aug 2024 18:10:10 +0200 Subject: [PATCH 105/519] linux-drm-syncobj-v1: Skip release if there is no timeline If a surface with an existing buffer has a syncobj surface state created without committing a new buffer with associated timelines, callers will see the surface as having a syncobj state and may try to use it, but calling the signal_release_with_buffer helper at this time will assert on the lacking release timeline. As this is a valid situation, remove the assert and replace it with an early return so that callers do not need to explicitly check for the presence of valid timelines. Fixes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3895 --- types/wlr_linux_drm_syncobj_v1.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/types/wlr_linux_drm_syncobj_v1.c b/types/wlr_linux_drm_syncobj_v1.c index c2e339a4e..0713bcce6 100644 --- a/types/wlr_linux_drm_syncobj_v1.c +++ b/types/wlr_linux_drm_syncobj_v1.c @@ -492,7 +492,12 @@ static void release_signaller_handle_buffer_release(struct wl_listener *listener bool wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer( struct wlr_linux_drm_syncobj_surface_v1_state *state, struct wlr_buffer *buffer) { assert(buffer->n_locks > 0); - assert(state->release_timeline != NULL); + if (state->release_timeline == NULL) { + // This can happen if an existing surface with a buffer has a + // syncobj_surface_v1_state created but no new buffer with release + // timeline committed. + return true; + } struct release_signaller *signaller = calloc(1, sizeof(*signaller)); if (signaller == NULL) { From 234d31f13832a701e2d2fa266ced76ba8d95f289 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 21 Aug 2024 23:54:24 +0200 Subject: [PATCH 106/519] backend/drm: improve doc comment for match_connectors_with_crtcs_() - Add general description - Mention the computer theory problem that this is solving - More wording cleanup --- backend/drm/util.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/backend/drm/util.c b/backend/drm/util.c index 6ba090c18..f30898966 100644 --- a/backend/drm/util.c +++ b/backend/drm/util.c @@ -123,9 +123,12 @@ struct match_state { bool exit_early; }; -/* - * skips: The number of SKIP elements encountered so far. - * score: The number of resources we've matched so far. +/** + * Step to process a CRTC. + * + * This is a naive implementation of maximum bipartite matching. + * + * score: The number of connectors we've matched so far. * replaced: The number of changes from the original solution. * crtc_index: The index of the current CRTC. * From 1a7981f7c9120d0d0b0cc935d96bdc5a8a39f6bb Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 5 Sep 2024 19:49:20 +0200 Subject: [PATCH 107/519] compositor: document surface events --- include/wlr/types/wlr_compositor.h | 31 ++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/include/wlr/types/wlr_compositor.h b/include/wlr/types/wlr_compositor.h index d0d25b7aa..6ba8d8a07 100644 --- a/include/wlr/types/wlr_compositor.h +++ b/include/wlr/types/wlr_compositor.h @@ -187,28 +187,47 @@ struct wlr_surface { struct wl_resource *role_resource; struct { + /** + * Signals that the client has sent a wl_surface.commit request. + * + * The state to be committed can be accessed in wlr_surface.pending. + * + * The commit may not be applied immediately, in which case it's marked + * as "cached" and put into a queue. See wlr_surface_lock_pending(). + */ struct wl_signal client_commit; + /** + * Signals that a commit has been applied. + * + * The new state can be accessed in wlr_surface.current. + */ struct wl_signal commit; /** - * The `map` event signals that the surface has a non-null buffer - * committed and is ready to be displayed. + * Signals that the surface has a non-null buffer committed and is + * ready to be displayed. */ struct wl_signal map; /** - * The `unmap` event signals that the surface shouldn't be displayed - * anymore. This can happen when a null buffer is committed, - * the associated role object is destroyed, or when the role-specific - * conditions for the surface to be mapped no longer apply. + * Signals that the surface shouldn't be displayed anymore. This can + * happen when a null buffer is committed, the associated role object + * is destroyed, or when the role-specific conditions for the surface + * to be mapped no longer apply. */ struct wl_signal unmap; /** + * Signals that a new child sub-surface has been added. + * * Note: unlike other new_* signals, new_subsurface is emitted when * the subsurface is added to the parent surface's current state, * not when the object is created. */ struct wl_signal new_subsurface; // struct wlr_subsurface + + /** + * Signals that the surface is being destroyed. + */ struct wl_signal destroy; } events; From 9f7ab85718302955cb39454e675b419527177e52 Mon Sep 17 00:00:00 2001 From: YaoBing Xiao Date: Mon, 9 Sep 2024 16:12:01 +0800 Subject: [PATCH 108/519] tearing_control_v1: Free the wlr_tearing_control_v1 on error Signed-off-by: YaoBing Xiao --- types/wlr_tearing_control_v1.c | 1 + 1 file changed, 1 insertion(+) diff --git a/types/wlr_tearing_control_v1.c b/types/wlr_tearing_control_v1.c index cd7cd5a59..e5a88fec5 100644 --- a/types/wlr_tearing_control_v1.c +++ b/types/wlr_tearing_control_v1.c @@ -122,6 +122,7 @@ static void tearing_control_manager_handle_get_tearing_control( wl_resource_get_version(resource), id); if (created_resource == NULL) { + free(hint); wl_resource_post_no_memory(resource); return; } From 96ad414ec9732e32b28f960c3bfa56a7b530b3cd Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Fri, 6 Sep 2024 21:43:33 +0200 Subject: [PATCH 109/519] backend/drm: Remove call to CRTC realloc on scan After a connector scan, new connectors might have appeared and old ones gone away. At this point, old CRTC allocations are already gone, while new allocations are not yet needed. Skip the call. --- backend/drm/drm.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 1a1af3099..a20442d40 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -1811,8 +1811,6 @@ void scan_drm_connectors(struct wlr_drm_backend *drm, destroy_drm_connector(conn); } - realloc_crtcs(drm, NULL); - for (size_t i = 0; i < new_outputs_len; ++i) { struct wlr_drm_connector *conn = new_outputs[i]; From 04525e6f8207c97f13c2fef5d604a997613322e9 Mon Sep 17 00:00:00 2001 From: YaoBing Xiao Date: Tue, 10 Sep 2024 11:47:32 +0800 Subject: [PATCH 110/519] wlr_screencopy_v1: send fine-grained damage events Signed-off-by: YaoBing Xiao --- types/wlr_screencopy_v1.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/types/wlr_screencopy_v1.c b/types/wlr_screencopy_v1.c index c1fbb59ce..47cb5a3cf 100644 --- a/types/wlr_screencopy_v1.c +++ b/types/wlr_screencopy_v1.c @@ -165,17 +165,19 @@ static void frame_send_damage(struct wlr_screencopy_frame_v1 *frame) { return; } - // TODO: send fine-grained damage events - struct pixman_box32 *damage_box = - pixman_region32_extents(&damage->damage); + int n_boxes; + const pixman_box32_t *boxes = pixman_region32_rectangles(&damage->damage, &n_boxes); + for (int i = 0; i < n_boxes; i++) { + const pixman_box32_t *box = &boxes[i]; - int damage_x = damage_box->x1; - int damage_y = damage_box->y1; - int damage_width = damage_box->x2 - damage_box->x1; - int damage_height = damage_box->y2 - damage_box->y1; + int damage_x = box->x1; + int damage_y = box->y1; + int damage_width = box->x2 - box->x1; + int damage_height = box->y2 - box->y1; - zwlr_screencopy_frame_v1_send_damage(frame->resource, - damage_x, damage_y, damage_width, damage_height); + zwlr_screencopy_frame_v1_send_damage(frame->resource, + damage_x, damage_y, damage_width, damage_height); + } pixman_region32_clear(&damage->damage); } From bf0cac12a3ac68841245d0af19aafd5f43d42cb1 Mon Sep 17 00:00:00 2001 From: llyyr Date: Sun, 8 Sep 2024 23:20:30 +0530 Subject: [PATCH 111/519] input-method-v2: set no keymap format if no keymap is set Also don't copy the keymap each time it gets sent to a client --- types/wlr_input_method_v2.c | 56 +++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/types/wlr_input_method_v2.c b/types/wlr_input_method_v2.c index 257666c46..e187ea74c 100644 --- a/types/wlr_input_method_v2.c +++ b/types/wlr_input_method_v2.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -306,34 +307,30 @@ void wlr_input_method_keyboard_grab_v2_send_modifiers( modifiers->locked, modifiers->group); } -static bool keyboard_grab_send_keymap( +static void keyboard_grab_send_keymap( struct wlr_input_method_keyboard_grab_v2 *keyboard_grab, struct wlr_keyboard *keyboard) { - int keymap_fd = allocate_shm_file(keyboard->keymap_size); - if (keymap_fd < 0) { - wlr_log(WLR_ERROR, "creating a keymap file for %zu bytes failed", - keyboard->keymap_size); - return false; + enum wl_keyboard_keymap_format format; + int fd, devnull = -1; + if (keyboard->keymap != NULL) { + format = WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1; + fd = keyboard->keymap_fd; + } else { + format = WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP; + devnull = open("/dev/null", O_RDONLY | O_CLOEXEC); + if (devnull < 0) { + wlr_log_errno(WLR_ERROR, "Failed to open /dev/null"); + return; + } + fd = devnull; } - void *ptr = mmap(NULL, keyboard->keymap_size, PROT_READ | PROT_WRITE, - MAP_SHARED, keymap_fd, 0); - if (ptr == MAP_FAILED) { - wlr_log(WLR_ERROR, "failed to mmap() %zu bytes", - keyboard->keymap_size); - close(keymap_fd); - return false; - } - - memcpy(ptr, keyboard->keymap_string, keyboard->keymap_size); - munmap(ptr, keyboard->keymap_size); - zwp_input_method_keyboard_grab_v2_send_keymap(keyboard_grab->resource, - WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, keymap_fd, - keyboard->keymap_size); + format, fd, keyboard->keymap_size); - close(keymap_fd); - return true; + if (devnull >= 0) { + close(devnull); + } } static void keyboard_grab_send_repeat_info( @@ -379,15 +376,12 @@ void wlr_input_method_keyboard_grab_v2_set_keyboard( if (keyboard) { if (keyboard_grab->keyboard == NULL || - strcmp(keyboard_grab->keyboard->keymap_string, - keyboard->keymap_string) != 0) { - // send keymap only if it is changed, or if input method is not - // aware that it did not change and blindly send it back with - // virtual keyboard, it may cause an infinite recursion. - if (!keyboard_grab_send_keymap(keyboard_grab, keyboard)) { - wlr_log(WLR_ERROR, "Failed to send keymap for input-method keyboard grab"); - return; - } + !wlr_keyboard_keymaps_match(keyboard_grab->keyboard->keymap, + keyboard->keymap)) { + // Only send keymap if it changed, otherwise if the input-method + // client sent back the same keymap with virtual-keyboard, it would + // result in an infinite loop of keymap updates. + keyboard_grab_send_keymap(keyboard_grab, keyboard); } keyboard_grab_send_repeat_info(keyboard_grab, keyboard); keyboard_grab->keyboard_keymap.notify = handle_keyboard_keymap; From 7debaced03ee466a18a9bc220fa90a49d5aa9704 Mon Sep 17 00:00:00 2001 From: YaoBing Xiao Date: Thu, 12 Sep 2024 17:26:21 +0800 Subject: [PATCH 112/519] x11/backend: Optimize query_version error handling Signed-off-by: YaoBing Xiao --- backend/x11/backend.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/backend/x11/backend.c b/backend/x11/backend.c index 9c8ea326e..31ed2aafe 100644 --- a/backend/x11/backend.c +++ b/backend/x11/backend.c @@ -512,7 +512,10 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_event_loop *loop, xcb_present_query_version(x11->xcb, 1, 2); xcb_present_query_version_reply_t *present_reply = xcb_present_query_version_reply(x11->xcb, present_cookie, NULL); - if (!present_reply || present_reply->major_version < 1) { + if (!present_reply) { + wlr_log(WLR_ERROR, "Failed to query Present version"); + goto error_display; + } else if (present_reply->major_version < 1) { wlr_log(WLR_ERROR, "X11 does not support required Present version " "(has %"PRIu32".%"PRIu32", want 1.0)", present_reply->major_version, present_reply->minor_version); @@ -533,7 +536,10 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_event_loop *loop, xcb_xfixes_query_version(x11->xcb, 4, 0); xcb_xfixes_query_version_reply_t *fixes_reply = xcb_xfixes_query_version_reply(x11->xcb, fixes_cookie, NULL); - if (!fixes_reply || fixes_reply->major_version < 4) { + if (!fixes_reply) { + wlr_log(WLR_ERROR, "Failed to query Xfixes version"); + goto error_display; + } else if (fixes_reply->major_version < 4) { wlr_log(WLR_ERROR, "X11 does not support required Xfixes version " "(has %"PRIu32".%"PRIu32", want 4.0)", fixes_reply->major_version, fixes_reply->minor_version); @@ -555,7 +561,10 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_event_loop *loop, xcb_input_xi_query_version(x11->xcb, 2, 0); xcb_input_xi_query_version_reply_t *xi_reply = xcb_input_xi_query_version_reply(x11->xcb, xi_cookie, NULL); - if (!xi_reply || xi_reply->major_version < 2) { + if (!xi_reply) { + wlr_log(WLR_ERROR, "Failed to query Xinput version"); + goto error_display; + } else if (xi_reply->major_version < 2) { wlr_log(WLR_ERROR, "X11 does not support required Xinput version " "(has %"PRIu32".%"PRIu32", want 2.0)", xi_reply->major_version, xi_reply->minor_version); From c752270be78d8e05fed23096da1643f89ffa0d53 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Mon, 9 Sep 2024 19:27:24 +0300 Subject: [PATCH 113/519] tinywl: drop focused surface check for interactive ops This serves as a bad example: compositors should validate serials from events instead. Also, the current implementation segfaults if focused_surface is NULL. --- tinywl/tinywl.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/tinywl/tinywl.c b/tinywl/tinywl.c index edc6269b3..6cf08b119 100644 --- a/tinywl/tinywl.c +++ b/tinywl/tinywl.c @@ -712,13 +712,7 @@ static void begin_interactive(struct tinywl_toplevel *toplevel, * compositor stops propegating pointer events to clients and instead * consumes them itself, to move or resize windows. */ struct tinywl_server *server = toplevel->server; - struct wlr_surface *focused_surface = - server->seat->pointer_state.focused_surface; - if (toplevel->xdg_toplevel->base->surface != - wlr_surface_get_root_surface(focused_surface)) { - /* Deny move/resize requests from unfocused clients. */ - return; - } + server->grabbed_toplevel = toplevel; server->cursor_mode = mode; From 56d69320c7b6dc6ec77b2dca89ac9905fb6e11ba Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Wed, 14 Aug 2024 20:23:45 +0300 Subject: [PATCH 114/519] pointer: release pressed buttons on destroy --- backend/libinput/pointer.c | 2 +- backend/wayland/pointer.c | 2 +- backend/x11/input_device.c | 2 +- include/wlr/interfaces/wlr_pointer.h | 3 +++ include/wlr/types/wlr_pointer.h | 5 +++++ include/wlr/types/wlr_seat.h | 2 -- types/wlr_pointer.c | 26 ++++++++++++++++++++++++++ types/wlr_virtual_pointer_v1.c | 2 +- 8 files changed, 38 insertions(+), 6 deletions(-) diff --git a/backend/libinput/pointer.c b/backend/libinput/pointer.c index 703ad7b33..9b9996780 100644 --- a/backend/libinput/pointer.c +++ b/backend/libinput/pointer.c @@ -69,7 +69,7 @@ void handle_pointer_button(struct libinput_event *event, wlr_event.state = WL_POINTER_BUTTON_STATE_RELEASED; break; } - wl_signal_emit_mutable(&pointer->events.button, &wlr_event); + wlr_pointer_notify_button(pointer, &wlr_event); wl_signal_emit_mutable(&pointer->events.frame, pointer); } diff --git a/backend/wayland/pointer.c b/backend/wayland/pointer.c index 87383fa8d..b61e32cd0 100644 --- a/backend/wayland/pointer.c +++ b/backend/wayland/pointer.c @@ -112,7 +112,7 @@ static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, .state = state, .time_msec = time, }; - wl_signal_emit_mutable(&pointer->wlr_pointer.events.button, &event); + wlr_pointer_notify_button(&pointer->wlr_pointer, &event); } static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, diff --git a/backend/x11/input_device.c b/backend/x11/input_device.c index c2ed6fd33..8246653b7 100644 --- a/backend/x11/input_device.c +++ b/backend/x11/input_device.c @@ -36,7 +36,7 @@ static void send_button_event(struct wlr_x11_output *output, uint32_t key, .button = key, .state = st, }; - wl_signal_emit_mutable(&output->pointer.events.button, &ev); + wlr_pointer_notify_button(&output->pointer, &ev); wl_signal_emit_mutable(&output->pointer.events.frame, &output->pointer); } diff --git a/include/wlr/interfaces/wlr_pointer.h b/include/wlr/interfaces/wlr_pointer.h index 74e0feafe..93adb714e 100644 --- a/include/wlr/interfaces/wlr_pointer.h +++ b/include/wlr/interfaces/wlr_pointer.h @@ -19,4 +19,7 @@ void wlr_pointer_init(struct wlr_pointer *pointer, const struct wlr_pointer_impl *impl, const char *name); void wlr_pointer_finish(struct wlr_pointer *pointer); +void wlr_pointer_notify_button(struct wlr_pointer *pointer, + struct wlr_pointer_button_event *event); + #endif diff --git a/include/wlr/types/wlr_pointer.h b/include/wlr/types/wlr_pointer.h index 2fa851383..756ffa1f6 100644 --- a/include/wlr/types/wlr_pointer.h +++ b/include/wlr/types/wlr_pointer.h @@ -14,6 +14,8 @@ #include #include +#define WLR_POINTER_BUTTONS_CAP 16 + struct wlr_pointer_impl; struct wlr_pointer { @@ -23,6 +25,9 @@ struct wlr_pointer { char *output_name; + uint32_t buttons[WLR_POINTER_BUTTONS_CAP]; + size_t button_count; + struct { struct wl_signal motion; // struct wlr_pointer_motion_event struct wl_signal motion_absolute; // struct wlr_pointer_motion_absolute_event diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h index c04cd5e06..dcb35c62c 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -170,8 +170,6 @@ struct wlr_seat_pointer_grab { void *data; }; -#define WLR_POINTER_BUTTONS_CAP 16 - struct wlr_seat_pointer_button { uint32_t button; size_t n_pressed; diff --git a/types/wlr_pointer.c b/types/wlr_pointer.c index 150b90133..e4ee10c8e 100644 --- a/types/wlr_pointer.c +++ b/types/wlr_pointer.c @@ -6,6 +6,8 @@ #include #include "interfaces/wlr_input_device.h" +#include "util/set.h" +#include "util/time.h" struct wlr_pointer *wlr_pointer_from_input_device( struct wlr_input_device *input_device) { @@ -36,7 +38,31 @@ void wlr_pointer_init(struct wlr_pointer *pointer, } void wlr_pointer_finish(struct wlr_pointer *pointer) { + int64_t time_msec = get_current_time_msec(); + while (pointer->button_count > 0) { + struct wlr_pointer_button_event event = { + .pointer = pointer, + .time_msec = time_msec, + .button = pointer->buttons[pointer->button_count - 1], + .state = WL_POINTER_BUTTON_STATE_RELEASED, + }; + wlr_pointer_notify_button(pointer, &event); + } + wlr_input_device_finish(&pointer->base); free(pointer->output_name); } + +void wlr_pointer_notify_button(struct wlr_pointer *pointer, + struct wlr_pointer_button_event *event) { + if (event->state == WL_POINTER_BUTTON_STATE_PRESSED) { + set_add(pointer->buttons, &pointer->button_count, + WLR_POINTER_BUTTONS_CAP, event->button); + } else { + set_remove(pointer->buttons, &pointer->button_count, + WLR_POINTER_BUTTONS_CAP, event->button); + } + + wl_signal_emit_mutable(&pointer->events.button, event); +} diff --git a/types/wlr_virtual_pointer_v1.c b/types/wlr_virtual_pointer_v1.c index 2ae39a0a8..812e7d565 100644 --- a/types/wlr_virtual_pointer_v1.c +++ b/types/wlr_virtual_pointer_v1.c @@ -73,7 +73,7 @@ static void virtual_pointer_button(struct wl_client *client, .button = button, .state = state ? WL_POINTER_BUTTON_STATE_PRESSED : WL_POINTER_BUTTON_STATE_RELEASED, }; - wl_signal_emit_mutable(&pointer->pointer.events.button, &event); + wlr_pointer_notify_button(&pointer->pointer, &event); } static void virtual_pointer_axis(struct wl_client *client, From a8d1e5273abad02e594c4ad2f237a204ca239528 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Tue, 24 Sep 2024 21:55:10 +0300 Subject: [PATCH 115/519] linux-dmabuf-v1: use static_assert --- types/wlr_linux_dmabuf_v1.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/types/wlr_linux_dmabuf_v1.c b/types/wlr_linux_dmabuf_v1.c index bfd97637a..73a1d8f0f 100644 --- a/types/wlr_linux_dmabuf_v1.c +++ b/types/wlr_linux_dmabuf_v1.c @@ -51,9 +51,7 @@ struct wlr_linux_dmabuf_feedback_v1_table_entry { uint64_t modifier; }; -// TODO: switch back to static_assert once this fix propagates in stable trees: -// https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=255290 -_Static_assert(sizeof(struct wlr_linux_dmabuf_feedback_v1_table_entry) == 16, +static_assert(sizeof(struct wlr_linux_dmabuf_feedback_v1_table_entry) == 16, "Expected wlr_linux_dmabuf_feedback_v1_table_entry to be tightly packed"); struct wlr_linux_dmabuf_v1_surface { From d55c175777a771b326d5eac2343b7686c9117a4e Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 29 Sep 2024 16:35:07 +0200 Subject: [PATCH 116/519] render/vulkan: log size when importing too-large DMA-BUF --- render/vulkan/texture.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/render/vulkan/texture.c b/render/vulkan/texture.c index c40533133..2a4f60c57 100644 --- a/render/vulkan/texture.c +++ b/render/vulkan/texture.c @@ -534,7 +534,8 @@ VkImage vulkan_import_dmabuf(struct wlr_vk_renderer *renderer, if ((uint32_t) attribs->width > mod->max_extent.width || (uint32_t) attribs->height > mod->max_extent.height) { - wlr_log(WLR_ERROR, "DMA-BUF is too large to import"); + wlr_log(WLR_ERROR, "DMA-BUF is too large to import (%"PRIi32"x%"PRIi32" > %"PRIu32"x%"PRIu32")", + attribs->width, attribs->height, mod->max_extent.width, mod->max_extent.height); return VK_NULL_HANDLE; } From 3da6fac1f21aa12eeeb55b44b3e1c16b637201f5 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 29 Sep 2024 16:35:33 +0200 Subject: [PATCH 117/519] render/vulkan: check size when creating shm texture --- render/vulkan/texture.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/render/vulkan/texture.c b/render/vulkan/texture.c index 2a4f60c57..e3bd69793 100644 --- a/render/vulkan/texture.c +++ b/render/vulkan/texture.c @@ -390,6 +390,12 @@ static struct wlr_texture *vulkan_texture_from_pixels( return NULL; } + if (width > fmt->shm.max_extent.width || height > fmt->shm.max_extent.height) { + wlr_log(WLR_ERROR, "Texture is too large to upload (%"PRIu32"x%"PRIu32" > %"PRIu32"x%"PRIu32")", + width, height, fmt->shm.max_extent.width, fmt->shm.max_extent.height); + return NULL; + } + struct wlr_vk_texture *texture = vulkan_texture_create(renderer, width, height); if (texture == NULL) { return NULL; From ab118042ea393f29ca8907d269370dd8952d5a35 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Tue, 1 Oct 2024 15:42:06 +0300 Subject: [PATCH 118/519] ext-foreign-toplevel-list: add _from_resource() --- include/wlr/types/wlr_ext_foreign_toplevel_list_v1.h | 3 +++ types/wlr_ext_foreign_toplevel_list_v1.c | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/include/wlr/types/wlr_ext_foreign_toplevel_list_v1.h b/include/wlr/types/wlr_ext_foreign_toplevel_list_v1.h index a5ba9d384..b2e4c7b03 100644 --- a/include/wlr/types/wlr_ext_foreign_toplevel_list_v1.h +++ b/include/wlr/types/wlr_ext_foreign_toplevel_list_v1.h @@ -64,4 +64,7 @@ void wlr_ext_foreign_toplevel_handle_v1_update_state( struct wlr_ext_foreign_toplevel_handle_v1 *toplevel, const struct wlr_ext_foreign_toplevel_handle_v1_state *state); +struct wlr_ext_foreign_toplevel_handle_v1 *wlr_ext_foreign_toplevel_handle_v1_from_resource( + struct wl_resource *resource); + #endif diff --git a/types/wlr_ext_foreign_toplevel_list_v1.c b/types/wlr_ext_foreign_toplevel_list_v1.c index 22a126f84..a773ee8a2 100644 --- a/types/wlr_ext_foreign_toplevel_list_v1.c +++ b/types/wlr_ext_foreign_toplevel_list_v1.c @@ -54,6 +54,13 @@ static bool update_string(struct wlr_ext_foreign_toplevel_handle_v1 *toplevel, return true; } +struct wlr_ext_foreign_toplevel_handle_v1 *wlr_ext_foreign_toplevel_handle_v1_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &ext_foreign_toplevel_handle_v1_interface, + &toplevel_handle_impl)); + return wl_resource_get_user_data(resource); +} + void wlr_ext_foreign_toplevel_handle_v1_update_state( struct wlr_ext_foreign_toplevel_handle_v1 *toplevel, const struct wlr_ext_foreign_toplevel_handle_v1_state *state) { From 7ce868bcf6593941669bedbd1be8ea7cc9205da2 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 3 Oct 2024 19:15:58 +0200 Subject: [PATCH 119/519] render/vulkan: make VK_KHR_external_semaphore_fd optional We already block instead of using sync_file when the driver doesn't support import/export. --- render/vulkan/vulkan.c | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/render/vulkan/vulkan.c b/render/vulkan/vulkan.c index 7cdc44a02..905feb906 100644 --- a/render/vulkan/vulkan.c +++ b/render/vulkan/vulkan.c @@ -462,7 +462,6 @@ struct wlr_vk_device *vulkan_device_create(struct wlr_vk_instance *ini, const char *extensions[32] = {0}; size_t extensions_len = 0; extensions[extensions_len++] = VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME; - extensions[extensions_len++] = VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME; extensions[extensions_len++] = VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME; // or vulkan 1.2 extensions[extensions_len++] = VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME; extensions[extensions_len++] = VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME; @@ -497,19 +496,25 @@ struct wlr_vk_device *vulkan_device_create(struct wlr_vk_instance *ini, assert(graphics_found); } - const VkPhysicalDeviceExternalSemaphoreInfo ext_semaphore_info = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, - .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, - }; - VkExternalSemaphoreProperties ext_semaphore_props = { - .sType = VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES, - }; - vkGetPhysicalDeviceExternalSemaphoreProperties(phdev, - &ext_semaphore_info, &ext_semaphore_props); - bool exportable_semaphore = ext_semaphore_props.externalSemaphoreFeatures & - VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT; - bool importable_semaphore = ext_semaphore_props.externalSemaphoreFeatures & - VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT; + bool exportable_semaphore = false, importable_semaphore = false; + bool has_external_semaphore_fd = + check_extension(avail_ext_props, avail_extc, VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME); + if (has_external_semaphore_fd) { + const VkPhysicalDeviceExternalSemaphoreInfo ext_semaphore_info = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, + .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + }; + VkExternalSemaphoreProperties ext_semaphore_props = { + .sType = VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES, + }; + vkGetPhysicalDeviceExternalSemaphoreProperties(phdev, + &ext_semaphore_info, &ext_semaphore_props); + exportable_semaphore = ext_semaphore_props.externalSemaphoreFeatures & + VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT; + importable_semaphore = ext_semaphore_props.externalSemaphoreFeatures & + VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT; + extensions[extensions_len++] = VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME; + } if (!exportable_semaphore) { wlr_log(WLR_DEBUG, "VkSemaphore is not exportable to a sync_file"); } @@ -617,10 +622,13 @@ struct wlr_vk_device *vulkan_device_create(struct wlr_vk_instance *ini, load_device_proc(dev, "vkWaitSemaphoresKHR", &dev->api.vkWaitSemaphoresKHR); load_device_proc(dev, "vkGetSemaphoreCounterValueKHR", &dev->api.vkGetSemaphoreCounterValueKHR); - load_device_proc(dev, "vkGetSemaphoreFdKHR", &dev->api.vkGetSemaphoreFdKHR); - load_device_proc(dev, "vkImportSemaphoreFdKHR", &dev->api.vkImportSemaphoreFdKHR); load_device_proc(dev, "vkQueueSubmit2KHR", &dev->api.vkQueueSubmit2KHR); + if (has_external_semaphore_fd) { + load_device_proc(dev, "vkGetSemaphoreFdKHR", &dev->api.vkGetSemaphoreFdKHR); + load_device_proc(dev, "vkImportSemaphoreFdKHR", &dev->api.vkImportSemaphoreFdKHR); + } + size_t max_fmts; const struct wlr_vk_format *fmts = vulkan_get_format_list(&max_fmts); dev->format_props = calloc(max_fmts, sizeof(*dev->format_props)); From 95d25d833fda4224037a7b9fd42f74ad82c3783c Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 8 Aug 2024 23:36:04 +0200 Subject: [PATCH 120/519] keyboard: add utilities for pointer keys References: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3803 --- include/wlr/types/wlr_keyboard.h | 17 ++++++++++++ types/wlr_keyboard.c | 44 ++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/include/wlr/types/wlr_keyboard.h b/include/wlr/types/wlr_keyboard.h index 904649367..23ca006a1 100644 --- a/include/wlr/types/wlr_keyboard.h +++ b/include/wlr/types/wlr_keyboard.h @@ -115,6 +115,23 @@ bool wlr_keyboard_set_keymap(struct wlr_keyboard *kb, bool wlr_keyboard_keymaps_match(struct xkb_keymap *km1, struct xkb_keymap *km2); +/** + * Interpret pointer button key symbols. + * + * Returns a button code (BTN_*) if the key symbol is a pointer button + * (XKB_KEY_Pointer_Button*), 0 otherwise. + */ +uint32_t wlr_keyboard_keysym_to_pointer_button(xkb_keysym_t keysym); + +/** + * Interpret pointer motion key symbols. + * + * Sets dx and dy to horizontal and vertical motion deltas (0, 1 or -1) if the + * key symbol is a pointer motion (XKB_KEY_Pointer_*). Otherwise, sets both dx + * and dy to 0. + */ +void wlr_keyboard_keysym_to_pointer_motion(xkb_keysym_t keysym, int *dx, int *dy); + /** * Set the keyboard repeat info. * diff --git a/types/wlr_keyboard.c b/types/wlr_keyboard.c index 366f4218a..81d45a624 100644 --- a/types/wlr_keyboard.c +++ b/types/wlr_keyboard.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -304,3 +305,46 @@ bool wlr_keyboard_keymaps_match(struct xkb_keymap *km1, free(km2_str); return result; } + +uint32_t wlr_keyboard_keysym_to_pointer_button(xkb_keysym_t keysym) { + switch (keysym) { + case XKB_KEY_Pointer_Button1: + return BTN_LEFT; + case XKB_KEY_Pointer_Button2: + return BTN_MIDDLE; + case XKB_KEY_Pointer_Button3: + return BTN_RIGHT; + default: + return 0; + } +} + +void wlr_keyboard_keysym_to_pointer_motion(xkb_keysym_t keysym, int *dx, int *dy) { + *dx = 0; + switch (keysym) { + case XKB_KEY_Pointer_Right: + case XKB_KEY_Pointer_DownRight: + case XKB_KEY_Pointer_UpRight: + *dx = 1; + break; + case XKB_KEY_Pointer_Left: + case XKB_KEY_Pointer_DownLeft: + case XKB_KEY_Pointer_UpLeft: + *dx = -1; + break; + } + + *dy = 0; + switch (keysym) { + case XKB_KEY_Pointer_Down: + case XKB_KEY_Pointer_DownLeft: + case XKB_KEY_Pointer_DownRight: + *dy = 1; + break; + case XKB_KEY_Pointer_Up: + case XKB_KEY_Pointer_UpLeft: + case XKB_KEY_Pointer_UpRight: + *dy = -1; + break; + } +} From c9fe96102d6e179baa22274376a273a27c37bdd2 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 21 Aug 2023 12:03:26 +0200 Subject: [PATCH 121/519] xwayland/xwm: extract read loop to separate function --- xwayland/xwm.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/xwayland/xwm.c b/xwayland/xwm.c index f25e4734b..5b6a07b31 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -1736,16 +1736,10 @@ static void xwm_handle_unhandled_event(struct wlr_xwm *xwm, xcb_generic_event_t #endif } -static int x11_event_handler(int fd, uint32_t mask, void *data) { +static int read_x11_events(struct wlr_xwm *xwm) { int count = 0; + xcb_generic_event_t *event; - struct wlr_xwm *xwm = data; - - if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) { - xwm_destroy(xwm); - return 0; - } - while ((event = xcb_poll_for_event(xwm->xcb_conn))) { count++; @@ -1804,6 +1798,18 @@ static int x11_event_handler(int fd, uint32_t mask, void *data) { free(event); } + return count; +} + +static int x11_event_handler(int fd, uint32_t mask, void *data) { + struct wlr_xwm *xwm = data; + + if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) { + xwm_destroy(xwm); + return 0; + } + + int count = read_x11_events(xwm); if (count) { xcb_flush(xwm->xcb_conn); } From 6ada67da9bb0713df4750f8a177294fa2ff4ba71 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 21 Aug 2023 12:16:24 +0200 Subject: [PATCH 122/519] xwayland/xwm: implement somewhat asynchronous request flushing Instead of calling xcb_flush() directly, wait until the FD is writable. Ideally we'd have a non-blocking variant instead of xcb_flush(), but libxcb doesn't have this. Also libxcb blocks when its internal buffer is full, but not much we can do here. --- include/xwayland/xwm.h | 2 ++ xwayland/selection/dnd.c | 2 +- xwayland/selection/incoming.c | 8 +++---- xwayland/selection/outgoing.c | 4 ++-- xwayland/selection/selection.c | 6 ++--- xwayland/xwm.c | 40 +++++++++++++++++++++++----------- 6 files changed, 39 insertions(+), 23 deletions(-) diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h index 364095399..8b4bbdb54 100644 --- a/include/xwayland/xwm.h +++ b/include/xwayland/xwm.h @@ -172,4 +172,6 @@ xcb_void_cookie_t xwm_send_event_with_size(xcb_connection_t *c, uint8_t propagate, xcb_window_t destination, uint32_t event_mask, const void *event, uint32_t length); +void xwm_schedule_flush(struct wlr_xwm *xwm); + #endif diff --git a/xwayland/selection/dnd.c b/xwayland/selection/dnd.c index e9cc63d9b..166e4e637 100644 --- a/xwayland/selection/dnd.c +++ b/xwayland/selection/dnd.c @@ -55,7 +55,7 @@ static void xwm_dnd_send_event(struct wlr_xwm *xwm, xcb_atom_t type, XCB_EVENT_MASK_NO_EVENT, &event, sizeof(event)); - xcb_flush(xwm->xcb_conn); + xwm_schedule_flush(xwm); } static void xwm_dnd_send_enter(struct wlr_xwm *xwm) { diff --git a/xwayland/selection/incoming.c b/xwayland/selection/incoming.c index 4a0b450c1..72f82c279 100644 --- a/xwayland/selection/incoming.c +++ b/xwayland/selection/incoming.c @@ -37,7 +37,7 @@ xwm_selection_transfer_create_incoming(struct wlr_xwm_selection *selection) { XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE } ); - xcb_flush(xwm->xcb_conn); + xwm_schedule_flush(xwm); return transfer; } @@ -89,7 +89,7 @@ static void xwm_notify_ready_for_next_incr_chunk( wlr_log(WLR_DEBUG, "deleting property"); xcb_delete_property(xwm->xcb_conn, transfer->incoming_window, xwm->atoms[WL_SELECTION]); - xcb_flush(xwm->xcb_conn); + xwm_schedule_flush(xwm); xwm_selection_transfer_remove_event_source(transfer); xwm_selection_transfer_destroy_property_reply(transfer); @@ -234,7 +234,7 @@ static void source_send(struct wlr_xwm_selection *selection, xwm->atoms[WL_SELECTION], XCB_TIME_CURRENT_TIME); - xcb_flush(xwm->xcb_conn); + xwm_schedule_flush(xwm); fcntl(fd, F_SETFL, O_WRONLY | O_NONBLOCK); transfer->wl_client_fd = fd; @@ -533,7 +533,7 @@ int xwm_handle_xfixes_selection_notify(struct wlr_xwm *xwm, xwm->atoms[WL_SELECTION], event->timestamp ); - xcb_flush(xwm->xcb_conn); + xwm_schedule_flush(xwm); return 1; } diff --git a/xwayland/selection/outgoing.c b/xwayland/selection/outgoing.c index 3f2e2c711..4763da004 100644 --- a/xwayland/selection/outgoing.c +++ b/xwayland/selection/outgoing.c @@ -33,7 +33,7 @@ static void xwm_selection_send_notify(struct wlr_xwm *xwm, XCB_EVENT_MASK_NO_EVENT, &selection_notify, sizeof(selection_notify)); - xcb_flush(xwm->xcb_conn); + xwm_schedule_flush(xwm); } static int xwm_selection_flush_source_data( @@ -46,7 +46,7 @@ static int xwm_selection_flush_source_data( 8, // format transfer->source_data.size, transfer->source_data.data); - xcb_flush(transfer->selection->xwm->xcb_conn); + xwm_schedule_flush(transfer->selection->xwm); transfer->property_set = true; size_t length = transfer->source_data.size; transfer->source_data.size = 0; diff --git a/xwayland/selection/selection.c b/xwayland/selection/selection.c index 4d1e366eb..deb695e24 100644 --- a/xwayland/selection/selection.c +++ b/xwayland/selection/selection.c @@ -52,7 +52,7 @@ void xwm_selection_transfer_destroy( if (transfer->incoming_window) { struct wlr_xwm *xwm = transfer->selection->xwm; xcb_destroy_window(xwm->xcb_conn, transfer->incoming_window); - xcb_flush(xwm->xcb_conn); + xwm_schedule_flush(xwm); } wl_list_remove(&transfer->link); @@ -269,14 +269,14 @@ static void xwm_selection_set_owner(struct wlr_xwm_selection *selection, selection->window, selection->atom, XCB_TIME_CURRENT_TIME); - xcb_flush(selection->xwm->xcb_conn); + xwm_schedule_flush(selection->xwm); } else { if (selection->owner == selection->window) { xcb_set_selection_owner(selection->xwm->xcb_conn, XCB_WINDOW_NONE, selection->atom, selection->timestamp); - xcb_flush(selection->xwm->xcb_conn); + xwm_schedule_flush(selection->xwm); } } } diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 5b6a07b31..b3e11ef41 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -302,7 +302,7 @@ static void xwm_send_wm_message(struct wlr_xwayland_surface *surface, event_mask, &event, sizeof(event)); - xcb_flush(xwm->xcb_conn); + xwm_schedule_flush(xwm); } static void xwm_set_net_client_list(struct wlr_xwm *xwm) { @@ -460,7 +460,7 @@ static void xwm_surface_activate(struct wlr_xwm *xwm, } xwm_set_focused_window(xwm, xsurface); - xcb_flush(xwm->xcb_conn); + xwm_schedule_flush(xwm); } static void xsurface_set_net_wm_state(struct wlr_xwayland_surface *xsurface) { @@ -1187,7 +1187,7 @@ void wlr_xwayland_surface_restack(struct wlr_xwayland_surface *xsurface, wl_list_insert(node, &xsurface->stack_link); xwm_set_net_client_list_stacking(xwm); - xcb_flush(xwm->xcb_conn); + xwm_schedule_flush(xwm); } static void xwm_handle_map_request(struct wlr_xwm *xwm, @@ -1809,9 +1809,19 @@ static int x11_event_handler(int fd, uint32_t mask, void *data) { return 0; } - int count = read_x11_events(xwm); - if (count) { + int count = 0; + if (mask & WL_EVENT_READABLE) { + count = read_x11_events(xwm); + if (count) { + xwm_schedule_flush(xwm); + } + } + + if (mask & WL_EVENT_WRITABLE) { + // xcb_flush() always blocks until it's written all pending requests, + // but it's the only thing we have xcb_flush(xwm->xcb_conn); + wl_event_source_fd_update(xwm->event_source, WL_EVENT_READABLE); } return count; @@ -1835,7 +1845,7 @@ static void handle_compositor_new_surface(struct wl_listener *listener, wl_list_for_each(xsurface, &xwm->unpaired_surfaces, unpaired_link) { if (xsurface->surface_id == surface_id) { xwayland_surface_associate(xwm, xsurface, surface); - xcb_flush(xwm->xcb_conn); + xwm_schedule_flush(xwm); return; } } @@ -1924,7 +1934,7 @@ void wlr_xwayland_surface_configure(struct wlr_xwayland_surface *xsurface, sizeof(configure_notify)); } - xcb_flush(xwm->xcb_conn); + xwm_schedule_flush(xwm); } void wlr_xwayland_surface_close(struct wlr_xwayland_surface *xsurface) { @@ -1945,7 +1955,7 @@ void wlr_xwayland_surface_close(struct wlr_xwayland_surface *xsurface) { xwm_send_wm_message(xsurface, &message_data, XCB_EVENT_MASK_NO_EVENT); } else { xcb_kill_client(xwm->xcb_conn, xsurface->window_id); - xcb_flush(xwm->xcb_conn); + xwm_schedule_flush(xwm); } } @@ -2238,7 +2248,7 @@ void xwm_set_cursor(struct wlr_xwm *xwm, const uint8_t *pixels, uint32_t stride, uint32_t values[] = {xwm->cursor}; xcb_change_window_attributes(xwm->xcb_conn, xwm->screen->root, XCB_CW_CURSOR, values); - xcb_flush(xwm->xcb_conn); + xwm_schedule_flush(xwm); } struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) { @@ -2364,7 +2374,7 @@ void wlr_xwayland_surface_set_withdrawn(struct wlr_xwayland_surface *surface, surface->withdrawn = withdrawn; xsurface_set_wm_state(surface); xsurface_set_net_wm_state(surface); - xcb_flush(surface->xwm->xcb_conn); + xwm_schedule_flush(surface->xwm); } void wlr_xwayland_surface_set_minimized(struct wlr_xwayland_surface *surface, @@ -2372,7 +2382,7 @@ void wlr_xwayland_surface_set_minimized(struct wlr_xwayland_surface *surface, surface->minimized = minimized; xsurface_set_wm_state(surface); xsurface_set_net_wm_state(surface); - xcb_flush(surface->xwm->xcb_conn); + xwm_schedule_flush(surface->xwm); } void wlr_xwayland_surface_set_maximized(struct wlr_xwayland_surface *surface, @@ -2380,14 +2390,14 @@ void wlr_xwayland_surface_set_maximized(struct wlr_xwayland_surface *surface, surface->maximized_horz = maximized_horz; surface->maximized_vert = maximized_vert; xsurface_set_net_wm_state(surface); - xcb_flush(surface->xwm->xcb_conn); + xwm_schedule_flush(surface->xwm); } void wlr_xwayland_surface_set_fullscreen(struct wlr_xwayland_surface *surface, bool fullscreen) { surface->fullscreen = fullscreen; xsurface_set_net_wm_state(surface); - xcb_flush(surface->xwm->xcb_conn); + xwm_schedule_flush(surface->xwm); } bool xwm_atoms_contains(struct wlr_xwm *xwm, xcb_atom_t *atoms, @@ -2513,3 +2523,7 @@ xcb_connection_t *wlr_xwayland_get_xwm_connection( struct wlr_xwayland *wlr_xwayland) { return wlr_xwayland->xwm ? wlr_xwayland->xwm->xcb_conn : NULL; } + +void xwm_schedule_flush(struct wlr_xwm *xwm) { + wl_event_source_fd_update(xwm->event_source, WL_EVENT_READABLE | WL_EVENT_WRITABLE); +} From dd8f4913a438a25201fce05efad76da62491e336 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Tue, 8 Oct 2024 19:18:38 +0300 Subject: [PATCH 123/519] subcompositor: drop unused subsurface state --- include/wlr/types/wlr_subcompositor.h | 4 ---- types/wlr_subcompositor.c | 3 --- 2 files changed, 7 deletions(-) diff --git a/include/wlr/types/wlr_subcompositor.h b/include/wlr/types/wlr_subcompositor.h index bd3899b8a..61a2cccc9 100644 --- a/include/wlr/types/wlr_subcompositor.h +++ b/include/wlr/types/wlr_subcompositor.h @@ -53,10 +53,6 @@ struct wlr_subsurface { // private state struct wlr_surface_synced parent_synced; - - struct { - int32_t x, y; - } previous; }; struct wlr_subcompositor { diff --git a/types/wlr_subcompositor.c b/types/wlr_subcompositor.c index 771b98739..f0d0827b6 100644 --- a/types/wlr_subcompositor.c +++ b/types/wlr_subcompositor.c @@ -289,9 +289,6 @@ void subsurface_handle_parent_commit(struct wlr_subsurface *subsurface) { subsurface); subsurface_consider_map(subsurface); } - - subsurface->previous.x = subsurface->current.x; - subsurface->previous.y = subsurface->current.y; } struct wlr_subsurface *wlr_subsurface_try_from_wlr_surface(struct wlr_surface *surface) { From b8418b7b91483d9bb3da76eefdb8ac3042804191 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Mon, 26 Aug 2024 18:39:14 -0400 Subject: [PATCH 124/519] scene: Use wlr_output_transformed_resolution --- types/scene/wlr_scene.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 713bfdfd0..d1b44e14b 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -336,19 +336,15 @@ static void transform_output_box(struct wlr_box *box, const struct render_data * static void scene_output_damage(struct wlr_scene_output *scene_output, const pixman_region32_t *region) { if (wlr_damage_ring_add(&scene_output->damage_ring, region)) { + int width, height; + wlr_output_transformed_resolution(scene_output->output, &width, &height); + wlr_output_schedule_frame(scene_output->output); struct wlr_output *output = scene_output->output; enum wl_output_transform transform = wlr_output_transform_invert(scene_output->output->transform); - int width = output->width; - int height = output->height; - if (transform & WL_OUTPUT_TRANSFORM_90) { - width = output->height; - height = output->width; - } - pixman_region32_t frame_damage; pixman_region32_init(&frame_damage); wlr_region_transform(&frame_damage, region, transform, width, height); From 9904f160afbff25be422bb7232b67aee7a469efa Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Mon, 26 Aug 2024 18:37:48 -0400 Subject: [PATCH 125/519] scene: Don't rely on return value of wlr_damage_ring_add --- types/scene/wlr_scene.c | 48 +++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index d1b44e14b..80122ba36 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -335,33 +335,43 @@ static void transform_output_box(struct wlr_box *box, const struct render_data * static void scene_output_damage(struct wlr_scene_output *scene_output, const pixman_region32_t *region) { - if (wlr_damage_ring_add(&scene_output->damage_ring, region)) { - int width, height; - wlr_output_transformed_resolution(scene_output->output, &width, &height); + int width, height; + wlr_output_transformed_resolution(scene_output->output, &width, &height); - wlr_output_schedule_frame(scene_output->output); + pixman_region32_t clipped; + pixman_region32_init(&clipped); + pixman_region32_intersect_rect(&clipped, region, 0, 0, width, height); - struct wlr_output *output = scene_output->output; - enum wl_output_transform transform = - wlr_output_transform_invert(scene_output->output->transform); - - pixman_region32_t frame_damage; - pixman_region32_init(&frame_damage); - wlr_region_transform(&frame_damage, region, transform, width, height); - - pixman_region32_union(&scene_output->pending_commit_damage, - &scene_output->pending_commit_damage, &frame_damage); - pixman_region32_intersect_rect(&scene_output->pending_commit_damage, - &scene_output->pending_commit_damage, 0, 0, output->width, output->height); - pixman_region32_fini(&frame_damage); + if (!pixman_region32_not_empty(&clipped)) { + pixman_region32_fini(&clipped); + return; } + + wlr_damage_ring_add(&scene_output->damage_ring, &clipped); + pixman_region32_fini(&clipped); + wlr_output_schedule_frame(scene_output->output); + + struct wlr_output *output = scene_output->output; + enum wl_output_transform transform = + wlr_output_transform_invert(scene_output->output->transform); + + pixman_region32_t frame_damage; + pixman_region32_init(&frame_damage); + wlr_region_transform(&frame_damage, region, transform, width, height); + + pixman_region32_union(&scene_output->pending_commit_damage, + &scene_output->pending_commit_damage, &frame_damage); + pixman_region32_intersect_rect(&scene_output->pending_commit_damage, + &scene_output->pending_commit_damage, 0, 0, output->width, output->height); + pixman_region32_fini(&frame_damage); } static void scene_output_damage_whole(struct wlr_scene_output *scene_output) { - struct wlr_damage_ring *ring = &scene_output->damage_ring; + int width, height; + wlr_output_transformed_resolution(scene_output->output, &width, &height); pixman_region32_t damage; - pixman_region32_init_rect(&damage, 0, 0, ring->width, ring->height); + pixman_region32_init_rect(&damage, 0, 0, width, height); scene_output_damage(scene_output, &damage); pixman_region32_fini(&damage); } From fbafd8ed949ba76b6a476e848ccc26e75272751c Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Mon, 7 Oct 2024 14:48:15 -0400 Subject: [PATCH 126/519] wlr_damage_ring: Clamp damage region to buffer size --- types/wlr_damage_ring.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/types/wlr_damage_ring.c b/types/wlr_damage_ring.c index 840c872d4..e0da90c68 100644 --- a/types/wlr_damage_ring.c +++ b/types/wlr_damage_ring.c @@ -116,6 +116,8 @@ void wlr_damage_ring_rotate_buffer(struct wlr_damage_ring *ring, continue; } + pixman_region32_intersect_rect(damage, damage, 0, 0, buffer->width, buffer->height); + // Check the number of rectangles int n_rects = pixman_region32_n_rects(damage); if (n_rects > WLR_DAMAGE_RING_MAX_RECTS) { From 502eb38d80eb5a273f5481ec8559cb35d726f5da Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Mon, 7 Oct 2024 14:45:17 -0400 Subject: [PATCH 127/519] damage_ring: Remove return value of wlr_damage_ring_add/wlr_damage_ring_add_box Compositors should compute whether the damage is part of the output themselves. --- include/wlr/types/wlr_damage_ring.h | 8 ++------ types/wlr_damage_ring.c | 31 ++++++----------------------- 2 files changed, 8 insertions(+), 31 deletions(-) diff --git a/include/wlr/types/wlr_damage_ring.h b/include/wlr/types/wlr_damage_ring.h index 3f469af60..68a935c02 100644 --- a/include/wlr/types/wlr_damage_ring.h +++ b/include/wlr/types/wlr_damage_ring.h @@ -54,18 +54,14 @@ void wlr_damage_ring_set_bounds(struct wlr_damage_ring *ring, /** * Add a region to the current damage. - * - * Returns true if the region intersects the ring bounds, false otherwise. */ -bool wlr_damage_ring_add(struct wlr_damage_ring *ring, +void wlr_damage_ring_add(struct wlr_damage_ring *ring, const pixman_region32_t *damage); /** * Add a box to the current damage. - * - * Returns true if the box intersects the ring bounds, false otherwise. */ -bool wlr_damage_ring_add_box(struct wlr_damage_ring *ring, +void wlr_damage_ring_add_box(struct wlr_damage_ring *ring, const struct wlr_box *box); /** diff --git a/types/wlr_damage_ring.c b/types/wlr_damage_ring.c index e0da90c68..108bb9c4e 100644 --- a/types/wlr_damage_ring.c +++ b/types/wlr_damage_ring.c @@ -49,35 +49,16 @@ void wlr_damage_ring_set_bounds(struct wlr_damage_ring *ring, wlr_damage_ring_add_whole(ring); } -bool wlr_damage_ring_add(struct wlr_damage_ring *ring, +void wlr_damage_ring_add(struct wlr_damage_ring *ring, const pixman_region32_t *damage) { - pixman_region32_t clipped; - pixman_region32_init(&clipped); - pixman_region32_intersect_rect(&clipped, damage, - 0, 0, ring->width, ring->height); - bool intersects = pixman_region32_not_empty(&clipped); - if (intersects) { - pixman_region32_union(&ring->current, &ring->current, &clipped); - } - pixman_region32_fini(&clipped); - return intersects; + pixman_region32_union(&ring->current, &ring->current, damage); } -bool wlr_damage_ring_add_box(struct wlr_damage_ring *ring, +void wlr_damage_ring_add_box(struct wlr_damage_ring *ring, const struct wlr_box *box) { - struct wlr_box clipped = { - .x = 0, - .y = 0, - .width = ring->width, - .height = ring->height, - }; - if (wlr_box_intersection(&clipped, &clipped, box)) { - pixman_region32_union_rect(&ring->current, - &ring->current, clipped.x, clipped.y, - clipped.width, clipped.height); - return true; - } - return false; + pixman_region32_union_rect(&ring->current, + &ring->current, box->x, box->y, + box->width, box->height); } void wlr_damage_ring_add_whole(struct wlr_damage_ring *ring) { From 6202580b7b9674415d02f16d9675233ca6a7a29c Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Mon, 26 Aug 2024 18:42:27 -0400 Subject: [PATCH 128/519] damage_ring: Stop using ring->{width, height} --- types/wlr_damage_ring.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/types/wlr_damage_ring.c b/types/wlr_damage_ring.c index 108bb9c4e..fc80631e0 100644 --- a/types/wlr_damage_ring.c +++ b/types/wlr_damage_ring.c @@ -62,8 +62,17 @@ void wlr_damage_ring_add_box(struct wlr_damage_ring *ring, } void wlr_damage_ring_add_whole(struct wlr_damage_ring *ring) { + int width = 0; + int height = 0; + + struct wlr_damage_ring_buffer *entry; + wl_list_for_each(entry, &ring->buffers, link) { + width = width < entry->buffer->width ? entry->buffer->width : width; + height = height < entry->buffer->height ? entry->buffer->height : height; + } + pixman_region32_union_rect(&ring->current, - &ring->current, 0, 0, ring->width, ring->height); + &ring->current, 0, 0, width, height); } static void entry_squash_damage(struct wlr_damage_ring_buffer *entry) { @@ -121,7 +130,7 @@ void wlr_damage_ring_rotate_buffer(struct wlr_damage_ring *ring, pixman_region32_clear(damage); pixman_region32_union_rect(damage, damage, - 0, 0, ring->width, ring->height); + 0, 0, buffer->width, buffer->height); entry = calloc(1, sizeof(*entry)); if (!entry) { From 79e063035c7eda9f98129f59717c304249e43583 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Mon, 26 Aug 2024 18:50:14 -0400 Subject: [PATCH 129/519] damage_ring: Remove wlr_damage_ring_set_bounds This wasn't that great: 1. Now that damage ring tracks damage across actual wlr_buffer objects, it can use the buffer size to do any sort of cropping that needs to happen. 2. The damage ring size really should be the size of the transformed size of the output. Compositors currently have to call `wlr_damage_ring_set_bounds()` where it might not be clear when to call the function. Compositors can just check against the actual output bounds that they care about when processing the damage. Fixes: #3891 --- include/wlr/types/wlr_damage_ring.h | 13 ------------- types/scene/wlr_scene.c | 7 ------- types/wlr_damage_ring.c | 22 +--------------------- 3 files changed, 1 insertion(+), 41 deletions(-) diff --git a/include/wlr/types/wlr_damage_ring.h b/include/wlr/types/wlr_damage_ring.h index 68a935c02..3baeb9d5b 100644 --- a/include/wlr/types/wlr_damage_ring.h +++ b/include/wlr/types/wlr_damage_ring.h @@ -27,8 +27,6 @@ struct wlr_damage_ring_buffer { }; struct wlr_damage_ring { - int32_t width, height; - // Difference between the current buffer and the previous one pixman_region32_t current; @@ -41,17 +39,6 @@ void wlr_damage_ring_init(struct wlr_damage_ring *ring); void wlr_damage_ring_finish(struct wlr_damage_ring *ring); -/** - * Set ring bounds and damage the ring fully. - * - * Next time damage will be added, it will be cropped to the ring bounds. - * If at least one of the dimensions is 0, bounds are removed. - * - * By default, a damage ring doesn't have bounds. - */ -void wlr_damage_ring_set_bounds(struct wlr_damage_ring *ring, - int32_t width, int32_t height); - /** * Add a region to the current damage. */ diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 80122ba36..6df64734d 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1518,10 +1518,6 @@ static void scene_node_output_update(struct wlr_scene_node *node, static void scene_output_update_geometry(struct wlr_scene_output *scene_output, bool force_update) { - int ring_width, ring_height; - wlr_output_transformed_resolution(scene_output->output, &ring_width, &ring_height); - wlr_damage_ring_set_bounds(&scene_output->damage_ring, ring_width, ring_height); - scene_output_damage_whole(scene_output); scene_node_output_update(&scene_output->scene->tree.node, @@ -2021,9 +2017,6 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, struct render_list_entry *list_data = list_con.render_list->data; int list_len = list_con.render_list->size / sizeof(*list_data); - wlr_damage_ring_set_bounds(&scene_output->damage_ring, - render_data.trans_width, render_data.trans_height); - if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_RERENDER) { scene_output_damage_whole(scene_output); } diff --git a/types/wlr_damage_ring.c b/types/wlr_damage_ring.c index fc80631e0..b5be7391b 100644 --- a/types/wlr_damage_ring.c +++ b/types/wlr_damage_ring.c @@ -9,11 +9,7 @@ #define WLR_DAMAGE_RING_MAX_RECTS 20 void wlr_damage_ring_init(struct wlr_damage_ring *ring) { - *ring = (struct wlr_damage_ring){ - .width = INT_MAX, - .height = INT_MAX, - }; - + *ring = (struct wlr_damage_ring){ 0 }; pixman_region32_init(&ring->current); wl_list_init(&ring->buffers); } @@ -33,22 +29,6 @@ void wlr_damage_ring_finish(struct wlr_damage_ring *ring) { } } -void wlr_damage_ring_set_bounds(struct wlr_damage_ring *ring, - int32_t width, int32_t height) { - if (width == 0 || height == 0) { - width = INT_MAX; - height = INT_MAX; - } - - if (ring->width == width && ring->height == height) { - return; - } - - ring->width = width; - ring->height = height; - wlr_damage_ring_add_whole(ring); -} - void wlr_damage_ring_add(struct wlr_damage_ring *ring, const pixman_region32_t *damage) { pixman_region32_union(&ring->current, &ring->current, damage); From 831e7fc7eea7a273e2156909abbb6fb3ca2e5623 Mon Sep 17 00:00:00 2001 From: Alexander Orezechowski Date: Wed, 9 Oct 2024 18:15:08 -0400 Subject: [PATCH 130/519] damage_ring: Add notes about damage being in buffer local coordinates --- include/wlr/types/wlr_damage_ring.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/include/wlr/types/wlr_damage_ring.h b/include/wlr/types/wlr_damage_ring.h index 3baeb9d5b..17f5bfe89 100644 --- a/include/wlr/types/wlr_damage_ring.h +++ b/include/wlr/types/wlr_damage_ring.h @@ -40,13 +40,15 @@ void wlr_damage_ring_init(struct wlr_damage_ring *ring); void wlr_damage_ring_finish(struct wlr_damage_ring *ring); /** - * Add a region to the current damage. + * Add a region to the current damage. The region must be in the buffer-local + * coordinate space. */ void wlr_damage_ring_add(struct wlr_damage_ring *ring, const pixman_region32_t *damage); /** - * Add a box to the current damage. + * Add a box to the current damage. The box must be in the buffer-local + * coordinate space. */ void wlr_damage_ring_add_box(struct wlr_damage_ring *ring, const struct wlr_box *box); @@ -65,6 +67,8 @@ void wlr_damage_ring_add_whole(struct wlr_damage_ring *ring); * * Users should damage the ring if an error occurs while rendering or * submitting the new buffer to the backend. + * + * The returned damage will be in the buffer-local coordinate space. */ void wlr_damage_ring_rotate_buffer(struct wlr_damage_ring *ring, struct wlr_buffer *buffer, pixman_region32_t *damage); From b9f0b9c766dfdec9addd177f13d23557acc4533c Mon Sep 17 00:00:00 2001 From: Alexander Orezechowski Date: Wed, 9 Oct 2024 18:15:33 -0400 Subject: [PATCH 131/519] scene: Apply damage ring as buffer local coordinates --- types/scene/wlr_scene.c | 60 ++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 6df64734d..4bd50493c 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -333,24 +333,24 @@ static void transform_output_box(struct wlr_box *box, const struct render_data * wlr_box_transform(box, box, transform, data->trans_width, data->trans_height); } +static void scene_output_damage_buffer_coords(struct wlr_scene_output *scene_output, + const pixman_region32_t *frame_damage) { + if (!pixman_region32_not_empty(frame_damage)) { + return; + } + + wlr_output_schedule_frame(scene_output->output); + wlr_damage_ring_add(&scene_output->damage_ring, frame_damage); + + pixman_region32_union(&scene_output->pending_commit_damage, + &scene_output->pending_commit_damage, frame_damage); +} + static void scene_output_damage(struct wlr_scene_output *scene_output, const pixman_region32_t *region) { int width, height; wlr_output_transformed_resolution(scene_output->output, &width, &height); - pixman_region32_t clipped; - pixman_region32_init(&clipped); - pixman_region32_intersect_rect(&clipped, region, 0, 0, width, height); - - if (!pixman_region32_not_empty(&clipped)) { - pixman_region32_fini(&clipped); - return; - } - - wlr_damage_ring_add(&scene_output->damage_ring, &clipped); - pixman_region32_fini(&clipped); - wlr_output_schedule_frame(scene_output->output); - struct wlr_output *output = scene_output->output; enum wl_output_transform transform = wlr_output_transform_invert(scene_output->output->transform); @@ -359,10 +359,9 @@ static void scene_output_damage(struct wlr_scene_output *scene_output, pixman_region32_init(&frame_damage); wlr_region_transform(&frame_damage, region, transform, width, height); - pixman_region32_union(&scene_output->pending_commit_damage, - &scene_output->pending_commit_damage, &frame_damage); - pixman_region32_intersect_rect(&scene_output->pending_commit_damage, - &scene_output->pending_commit_damage, 0, 0, output->width, output->height); + pixman_region32_intersect_rect(&frame_damage, &frame_damage, 0, 0, output->width, output->height); + scene_output_damage_buffer_coords(scene_output, &frame_damage); + pixman_region32_fini(&frame_damage); } @@ -1334,6 +1333,7 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren pixman_region32_copy(&render_region, &node->visible); pixman_region32_translate(&render_region, -data->logical.x, -data->logical.y); scale_output_damage(&render_region, data->scale); + transform_output_damage(&render_region, data); pixman_region32_intersect(&render_region, &render_region, &data->damage); if (!pixman_region32_not_empty(&render_region)) { pixman_region32_fini(&render_region); @@ -1349,16 +1349,15 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren }; scene_node_get_size(node, &dst_box.width, &dst_box.height); scale_box(&dst_box, data->scale); + transform_output_box(&dst_box, data); pixman_region32_t opaque; pixman_region32_init(&opaque); scene_node_opaque_region(node, x, y, &opaque); scale_output_damage(&opaque, data->scale); + transform_output_damage(&opaque, data); pixman_region32_subtract(&opaque, &render_region, &opaque); - transform_output_box(&dst_box, data); - transform_output_damage(&render_region, data); - switch (node->type) { case WLR_SCENE_NODE_TREE: assert(false); @@ -2055,7 +2054,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, } } - scene_output_damage(scene_output, &acc_damage); + scene_output_damage_buffer_coords(scene_output, &acc_damage); pixman_region32_fini(&acc_damage); } @@ -2154,6 +2153,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, pixman_region32_translate(&opaque, -scene_output->x, -scene_output->y); wlr_region_scale(&opaque, &opaque, render_data.scale); + transform_output_damage(&opaque, &render_data); pixman_region32_subtract(&background, &background, &opaque); pixman_region32_fini(&opaque); } @@ -2167,7 +2167,6 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, } } - transform_output_damage(&background, &render_data); wlr_render_pass_add_rect(render_pass, &(struct wlr_render_rect_options){ .box = { .width = buffer->width, .height = buffer->height }, .color = { .r = 0, .g = 0, .b = 0, .a = 1 }, @@ -2201,22 +2200,21 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, int64_t time_diff_ms = timespec_to_msec(&time_diff); float alpha = 1.0 - (double)time_diff_ms / HIGHLIGHT_DAMAGE_FADEOUT_TIME; - pixman_region32_t clip; - pixman_region32_init(&clip); - pixman_region32_copy(&clip, &damage->region); - transform_output_damage(&clip, &render_data); - wlr_render_pass_add_rect(render_pass, &(struct wlr_render_rect_options){ .box = { .width = buffer->width, .height = buffer->height }, .color = { .r = alpha * 0.5, .g = 0, .b = 0, .a = alpha * 0.5 }, - .clip = &clip, + .clip = &damage->region, }); - - pixman_region32_fini(&clip); } } - wlr_output_add_software_cursors_to_render_pass(output, render_pass, &render_data.damage); + pixman_region32_t cursor_damage; + pixman_region32_init(&cursor_damage); + pixman_region32_copy(&cursor_damage, &render_data.damage); + wlr_region_transform(&cursor_damage, &cursor_damage, + output->transform, resolution_width, resolution_height); + wlr_output_add_software_cursors_to_render_pass(output, render_pass, &cursor_damage); + pixman_region32_fini(&cursor_damage); pixman_region32_fini(&render_data.damage); From f1b8937345c751e38f3faca87cc3953ab769e1da Mon Sep 17 00:00:00 2001 From: Alexander Orezechowski Date: Wed, 9 Oct 2024 18:34:02 -0400 Subject: [PATCH 132/519] scene: Factor scaling into output transform function --- types/scene/wlr_scene.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 4bd50493c..f66e4fd9e 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -325,11 +325,24 @@ struct render_data { static void transform_output_damage(pixman_region32_t *damage, const struct render_data *data) { enum wl_output_transform transform = wlr_output_transform_invert(data->transform); + scale_output_damage(damage, data->scale); wlr_region_transform(damage, damage, transform, data->trans_width, data->trans_height); } +static int scale_length(int length, int offset, float scale) { + return round((offset + length) * scale) - round(offset * scale); +} + +static void scale_box(struct wlr_box *box, float scale) { + box->width = scale_length(box->width, box->x, scale); + box->height = scale_length(box->height, box->y, scale); + box->x = round(box->x * scale); + box->y = round(box->y * scale); +} + static void transform_output_box(struct wlr_box *box, const struct render_data *data) { enum wl_output_transform transform = wlr_output_transform_invert(data->transform); + scale_box(box, data->scale); wlr_box_transform(box, box, transform, data->trans_width, data->trans_height); } @@ -1103,17 +1116,6 @@ static void scene_node_get_size(struct wlr_scene_node *node, } } -static int scale_length(int length, int offset, float scale) { - return round((offset + length) * scale) - round(offset * scale); -} - -static void scale_box(struct wlr_box *box, float scale) { - box->width = scale_length(box->width, box->x, scale); - box->height = scale_length(box->height, box->y, scale); - box->x = round(box->x * scale); - box->y = round(box->y * scale); -} - void wlr_scene_node_set_enabled(struct wlr_scene_node *node, bool enabled) { if (node->enabled == enabled) { return; @@ -1332,7 +1334,6 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren pixman_region32_init(&render_region); pixman_region32_copy(&render_region, &node->visible); pixman_region32_translate(&render_region, -data->logical.x, -data->logical.y); - scale_output_damage(&render_region, data->scale); transform_output_damage(&render_region, data); pixman_region32_intersect(&render_region, &render_region, &data->damage); if (!pixman_region32_not_empty(&render_region)) { @@ -1348,13 +1349,11 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren .y = y, }; scene_node_get_size(node, &dst_box.width, &dst_box.height); - scale_box(&dst_box, data->scale); transform_output_box(&dst_box, data); pixman_region32_t opaque; pixman_region32_init(&opaque); scene_node_opaque_region(node, x, y, &opaque); - scale_output_damage(&opaque, data->scale); transform_output_damage(&opaque, data); pixman_region32_subtract(&opaque, &render_region, &opaque); @@ -2152,7 +2151,6 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, pixman_region32_intersect(&opaque, &opaque, &entry->node->visible); pixman_region32_translate(&opaque, -scene_output->x, -scene_output->y); - wlr_region_scale(&opaque, &opaque, render_data.scale); transform_output_damage(&opaque, &render_data); pixman_region32_subtract(&background, &background, &opaque); pixman_region32_fini(&opaque); From 009515161bd97d8f920d72d31ef462f2608688e8 Mon Sep 17 00:00:00 2001 From: Alexander Orezechowski Date: Thu, 10 Oct 2024 10:33:35 -0400 Subject: [PATCH 133/519] scene: Only accept buffer coordinates for damage --- types/scene/wlr_scene.c | 55 +++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index f66e4fd9e..dc88a4627 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -329,6 +329,14 @@ static void transform_output_damage(pixman_region32_t *damage, const struct rend wlr_region_transform(damage, damage, transform, data->trans_width, data->trans_height); } +static void output_to_buffer_coords(pixman_region32_t *damage, struct wlr_output *output) { + int width, height; + wlr_output_transformed_resolution(output, &width, &height); + + wlr_region_transform(damage, damage, + wlr_output_transform_invert(output->transform), width, height); +} + static int scale_length(int length, int offset, float scale) { return round((offset + length) * scale) - round(offset * scale); } @@ -359,32 +367,12 @@ static void scene_output_damage_buffer_coords(struct wlr_scene_output *scene_out &scene_output->pending_commit_damage, frame_damage); } -static void scene_output_damage(struct wlr_scene_output *scene_output, - const pixman_region32_t *region) { - int width, height; - wlr_output_transformed_resolution(scene_output->output, &width, &height); - - struct wlr_output *output = scene_output->output; - enum wl_output_transform transform = - wlr_output_transform_invert(scene_output->output->transform); - - pixman_region32_t frame_damage; - pixman_region32_init(&frame_damage); - wlr_region_transform(&frame_damage, region, transform, width, height); - - pixman_region32_intersect_rect(&frame_damage, &frame_damage, 0, 0, output->width, output->height); - scene_output_damage_buffer_coords(scene_output, &frame_damage); - - pixman_region32_fini(&frame_damage); -} - static void scene_output_damage_whole(struct wlr_scene_output *scene_output) { - int width, height; - wlr_output_transformed_resolution(scene_output->output, &width, &height); + struct wlr_output *output = scene_output->output; pixman_region32_t damage; - pixman_region32_init_rect(&damage, 0, 0, width, height); - scene_output_damage(scene_output, &damage); + pixman_region32_init_rect(&damage, 0, 0, output->width, output->height); + scene_output_damage_buffer_coords(scene_output, &damage); pixman_region32_fini(&damage); } @@ -401,7 +389,8 @@ static void scene_damage_outputs(struct wlr_scene *scene, pixman_region32_t *dam pixman_region32_translate(&output_damage, -scene_output->x, -scene_output->y); scale_output_damage(&output_damage, scene_output->output->scale); - scene_output_damage(scene_output, &output_damage); + output_to_buffer_coords(&output_damage, scene_output->output); + scene_output_damage_buffer_coords(scene_output, &output_damage); pixman_region32_fini(&output_damage); } } @@ -963,7 +952,8 @@ void wlr_scene_buffer_set_buffer_with_options(struct wlr_scene_buffer *scene_buf pixman_region32_translate(&output_damage, (int)round((lx - scene_output->x) * output_scale), (int)round((ly - scene_output->y) * output_scale)); - scene_output_damage(scene_output, &output_damage); + output_to_buffer_coords(&output_damage, scene_output->output); + scene_output_damage_buffer_coords(scene_output, &output_damage); pixman_region32_fini(&output_damage); } @@ -1381,7 +1371,7 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren struct wlr_texture *texture = scene_buffer_get_texture(scene_buffer, data->output->output->renderer); if (texture == NULL) { - scene_output_damage(data->output, &render_region); + scene_output_damage_buffer_coords(data->output, &render_region); break; } @@ -1567,8 +1557,19 @@ static void scene_output_handle_commit(struct wl_listener *listener, void *data) static void scene_output_handle_damage(struct wl_listener *listener, void *data) { struct wlr_scene_output *scene_output = wl_container_of(listener, scene_output, output_damage); + struct wlr_output *output = scene_output->output; struct wlr_output_event_damage *event = data; - scene_output_damage(scene_output, event->damage); + + int width, height; + wlr_output_transformed_resolution(output, &width, &height); + + pixman_region32_t damage; + pixman_region32_init(&damage); + pixman_region32_copy(&damage, event->damage); + wlr_region_transform(&damage, &damage, + wlr_output_transform_invert(output->transform), width, height); + scene_output_damage_buffer_coords(scene_output, &damage); + pixman_region32_fini(&damage); } static void scene_output_handle_needs_frame(struct wl_listener *listener, void *data) { From 8d8d5f5e94070e7118f0a19d76dec81ac0bd0819 Mon Sep 17 00:00:00 2001 From: Alexander Orezechowski Date: Thu, 10 Oct 2024 11:06:05 -0400 Subject: [PATCH 134/519] scene: Rename some functions --- types/scene/wlr_scene.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index dc88a4627..986d4c693 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -323,7 +323,7 @@ struct render_data { pixman_region32_t damage; }; -static void transform_output_damage(pixman_region32_t *damage, const struct render_data *data) { +static void logical_to_buffer_coords(pixman_region32_t *damage, const struct render_data *data) { enum wl_output_transform transform = wlr_output_transform_invert(data->transform); scale_output_damage(damage, data->scale); wlr_region_transform(damage, damage, transform, data->trans_width, data->trans_height); @@ -354,7 +354,7 @@ static void transform_output_box(struct wlr_box *box, const struct render_data * wlr_box_transform(box, box, transform, data->trans_width, data->trans_height); } -static void scene_output_damage_buffer_coords(struct wlr_scene_output *scene_output, +static void scene_output_damage(struct wlr_scene_output *scene_output, const pixman_region32_t *frame_damage) { if (!pixman_region32_not_empty(frame_damage)) { return; @@ -372,7 +372,7 @@ static void scene_output_damage_whole(struct wlr_scene_output *scene_output) { pixman_region32_t damage; pixman_region32_init_rect(&damage, 0, 0, output->width, output->height); - scene_output_damage_buffer_coords(scene_output, &damage); + scene_output_damage(scene_output, &damage); pixman_region32_fini(&damage); } @@ -390,7 +390,7 @@ static void scene_damage_outputs(struct wlr_scene *scene, pixman_region32_t *dam -scene_output->x, -scene_output->y); scale_output_damage(&output_damage, scene_output->output->scale); output_to_buffer_coords(&output_damage, scene_output->output); - scene_output_damage_buffer_coords(scene_output, &output_damage); + scene_output_damage(scene_output, &output_damage); pixman_region32_fini(&output_damage); } } @@ -953,7 +953,7 @@ void wlr_scene_buffer_set_buffer_with_options(struct wlr_scene_buffer *scene_buf (int)round((lx - scene_output->x) * output_scale), (int)round((ly - scene_output->y) * output_scale)); output_to_buffer_coords(&output_damage, scene_output->output); - scene_output_damage_buffer_coords(scene_output, &output_damage); + scene_output_damage(scene_output, &output_damage); pixman_region32_fini(&output_damage); } @@ -1324,7 +1324,7 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren pixman_region32_init(&render_region); pixman_region32_copy(&render_region, &node->visible); pixman_region32_translate(&render_region, -data->logical.x, -data->logical.y); - transform_output_damage(&render_region, data); + logical_to_buffer_coords(&render_region, data); pixman_region32_intersect(&render_region, &render_region, &data->damage); if (!pixman_region32_not_empty(&render_region)) { pixman_region32_fini(&render_region); @@ -1344,7 +1344,7 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren pixman_region32_t opaque; pixman_region32_init(&opaque); scene_node_opaque_region(node, x, y, &opaque); - transform_output_damage(&opaque, data); + logical_to_buffer_coords(&opaque, data); pixman_region32_subtract(&opaque, &render_region, &opaque); switch (node->type) { @@ -1371,7 +1371,7 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren struct wlr_texture *texture = scene_buffer_get_texture(scene_buffer, data->output->output->renderer); if (texture == NULL) { - scene_output_damage_buffer_coords(data->output, &render_region); + scene_output_damage(data->output, &render_region); break; } @@ -1568,7 +1568,7 @@ static void scene_output_handle_damage(struct wl_listener *listener, void *data) pixman_region32_copy(&damage, event->damage); wlr_region_transform(&damage, &damage, wlr_output_transform_invert(output->transform), width, height); - scene_output_damage_buffer_coords(scene_output, &damage); + scene_output_damage(scene_output, &damage); pixman_region32_fini(&damage); } @@ -2054,7 +2054,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, } } - scene_output_damage_buffer_coords(scene_output, &acc_damage); + scene_output_damage(scene_output, &acc_damage); pixman_region32_fini(&acc_damage); } @@ -2152,7 +2152,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, pixman_region32_intersect(&opaque, &opaque, &entry->node->visible); pixman_region32_translate(&opaque, -scene_output->x, -scene_output->y); - transform_output_damage(&opaque, &render_data); + logical_to_buffer_coords(&opaque, &render_data); pixman_region32_subtract(&background, &background, &opaque); pixman_region32_fini(&opaque); } From 514c4b4cce01aa9674b04fc86676a03c761c9c07 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 10 Oct 2024 16:23:49 +0200 Subject: [PATCH 135/519] backend: add timeline feature flag The output feature flag has a flaw: it's not possible to check whether the backend supports timelines during compositor initialization when we need to figure out whether we want to enable the linux-drm-syncobj-v1 protocol. Introduce a backend-wide feature flag to indicate support for timelines to address this defect. Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3904 --- backend/drm/drm.c | 6 ++++++ backend/headless/backend.c | 2 ++ backend/multi/backend.c | 12 ++++++++++++ include/wlr/backend.h | 5 +++++ 4 files changed, 25 insertions(+) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index a20442d40..2747c5891 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -126,6 +126,12 @@ bool check_drm_features(struct wlr_drm_backend *drm) { drm->supports_tearing_page_flips = drmGetCap(drm->fd, DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP, &cap) == 0 && cap == 1; } + drm->backend.features.timeline = drm->iface != &legacy_iface; + if (drm->parent) { + drm->backend.features.timeline = drm->backend.features.timeline && + drm->mgpu_renderer.wlr_rend->features.timeline; + } + if (env_parse_bool("WLR_DRM_NO_MODIFIERS")) { wlr_log(WLR_DEBUG, "WLR_DRM_NO_MODIFIERS set, disabling modifiers"); } else { diff --git a/backend/headless/backend.c b/backend/headless/backend.c index e643a06e1..c43c03971 100644 --- a/backend/headless/backend.c +++ b/backend/headless/backend.c @@ -80,6 +80,8 @@ struct wlr_backend *wlr_headless_backend_create(struct wl_event_loop *loop) { backend->event_loop_destroy.notify = handle_event_loop_destroy; wl_event_loop_add_destroy_listener(loop, &backend->event_loop_destroy); + backend->backend.features.timeline = true; + return &backend->backend; } diff --git a/backend/multi/backend.c b/backend/multi/backend.c index 7dc9f548e..82e96fd4c 100644 --- a/backend/multi/backend.c +++ b/backend/multi/backend.c @@ -225,6 +225,16 @@ static struct subbackend_state *multi_backend_get_subbackend(struct wlr_multi_ba return NULL; } +static void multi_backend_refresh_features(struct wlr_multi_backend *multi) { + multi->backend.features.timeline = true; + + struct subbackend_state *sub = NULL; + wl_list_for_each(sub, &multi->backends, link) { + multi->backend.features.timeline = multi->backend.features.timeline && + sub->backend->features.timeline; + } +} + bool wlr_multi_backend_add(struct wlr_backend *_multi, struct wlr_backend *backend) { assert(_multi && backend); @@ -256,6 +266,7 @@ bool wlr_multi_backend_add(struct wlr_backend *_multi, wl_signal_add(&backend->events.new_output, &sub->new_output); sub->new_output.notify = new_output_reemit; + multi_backend_refresh_features(multi); wl_signal_emit_mutable(&multi->events.backend_add, backend); return true; } @@ -270,6 +281,7 @@ void wlr_multi_backend_remove(struct wlr_backend *_multi, if (sub) { wl_signal_emit_mutable(&multi->events.backend_remove, backend); subbackend_state_destroy(sub); + multi_backend_refresh_features(multi); } } diff --git a/include/wlr/backend.h b/include/wlr/backend.h index 6e2269586..59feb644e 100644 --- a/include/wlr/backend.h +++ b/include/wlr/backend.h @@ -29,6 +29,11 @@ struct wlr_backend_output_state { struct wlr_backend { const struct wlr_backend_impl *impl; + struct { + // Whether wait/signal timelines are supported in output commits + bool timeline; + } features; + struct { /** Raised when destroyed */ struct wl_signal destroy; From 186bdc8da434bb7bf38b211dd6947fe418793f23 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 10 Oct 2024 16:30:31 +0200 Subject: [PATCH 136/519] output: use backend-wide timeline feature flag --- types/output/output.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/output/output.c b/types/output/output.c index 2dcd24229..e52803fc7 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -638,7 +638,7 @@ static bool output_basic_test(struct wlr_output *output, } if ((state->committed & (WLR_OUTPUT_STATE_WAIT_TIMELINE | WLR_OUTPUT_STATE_SIGNAL_TIMELINE)) && - !output->timeline) { + !output->backend->features.timeline) { wlr_log(WLR_DEBUG, "Wait/signal timelines are not supported for this output"); return false; } From 785e340f012d6ff2d76c0923ed6aead1c3b5fca6 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 10 Oct 2024 16:30:46 +0200 Subject: [PATCH 137/519] scene: use backend-wide timeline feature flag --- types/scene/wlr_scene.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 986d4c693..16ed63a1a 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1607,7 +1607,8 @@ struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene, } int drm_fd = wlr_backend_get_drm_fd(output->backend); - if (drm_fd >= 0 && output->timeline && output->renderer != NULL && output->renderer->features.timeline) { + if (drm_fd >= 0 && output->backend->features.timeline && + output->renderer != NULL && output->renderer->features.timeline) { scene_output->in_timeline = wlr_drm_syncobj_timeline_create(drm_fd); if (scene_output->in_timeline == NULL) { return NULL; From 1e949402b0f09bdf54011f491fda5a2d35743e0b Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 10 Oct 2024 16:30:57 +0200 Subject: [PATCH 138/519] output: drop output timeline flag This has been superseded by the backend-wide feature flag. --- backend/drm/drm.c | 5 ----- include/wlr/types/wlr_output.h | 2 -- 2 files changed, 7 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 2747c5891..13f934eb8 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -1652,11 +1652,6 @@ static bool connect_drm_connector(struct wlr_drm_connector *wlr_conn, output->non_desktop = non_desktop; } - output->timeline = drm->iface != &legacy_iface; - if (drm->parent) { - output->timeline = output->timeline && drm->mgpu_renderer.wlr_rend->features.timeline; - } - memset(wlr_conn->max_bpc_bounds, 0, sizeof(wlr_conn->max_bpc_bounds)); if (wlr_conn->props.max_bpc != 0) { if (!introspect_drm_prop_range(drm->fd, wlr_conn->props.max_bpc, diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 6227f0476..f7a7bed51 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -171,8 +171,6 @@ struct wlr_output { // true for example with VR headsets bool non_desktop; - // Whether wait/signal timelines are supported - bool timeline; // Commit sequence number. Incremented on each commit, may overflow. uint32_t commit_seq; From 402a862413c92af20f6563322f57b23265378448 Mon Sep 17 00:00:00 2001 From: Alexander Orezechowski Date: Fri, 11 Oct 2024 12:35:57 -0400 Subject: [PATCH 139/519] output: Change wlr_output_add_software_cursors_to_render_pass to take buffer coordinates Since wlr_damage_ring now only works with buffer local coordinates, this creates an inpedance mismatch for compositors that want to use this function. Instead of compositors needing to the the conversion itself, change thu function to take buffer local coordinates directly. --- include/wlr/types/wlr_output.h | 2 +- types/output/cursor.c | 23 ++++++++--------------- types/scene/wlr_scene.c | 9 +-------- 3 files changed, 10 insertions(+), 24 deletions(-) diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index f7a7bed51..3ed99b9ae 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -380,7 +380,7 @@ void wlr_output_lock_attach_render(struct wlr_output *output, bool lock); */ void wlr_output_lock_software_cursors(struct wlr_output *output, bool lock); /** - * Render software cursors. + * Render software cursors. The damage is in buffer-local coordinate space. * * This is a utility function that can be called when compositors render. */ diff --git a/types/output/cursor.c b/types/output/cursor.c index 7bc8d0d69..afa6b95bc 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -81,12 +81,6 @@ void wlr_output_add_software_cursors_to_render_pass(struct wlr_output *output, int width, height; wlr_output_transformed_resolution(output, &width, &height); - pixman_region32_t render_damage; - pixman_region32_init_rect(&render_damage, 0, 0, width, height); - if (damage != NULL) { - pixman_region32_intersect(&render_damage, &render_damage, damage); - } - struct wlr_output_cursor *cursor; wl_list_for_each(cursor, &output->cursors, link) { if (!cursor->enabled || !cursor->visible || @@ -101,20 +95,21 @@ void wlr_output_add_software_cursors_to_render_pass(struct wlr_output *output, struct wlr_box box; output_cursor_get_box(cursor, &box); + wlr_box_transform(&box, &box, + wlr_output_transform_invert(output->transform), width, height); pixman_region32_t cursor_damage; - pixman_region32_init_rect(&cursor_damage, box.x, box.y, box.width, box.height); - pixman_region32_intersect(&cursor_damage, &cursor_damage, &render_damage); + pixman_region32_init_rect(&cursor_damage, + box.x, box.y, box.width, box.height); + if (damage != NULL) { + pixman_region32_intersect(&cursor_damage, &cursor_damage, damage); + } + if (!pixman_region32_not_empty(&cursor_damage)) { pixman_region32_fini(&cursor_damage); continue; } - enum wl_output_transform transform = - wlr_output_transform_invert(output->transform); - wlr_box_transform(&box, &box, transform, width, height); - wlr_region_transform(&cursor_damage, &cursor_damage, transform, width, height); - wlr_render_pass_add_texture(render_pass, &(struct wlr_render_texture_options) { .texture = texture, .src_box = cursor->src_box, @@ -125,8 +120,6 @@ void wlr_output_add_software_cursors_to_render_pass(struct wlr_output *output, pixman_region32_fini(&cursor_damage); } - - pixman_region32_fini(&render_damage); } static void output_cursor_damage_whole(struct wlr_output_cursor *cursor) { diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 16ed63a1a..b5863f476 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -2208,14 +2208,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, } } - pixman_region32_t cursor_damage; - pixman_region32_init(&cursor_damage); - pixman_region32_copy(&cursor_damage, &render_data.damage); - wlr_region_transform(&cursor_damage, &cursor_damage, - output->transform, resolution_width, resolution_height); - wlr_output_add_software_cursors_to_render_pass(output, render_pass, &cursor_damage); - pixman_region32_fini(&cursor_damage); - + wlr_output_add_software_cursors_to_render_pass(output, render_pass, &render_data.damage); pixman_region32_fini(&render_data.damage); if (!wlr_render_pass_submit(render_pass)) { From 3b3ed21e61b871abdad83f0ee769effdb7d43e56 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 12 Oct 2024 17:11:32 +0200 Subject: [PATCH 140/519] backend/drm: fix timeline feature flag on multi-GPU setups This piece of code checks for multi-GPU renderer support, so it needs to run after the renderer is initialized. Fixes: 514c4b4cce01 ("backend: add timeline feature flag") Closes: https://github.com/swaywm/sway/issues/8382 --- backend/drm/backend.c | 6 ++++++ backend/drm/drm.c | 6 ------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/drm/backend.c b/backend/drm/backend.c index d166f4672..7f93e7550 100644 --- a/backend/drm/backend.c +++ b/backend/drm/backend.c @@ -264,6 +264,12 @@ struct wlr_backend *wlr_drm_backend_create(struct wlr_session *session, } } + drm->backend.features.timeline = drm->iface != &legacy_iface; + if (drm->parent) { + drm->backend.features.timeline = drm->backend.features.timeline && + drm->mgpu_renderer.wlr_rend->features.timeline; + } + drm->session_destroy.notify = handle_session_destroy; wl_signal_add(&session->events.destroy, &drm->session_destroy); diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 13f934eb8..d8724c02a 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -126,12 +126,6 @@ bool check_drm_features(struct wlr_drm_backend *drm) { drm->supports_tearing_page_flips = drmGetCap(drm->fd, DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP, &cap) == 0 && cap == 1; } - drm->backend.features.timeline = drm->iface != &legacy_iface; - if (drm->parent) { - drm->backend.features.timeline = drm->backend.features.timeline && - drm->mgpu_renderer.wlr_rend->features.timeline; - } - if (env_parse_bool("WLR_DRM_NO_MODIFIERS")) { wlr_log(WLR_DEBUG, "WLR_DRM_NO_MODIFIERS set, disabling modifiers"); } else { From 795265836719a7cdf6cad838477265f44454aabd Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Tue, 15 Oct 2024 15:33:24 +0300 Subject: [PATCH 141/519] scene: crop output buffer damage before adding This piece of logic was accidentally removed in 009515161bd97d8f920d72d31ef462f2608688e8. --- types/scene/wlr_scene.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index b5863f476..8cbb0b98e 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -355,16 +355,22 @@ static void transform_output_box(struct wlr_box *box, const struct render_data * } static void scene_output_damage(struct wlr_scene_output *scene_output, - const pixman_region32_t *frame_damage) { - if (!pixman_region32_not_empty(frame_damage)) { - return; + const pixman_region32_t *damage) { + struct wlr_output *output = scene_output->output; + + pixman_region32_t clipped; + pixman_region32_init(&clipped); + pixman_region32_intersect_rect(&clipped, damage, 0, 0, output->width, output->height); + + if (pixman_region32_not_empty(&clipped)) { + wlr_output_schedule_frame(scene_output->output); + wlr_damage_ring_add(&scene_output->damage_ring, &clipped); + + pixman_region32_union(&scene_output->pending_commit_damage, + &scene_output->pending_commit_damage, &clipped); } - wlr_output_schedule_frame(scene_output->output); - wlr_damage_ring_add(&scene_output->damage_ring, frame_damage); - - pixman_region32_union(&scene_output->pending_commit_damage, - &scene_output->pending_commit_damage, frame_damage); + pixman_region32_fini(&clipped); } static void scene_output_damage_whole(struct wlr_scene_output *scene_output) { From ba0cc8eb055caffaeb19945c1b8bff71864465d2 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 15 Oct 2024 17:06:32 +0200 Subject: [PATCH 142/519] backend/multi: Advance index on backend_commit wlr_multi_backend sorts the states it is given and tries to perform sequential backend-wide commits for each sub-backend with the states that belong to it. It did not manage the index correctly for the next iteration, so given N states for a backend it would perform N backend-wide commits. Clarify the logic by calculating a length rather than an end pointer and update the index after each iteration. --- backend/multi/backend.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/backend/multi/backend.c b/backend/multi/backend.c index 82e96fd4c..239524f16 100644 --- a/backend/multi/backend.c +++ b/backend/multi/backend.c @@ -126,22 +126,24 @@ static bool commit(struct wlr_backend *backend, qsort(by_backend, states_len, sizeof(by_backend[0]), compare_output_state_backend); bool ok = true; - for (size_t i = 0; i < states_len; i++) { + for (size_t i = 0; i < states_len;) { struct wlr_backend *sub = by_backend[i].output->backend; - size_t j = i; - while (j < states_len && by_backend[j].output->backend == sub) { - j++; + size_t len = 1; + while (i + len < states_len && + by_backend[i + len].output->backend == sub) { + len++; } if (test_only) { - ok = wlr_backend_test(sub, &by_backend[i], j - i); + ok = wlr_backend_test(sub, &by_backend[i], len); } else { - ok = wlr_backend_commit(sub, &by_backend[i], j - i); + ok = wlr_backend_commit(sub, &by_backend[i], len); } if (!ok) { break; } + i += len; } free(by_backend); From 47fb00f66d5a8367d0108bd960f99e51ab1a1318 Mon Sep 17 00:00:00 2001 From: YaoBing Xiao Date: Tue, 10 Sep 2024 21:51:26 +0800 Subject: [PATCH 143/519] wlr_linux_dmabuf_v1: log plane index in error messages for dma-buf Signed-off-by: YaoBing Xiao --- types/wlr_linux_dmabuf_v1.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/types/wlr_linux_dmabuf_v1.c b/types/wlr_linux_dmabuf_v1.c index 73a1d8f0f..77fe76ce9 100644 --- a/types/wlr_linux_dmabuf_v1.c +++ b/types/wlr_linux_dmabuf_v1.c @@ -213,11 +213,11 @@ static bool check_import_dmabuf(struct wlr_dmabuf_attributes *attribs, void *dat for (int i = 0; i < attribs->n_planes; i++) { uint32_t handle = 0; if (drmPrimeFDToHandle(linux_dmabuf->main_device_fd, attribs->fd[i], &handle) != 0) { - wlr_log_errno(WLR_DEBUG, "Failed to import DMA-BUF FD"); + wlr_log_errno(WLR_ERROR, "Failed to import DMA-BUF FD for plane %d", i); return false; } if (drmCloseBufferHandle(linux_dmabuf->main_device_fd, handle) != 0) { - wlr_log_errno(WLR_ERROR, "Failed to close buffer handle"); + wlr_log_errno(WLR_ERROR, "Failed to close buffer handle for plane %d", i); return false; } } From c87ab6465db9bd751c38b8c3499ec72d315d6042 Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 17 Oct 2024 14:24:05 +0100 Subject: [PATCH 144/519] Support direct scanout with src crop and dst boxes Enable scene-tree direct scanout of a single buffer with various options for scaling and source crop. This is intended to support direct scanout for fullscreen video with/without scaling, letterboxing/pillarboxing (e.g. 4:3 content on a 16:9 display), and source crop (e.g. when 1920x1088 planes are used for 1920x1080 video). This works by explicitly specifying the source crop and destination box for the primary buffer in the output state. DRM atomic and libliftoff backends will turn this into a crop and scale of the plane (assuming the hardware supports that). For the Wayland/X11/DRM-legacy backends I just reject this so scanout will be disabled. The previous behaviour is preserved if buffer_src_box and buffer_dst_box are unset: the buffer is displayed at native size at the top-left of the output with no crop. The change to `struct wlr_output_state` makes this a binary breaking change (but this works transparently for scene-tree compositors like labwc after a recompile). --- backend/drm/atomic.c | 44 ++++++++++++++------- backend/drm/legacy.c | 46 ++++++++++++++++------ backend/drm/libliftoff.c | 52 ++++++++++++++++--------- backend/wayland/output.c | 22 +++++++++++ backend/x11/output.c | 21 ++++++++++ include/types/wlr_output.h | 5 +++ include/wlr/types/wlr_output.h | 9 +++++ types/output/output.c | 70 ++++++++++++++++++++++++++++++---- types/output/state.c | 4 ++ types/scene/wlr_scene.c | 16 ++++---- 10 files changed, 230 insertions(+), 59 deletions(-) diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index 16b08f7a1..1844d024f 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -10,6 +11,7 @@ #include "backend/drm/fb.h" #include "backend/drm/iface.h" #include "backend/drm/util.h" +#include "types/wlr_output.h" static char *atomic_commit_flags_str(uint32_t flags) { const char *const l[] = { @@ -354,7 +356,8 @@ static void plane_disable(struct atomic *atom, struct wlr_drm_plane *plane) { static void set_plane_props(struct atomic *atom, struct wlr_drm_backend *drm, struct wlr_drm_plane *plane, struct wlr_drm_fb *fb, uint32_t crtc_id, - int32_t x, int32_t y) { + const struct wlr_box *dst_box, + const struct wlr_fbox *src_box) { uint32_t id = plane->id; const struct wlr_drm_plane_props *props = &plane->props; @@ -364,20 +367,17 @@ static void set_plane_props(struct atomic *atom, struct wlr_drm_backend *drm, return; } - uint32_t width = fb->wlr_buf->width; - uint32_t height = fb->wlr_buf->height; - // The src_* properties are in 16.16 fixed point - atomic_add(atom, id, props->src_x, 0); - atomic_add(atom, id, props->src_y, 0); - atomic_add(atom, id, props->src_w, (uint64_t)width << 16); - atomic_add(atom, id, props->src_h, (uint64_t)height << 16); - atomic_add(atom, id, props->crtc_w, width); - atomic_add(atom, id, props->crtc_h, height); + atomic_add(atom, id, props->src_x, src_box->x * (1 << 16)); + atomic_add(atom, id, props->src_y, src_box->y * (1 << 16)); + atomic_add(atom, id, props->src_w, src_box->width * (1 << 16)); + atomic_add(atom, id, props->src_h, src_box->height * (1 << 16)); atomic_add(atom, id, props->fb_id, fb->id); atomic_add(atom, id, props->crtc_id, crtc_id); - atomic_add(atom, id, props->crtc_x, (uint64_t)x); - atomic_add(atom, id, props->crtc_y, (uint64_t)y); + atomic_add(atom, id, props->crtc_x, dst_box->x); + atomic_add(atom, id, props->crtc_y, dst_box->y); + atomic_add(atom, id, props->crtc_w, dst_box->width); + atomic_add(atom, id, props->crtc_h, dst_box->height); } static bool supports_cursor_hotspots(const struct wlr_drm_plane *plane) { @@ -437,8 +437,14 @@ static void atomic_connector_add(struct atomic *atom, if (crtc->props.vrr_enabled != 0) { atomic_add(atom, crtc->id, crtc->props.vrr_enabled, state->vrr_enabled); } + + struct wlr_fbox src_box; + struct wlr_box dst_box; + output_state_get_buffer_src_box(state->base, &src_box); + output_state_get_buffer_dst_box(state->base, &dst_box); + set_plane_props(atom, drm, crtc->primary, state->primary_fb, crtc->id, - 0, 0); + &dst_box, &src_box); if (crtc->primary->props.fb_damage_clips != 0) { atomic_add(atom, crtc->primary->id, crtc->primary->props.fb_damage_clips, state->fb_damage_clips); @@ -451,8 +457,18 @@ static void atomic_connector_add(struct atomic *atom, } if (crtc->cursor) { if (drm_connector_is_cursor_visible(conn)) { + struct wlr_fbox cursor_src = { + .width = state->cursor_fb->wlr_buf->width, + .height = state->cursor_fb->wlr_buf->height, + }; + struct wlr_box cursor_dst = { + .x = conn->cursor_x, + .y = conn->cursor_y, + .width = state->cursor_fb->wlr_buf->width, + .height = state->cursor_fb->wlr_buf->height, + }; set_plane_props(atom, drm, crtc->cursor, state->cursor_fb, - crtc->id, conn->cursor_x, conn->cursor_y); + crtc->id, &cursor_dst, &cursor_src); if (supports_cursor_hotspots(crtc->cursor)) { atomic_add(atom, crtc->cursor->id, crtc->cursor->props.hotspot_x, conn->cursor_hotspot_x); diff --git a/backend/drm/legacy.c b/backend/drm/legacy.c index 0c591f4dd..371418c6f 100644 --- a/backend/drm/legacy.c +++ b/backend/drm/legacy.c @@ -7,6 +7,7 @@ #include "backend/drm/fb.h" #include "backend/drm/iface.h" #include "backend/drm/util.h" +#include "types/wlr_output.h" static bool legacy_fb_props_match(struct wlr_drm_fb *fb1, struct wlr_drm_fb *fb2) { @@ -39,20 +40,41 @@ static bool legacy_crtc_test(const struct wlr_drm_connector_state *state, struct wlr_drm_connector *conn = state->connector; struct wlr_drm_crtc *crtc = conn->crtc; - if ((state->base->committed & WLR_OUTPUT_STATE_BUFFER) && !modeset) { - struct wlr_drm_fb *pending_fb = state->primary_fb; - - struct wlr_drm_fb *prev_fb = crtc->primary->queued_fb; - if (!prev_fb) { - prev_fb = crtc->primary->current_fb; + if (state->base->committed & WLR_OUTPUT_STATE_BUFFER) { + // If the size doesn't match, reject buffer (scaling is not supported) + int pending_width, pending_height; + output_pending_resolution(&state->connector->output, state->base, + &pending_width, &pending_height); + if (state->base->buffer->width != pending_width || + state->base->buffer->height != pending_height) { + wlr_log(WLR_DEBUG, "Primary buffer size mismatch"); + return false; + } + // Source crop is also not supported + struct wlr_fbox src_box; + output_state_get_buffer_src_box(state->base, &src_box); + if (src_box.x != 0.0 || src_box.y != 0.0 || + src_box.width != (double)state->base->buffer->width || + src_box.height != (double)state->base->buffer->height) { + wlr_log(WLR_DEBUG, "Source crop not supported in DRM-legacy output"); + return false; } - /* Legacy is only guaranteed to be able to display a FB if it's been - * allocated the same way as the previous one. */ - if (prev_fb != NULL && !legacy_fb_props_match(prev_fb, pending_fb)) { - wlr_drm_conn_log(conn, WLR_DEBUG, - "Cannot change scan-out buffer parameters with legacy KMS API"); - return false; + if (!modeset) { + struct wlr_drm_fb *pending_fb = state->primary_fb; + + struct wlr_drm_fb *prev_fb = crtc->primary->queued_fb; + if (!prev_fb) { + prev_fb = crtc->primary->current_fb; + } + + /* Legacy is only guaranteed to be able to display a FB if it's been + * allocated the same way as the previous one. */ + if (prev_fb != NULL && !legacy_fb_props_match(prev_fb, pending_fb)) { + wlr_drm_conn_log(conn, WLR_DEBUG, + "Cannot change scan-out buffer parameters with legacy KMS API"); + return false; + } } } diff --git a/backend/drm/libliftoff.c b/backend/drm/libliftoff.c index a87a48265..297528488 100644 --- a/backend/drm/libliftoff.c +++ b/backend/drm/libliftoff.c @@ -4,12 +4,14 @@ #include #include #include +#include #include #include "backend/drm/drm.h" #include "backend/drm/fb.h" #include "backend/drm/iface.h" #include "config.h" +#include "types/wlr_output.h" static void log_handler(enum liftoff_log_priority priority, const char *fmt, va_list args) { enum wlr_log_importance importance = WLR_SILENT; @@ -149,25 +151,23 @@ static bool add_prop(drmModeAtomicReq *req, uint32_t obj, } static bool set_plane_props(struct wlr_drm_plane *plane, - struct liftoff_layer *layer, struct wlr_drm_fb *fb, int32_t x, int32_t y, uint64_t zpos) { + struct liftoff_layer *layer, struct wlr_drm_fb *fb, uint64_t zpos, + const struct wlr_box *dst_box, const struct wlr_fbox *src_box) { if (fb == NULL) { wlr_log(WLR_ERROR, "Failed to acquire FB for plane %"PRIu32, plane->id); return false; } - uint32_t width = fb->wlr_buf->width; - uint32_t height = fb->wlr_buf->height; - - // The SRC_* properties are in 16.16 fixed point + // The src_* properties are in 16.16 fixed point return liftoff_layer_set_property(layer, "zpos", zpos) == 0 && - liftoff_layer_set_property(layer, "SRC_X", 0) == 0 && - liftoff_layer_set_property(layer, "SRC_Y", 0) == 0 && - liftoff_layer_set_property(layer, "SRC_W", (uint64_t)width << 16) == 0 && - liftoff_layer_set_property(layer, "SRC_H", (uint64_t)height << 16) == 0 && - liftoff_layer_set_property(layer, "CRTC_X", (uint64_t)x) == 0 && - liftoff_layer_set_property(layer, "CRTC_Y", (uint64_t)y) == 0 && - liftoff_layer_set_property(layer, "CRTC_W", width) == 0 && - liftoff_layer_set_property(layer, "CRTC_H", height) == 0 && + liftoff_layer_set_property(layer, "SRC_X", src_box->x * (1 << 16)) == 0 && + liftoff_layer_set_property(layer, "SRC_Y", src_box->y * (1 << 16)) == 0 && + liftoff_layer_set_property(layer, "SRC_W", src_box->width * (1 << 16)) == 0 && + liftoff_layer_set_property(layer, "SRC_H", src_box->height * (1 << 16)) == 0 && + liftoff_layer_set_property(layer, "CRTC_X", dst_box->x) == 0 && + liftoff_layer_set_property(layer, "CRTC_Y", dst_box->y) == 0 && + liftoff_layer_set_property(layer, "CRTC_W", dst_box->width) == 0 && + liftoff_layer_set_property(layer, "CRTC_H", dst_box->height) == 0 && liftoff_layer_set_property(layer, "FB_ID", fb->id) == 0; } @@ -331,9 +331,17 @@ static bool add_connector(drmModeAtomicReq *req, if (crtc->props.vrr_enabled != 0) { ok = ok && add_prop(req, crtc->id, crtc->props.vrr_enabled, state->vrr_enabled); } + struct wlr_fbox src_box; + struct wlr_box dst_box; + output_state_get_buffer_src_box(state->base, &src_box); + output_state_get_buffer_dst_box(state->base, &dst_box); + ok = ok && - set_plane_props(crtc->primary, crtc->primary->liftoff_layer, state->primary_fb, 0, 0, 0) && - set_plane_props(crtc->primary, crtc->liftoff_composition_layer, state->primary_fb, 0, 0, 0); + set_plane_props(crtc->primary, crtc->primary->liftoff_layer, + state->primary_fb, 0, &dst_box, &src_box); + ok = ok && + set_plane_props(crtc->primary, crtc->liftoff_composition_layer, + state->primary_fb, 0, &dst_box, &src_box); liftoff_layer_set_property(crtc->primary->liftoff_layer, "FB_DAMAGE_CLIPS", state->fb_damage_clips); liftoff_layer_set_property(crtc->liftoff_composition_layer, @@ -360,9 +368,19 @@ static bool add_connector(drmModeAtomicReq *req, if (crtc->cursor) { if (drm_connector_is_cursor_visible(conn)) { + struct wlr_fbox cursor_src = { + .width = state->cursor_fb->wlr_buf->width, + .height = state->cursor_fb->wlr_buf->height, + }; + struct wlr_box cursor_dst = { + .x = conn->cursor_x, + .y = conn->cursor_y, + .width = state->cursor_fb->wlr_buf->width, + .height = state->cursor_fb->wlr_buf->height, + }; ok = ok && set_plane_props(crtc->cursor, crtc->cursor->liftoff_layer, - state->cursor_fb, conn->cursor_x, conn->cursor_y, - wl_list_length(&crtc->layers) + 1); + state->cursor_fb, wl_list_length(&crtc->layers) + 1, + &cursor_dst, &cursor_src); } else { ok = ok && disable_plane(crtc->cursor); } diff --git a/backend/wayland/output.c b/backend/wayland/output.c index e672028b7..5b8dfb0f2 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -300,6 +300,28 @@ static bool output_test(struct wlr_output *wlr_output, struct wlr_wl_output *output = get_wl_output_from_output(wlr_output); + if (state->committed & WLR_OUTPUT_STATE_BUFFER) { + // If the size doesn't match, reject buffer (scaling is not currently + // supported but could be implemented with viewporter) + int pending_width, pending_height; + output_pending_resolution(wlr_output, state, + &pending_width, &pending_height); + if (state->buffer->width != pending_width || + state->buffer->height != pending_height) { + wlr_log(WLR_DEBUG, "Primary buffer size mismatch"); + return false; + } + // Source crop is also not currently supported + struct wlr_fbox src_box; + output_state_get_buffer_src_box(state, &src_box); + if (src_box.x != 0.0 || src_box.y != 0.0 || + src_box.width != (double)state->buffer->width || + src_box.height != (double)state->buffer->height) { + wlr_log(WLR_DEBUG, "Source crop not supported in wayland output"); + return false; + } + } + uint32_t unsupported = state->committed & ~SUPPORTED_OUTPUT_STATE; if (unsupported != 0) { wlr_log(WLR_DEBUG, "Unsupported output state fields: 0x%"PRIx32, diff --git a/backend/x11/output.c b/backend/x11/output.c index b400f3df9..67f1dae95 100644 --- a/backend/x11/output.c +++ b/backend/x11/output.c @@ -129,6 +129,27 @@ static bool output_test(struct wlr_output *wlr_output, return false; } + if (state->committed & WLR_OUTPUT_STATE_BUFFER) { + // If the size doesn't match, reject buffer (scaling is not supported) + int pending_width, pending_height; + output_pending_resolution(wlr_output, state, + &pending_width, &pending_height); + if (state->buffer->width != pending_width || + state->buffer->height != pending_height) { + wlr_log(WLR_DEBUG, "Primary buffer size mismatch"); + return false; + } + // Source crop is not supported + struct wlr_fbox src_box; + output_state_get_buffer_src_box(state, &src_box); + if (src_box.x != 0.0 || src_box.y != 0.0 || + src_box.width != (double)state->buffer->width || + src_box.height != (double)state->buffer->height) { + wlr_log(WLR_DEBUG, "Source crop not supported in X11 output"); + return false; + } + } + // All we can do to influence adaptive sync on the X11 backend is set the // _VARIABLE_REFRESH window property like mesa automatically does. We don't // have any control beyond that, so we set the state to enabled on creating diff --git a/include/types/wlr_output.h b/include/types/wlr_output.h index bd095d8f2..2dc979c66 100644 --- a/include/types/wlr_output.h +++ b/include/types/wlr_output.h @@ -26,4 +26,9 @@ void output_defer_present(struct wlr_output *output, struct wlr_output_event_pre bool output_prepare_commit(struct wlr_output *output, const struct wlr_output_state *state); void output_apply_commit(struct wlr_output *output, const struct wlr_output_state *state); +void output_state_get_buffer_src_box(const struct wlr_output_state *state, + struct wlr_fbox *out); +void output_state_get_buffer_dst_box(const struct wlr_output_state *state, + struct wlr_box *out); + #endif diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 3ed99b9ae..e14034389 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -17,6 +17,7 @@ #include #include #include +#include enum wlr_output_mode_aspect_ratio { WLR_OUTPUT_MODE_ASPECT_RATIO_NONE, @@ -94,6 +95,14 @@ struct wlr_output_state { enum wl_output_subpixel subpixel; struct wlr_buffer *buffer; + // Source crop for the buffer. If all zeros then no crop is applied. + // Double-buffered by WLR_OUTPUT_STATE_BUFFER along with `buffer`. + struct wlr_fbox buffer_src_box; + // Destination rect to scale the buffer to (after source crop). If width + // and height are zero then the buffer is displayed at native size. + // Double-buffered by WLR_OUTPUT_STATE_BUFFER along with `buffer`. + struct wlr_box buffer_dst_box; + /* Request a tearing page-flip. When enabled, this may cause the output to * display a part of the previous buffer and a part of the current buffer at * the same time. The backend may reject the commit if a tearing page-flip diff --git a/types/output/output.c b/types/output/output.c index e52803fc7..f8ed118f5 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -548,14 +548,37 @@ static uint32_t output_compare_state(struct wlr_output *output, static bool output_basic_test(struct wlr_output *output, const struct wlr_output_state *state) { if (state->committed & WLR_OUTPUT_STATE_BUFFER) { - // If the size doesn't match, reject buffer (scaling is not - // supported) + struct wlr_fbox src_box; + output_state_get_buffer_src_box(state, &src_box); + + // Source box must be contained within the buffer + if (src_box.x < 0.0 || src_box.y < 0.0 || + src_box.x + src_box.width > state->buffer->width || + src_box.y + src_box.height > state->buffer->height) { + wlr_log(WLR_ERROR, "Tried to commit with invalid buffer_src_box"); + return false; + } + + // Source box must not be empty (but it can be smaller than 1 pixel, + // some DRM devices support sub-pixel crops) + if (wlr_fbox_empty(&src_box)) { + wlr_log(WLR_ERROR, "Tried to commit with an empty buffer_src_box"); + return false; + } + + // Destination box cannot be entirely off-screen (but it also doesn't + // have to be entirely on-screen). This also checks the dst box is + // not empty. int pending_width, pending_height; - output_pending_resolution(output, state, - &pending_width, &pending_height); - if (state->buffer->width != pending_width || - state->buffer->height != pending_height) { - wlr_log(WLR_DEBUG, "Primary buffer size mismatch"); + output_pending_resolution(output, state, &pending_width, &pending_height); + struct wlr_box output_box = { + .width = pending_width, + .height = pending_height + }; + struct wlr_box dst_box; + output_state_get_buffer_dst_box(state, &dst_box); + if (!wlr_box_intersection(&output_box, &output_box, &dst_box)) { + wlr_log(WLR_ERROR, "Primary buffer is entirely off-screen or 0-sized"); return false; } } else { @@ -845,6 +868,39 @@ void output_defer_present(struct wlr_output *output, struct wlr_output_event_pre deferred_present_event_handle_idle, deferred); } +void output_state_get_buffer_src_box(const struct wlr_output_state *state, + struct wlr_fbox *out) { + out->x = state->buffer_src_box.x; + out->y = state->buffer_src_box.y; + // If the source box is unset then default to the whole buffer. + if (state->buffer_src_box.width == 0.0 && + state->buffer_src_box.height == 0.0) { + out->width = (double)state->buffer->width; + out->height = (double)state->buffer->height; + } else { + out->width = state->buffer_src_box.width; + out->height = state->buffer_src_box.height; + } +} + +void output_state_get_buffer_dst_box(const struct wlr_output_state *state, + struct wlr_box *out) { + out->x = state->buffer_dst_box.x; + out->y = state->buffer_dst_box.y; + // If the dst box is unset then default to source crop size (which itself + // defaults to the whole buffer size if unset) + if (state->buffer_dst_box.width == 0 && + state->buffer_dst_box.height == 0) { + struct wlr_fbox src_box; + output_state_get_buffer_src_box(state, &src_box); + out->width = (int)src_box.width; + out->height = (int)src_box.height; + } else { + out->width = state->buffer_dst_box.width; + out->height = state->buffer_dst_box.height; + } +} + void wlr_output_send_request_state(struct wlr_output *output, const struct wlr_output_state *state) { uint32_t unchanged = output_compare_state(output, state); diff --git a/types/output/state.c b/types/output/state.c index 465b54ada..72849c027 100644 --- a/types/output/state.c +++ b/types/output/state.c @@ -142,6 +142,8 @@ bool wlr_output_state_copy(struct wlr_output_state *dst, WLR_OUTPUT_STATE_WAIT_TIMELINE | WLR_OUTPUT_STATE_SIGNAL_TIMELINE); copy.buffer = NULL; + copy.buffer_src_box = (struct wlr_fbox){0}; + copy.buffer_dst_box = (struct wlr_box){0}; pixman_region32_init(©.damage); copy.gamma_lut = NULL; copy.gamma_lut_size = 0; @@ -150,6 +152,8 @@ bool wlr_output_state_copy(struct wlr_output_state *dst, if (src->committed & WLR_OUTPUT_STATE_BUFFER) { wlr_output_state_set_buffer(©, src->buffer); + copy.buffer_src_box = src->buffer_src_box; + copy.buffer_dst_box = src->buffer_dst_box; } if (src->committed & WLR_OUTPUT_STATE_DAMAGE) { diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 8cbb0b98e..86547c732 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1831,6 +1831,7 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry, return false; } + // The native size of the buffer after any transform is applied int default_width = buffer->buffer->width; int default_height = buffer->buffer->height; wlr_output_transform_coords(buffer->transform, &default_width, &default_height); @@ -1839,11 +1840,6 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry, .height = default_height, }; - if (!wlr_fbox_empty(&buffer->src_box) && - !wlr_fbox_equal(&buffer->src_box, &default_box)) { - return false; - } - if (buffer->transform != data->transform) { return false; } @@ -1851,10 +1847,6 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry, struct wlr_box node_box = { .x = entry->x, .y = entry->y }; scene_node_get_size(node, &node_box.width, &node_box.height); - if (!wlr_box_equal(&data->logical, &node_box)) { - return false; - } - if (buffer->primary_output == scene_output) { struct wlr_linux_dmabuf_feedback_v1_init_options options = { .main_renderer = scene_output->output->renderer, @@ -1871,6 +1863,12 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry, return false; } + if (!wlr_fbox_empty(&buffer->src_box) && + !wlr_fbox_equal(&buffer->src_box, &default_box)) { + pending.buffer_src_box = buffer->src_box; + } + pending.buffer_dst_box = node_box; + wlr_output_state_set_buffer(&pending, buffer->buffer); if (buffer->wait_timeline != NULL) { wlr_output_state_set_wait_timeline(&pending, buffer->wait_timeline, buffer->wait_point); From 527b77b445f0c251e826c6e6d882ba600d07c0d5 Mon Sep 17 00:00:00 2001 From: xurui Date: Wed, 23 Oct 2024 18:43:27 +0800 Subject: [PATCH 145/519] xdg-toplevel: use enum_is_valid functions Signed-off-by: xurui --- types/xdg_shell/wlr_xdg_toplevel.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/types/xdg_shell/wlr_xdg_toplevel.c b/types/xdg_shell/wlr_xdg_toplevel.c index 556dcf80c..bfb583bcc 100644 --- a/types/xdg_shell/wlr_xdg_toplevel.c +++ b/types/xdg_shell/wlr_xdg_toplevel.c @@ -297,18 +297,8 @@ static void xdg_toplevel_handle_resize(struct wl_client *client, struct wlr_seat_client *seat = wlr_seat_client_from_resource(seat_resource); - switch (edges) { - case XDG_TOPLEVEL_RESIZE_EDGE_NONE: - case XDG_TOPLEVEL_RESIZE_EDGE_TOP: - case XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM: - case XDG_TOPLEVEL_RESIZE_EDGE_LEFT: - case XDG_TOPLEVEL_RESIZE_EDGE_RIGHT: - case XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT: - case XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT: - case XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT: - case XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT: - break; - default: + uint32_t version = wl_resource_get_version(toplevel->base->resource); + if (!xdg_toplevel_resize_edge_is_valid(edges, version)) { wl_resource_post_error(toplevel->base->resource, XDG_TOPLEVEL_ERROR_INVALID_RESIZE_EDGE, "provided value is not a valid variant of the resize_edge enum"); From 0d467ef9aaf5142175d7fe64c21c92e6799becd9 Mon Sep 17 00:00:00 2001 From: xurui Date: Wed, 23 Oct 2024 18:46:16 +0800 Subject: [PATCH 146/519] xdg-positioner: use enum_is_valid functions Signed-off-by: xurui --- types/xdg_shell/wlr_xdg_positioner.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/types/xdg_shell/wlr_xdg_positioner.c b/types/xdg_shell/wlr_xdg_positioner.c index 380c11310..e98009187 100644 --- a/types/xdg_shell/wlr_xdg_positioner.c +++ b/types/xdg_shell/wlr_xdg_positioner.c @@ -52,7 +52,8 @@ static void xdg_positioner_handle_set_anchor(struct wl_client *client, struct wlr_xdg_positioner *positioner = wlr_xdg_positioner_from_resource(resource); - if (anchor > XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT) { + uint32_t version = wl_resource_get_version(resource); + if (!xdg_positioner_anchor_is_valid(anchor, version)) { wl_resource_post_error(resource, XDG_POSITIONER_ERROR_INVALID_INPUT, "invalid anchor value"); @@ -67,7 +68,8 @@ static void xdg_positioner_handle_set_gravity(struct wl_client *client, struct wlr_xdg_positioner *positioner = wlr_xdg_positioner_from_resource(resource); - if (gravity > XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT) { + uint32_t version = wl_resource_get_version(resource); + if (!xdg_positioner_gravity_is_valid(gravity, version)) { wl_resource_post_error(resource, XDG_POSITIONER_ERROR_INVALID_INPUT, "invalid gravity value"); @@ -83,6 +85,14 @@ static void xdg_positioner_handle_set_constraint_adjustment( struct wlr_xdg_positioner *positioner = wlr_xdg_positioner_from_resource(resource); + uint32_t version = wl_resource_get_version(resource); + if (!xdg_positioner_constraint_adjustment_is_valid(constraint_adjustment, version)) { + wl_resource_post_error(resource, + XDG_POSITIONER_ERROR_INVALID_INPUT, + "invalid constraint_adjustment value"); + return; + } + positioner->rules.constraint_adjustment = constraint_adjustment; } From da8f7a07ba8c0767ffca134f871339cc475d5839 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Thu, 24 Oct 2024 10:49:08 +0200 Subject: [PATCH 147/519] backend/headless: actually perform output test Currently the headless backend does not actually implement the wlr_output_test function, causing tests containing output state unsupported by the headless backend to succeed while committing the same state will always fail. This commit fixes that by actually hooking up the already exisiting test function. References: https://codeberg.org/river/river/issues/1154 --- backend/headless/output.c | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/headless/output.c b/backend/headless/output.c index 5aaf1bd8a..5a00e2e91 100644 --- a/backend/headless/output.c +++ b/backend/headless/output.c @@ -89,6 +89,7 @@ static void output_destroy(struct wlr_output *wlr_output) { static const struct wlr_output_impl output_impl = { .destroy = output_destroy, + .test = output_test, .commit = output_commit, }; From 0ba19824889631199b83a3a1e160a7c7f085a493 Mon Sep 17 00:00:00 2001 From: Peng Liu Date: Thu, 24 Oct 2024 09:06:22 +0800 Subject: [PATCH 148/519] backend/x11: delete xcb conn check NULL xcb_connect always returns a non-NULL pointer to a xcb_connection_t, even on failure. Signed-off-by: Peng Liu --- backend/x11/backend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/x11/backend.c b/backend/x11/backend.c index 31ed2aafe..a265c2a65 100644 --- a/backend/x11/backend.c +++ b/backend/x11/backend.c @@ -409,7 +409,7 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_event_loop *loop, wl_list_init(&x11->outputs); x11->xcb = xcb_connect(x11_display, NULL); - if (!x11->xcb || xcb_connection_has_error(x11->xcb)) { + if (xcb_connection_has_error(x11->xcb)) { wlr_log(WLR_ERROR, "Failed to open xcb connection"); goto error_x11; } From e51ce333bc3c087cb333e9581e61bf80353291e9 Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 24 Oct 2024 11:09:57 +0100 Subject: [PATCH 149/519] scene: Apply output offset for direct scanout When setting the primary buffer location for direct scanout, subtract the offset of that output to put the buffer location in output-relative coordinates. Fixes #3910 --- include/wlr/types/wlr_output.h | 6 ++++-- types/scene/wlr_scene.c | 9 +++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index e14034389..1cc7baecd 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -96,11 +96,13 @@ struct wlr_output_state { struct wlr_buffer *buffer; // Source crop for the buffer. If all zeros then no crop is applied. + // As usual with source crop, this is in buffer coordinates. // Double-buffered by WLR_OUTPUT_STATE_BUFFER along with `buffer`. struct wlr_fbox buffer_src_box; // Destination rect to scale the buffer to (after source crop). If width - // and height are zero then the buffer is displayed at native size. - // Double-buffered by WLR_OUTPUT_STATE_BUFFER along with `buffer`. + // and height are zero then the buffer is displayed at native size. The + // offset is relative to the origin of this output. Double-buffered by + // WLR_OUTPUT_STATE_BUFFER along with `buffer`. struct wlr_box buffer_dst_box; /* Request a tearing page-flip. When enabled, this may cause the output to diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 86547c732..e4d68654a 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1844,9 +1844,6 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry, return false; } - struct wlr_box node_box = { .x = entry->x, .y = entry->y }; - scene_node_get_size(node, &node_box.width, &node_box.height); - if (buffer->primary_output == scene_output) { struct wlr_linux_dmabuf_feedback_v1_init_options options = { .main_renderer = scene_output->output->renderer, @@ -1867,7 +1864,11 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry, !wlr_fbox_equal(&buffer->src_box, &default_box)) { pending.buffer_src_box = buffer->src_box; } - pending.buffer_dst_box = node_box; + + // Translate the location from scene coordinates to output coordinates + pending.buffer_dst_box.x = entry->x - scene_output->x; + pending.buffer_dst_box.y = entry->y - scene_output->y; + scene_node_get_size(node, &pending.buffer_dst_box.width, &pending.buffer_dst_box.height); wlr_output_state_set_buffer(&pending, buffer->buffer); if (buffer->wait_timeline != NULL) { From 6006023a377868187f73d2e0922bbe952072684f Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Sat, 5 Oct 2024 11:37:22 +0300 Subject: [PATCH 150/519] Use WLR_PRIVATE for private fields --- CONTRIBUTING.md | 13 ++- include/wlr/render/drm_syncobj.h | 14 +-- include/wlr/render/wlr_renderer.h | 6 +- include/wlr/types/wlr_alpha_modifier_v1.h | 6 +- include/wlr/types/wlr_buffer.h | 10 +- include/wlr/types/wlr_compositor.h | 40 +++---- include/wlr/types/wlr_content_type_v1.h | 6 +- include/wlr/types/wlr_cursor_shape_v1.h | 6 +- include/wlr/types/wlr_damage_ring.h | 6 +- include/wlr/types/wlr_drm.h | 10 +- .../wlr_foreign_toplevel_management_v1.h | 8 +- include/wlr/types/wlr_fractional_scale_v1.h | 6 +- include/wlr/types/wlr_idle_notify_v1.h | 10 +- include/wlr/types/wlr_layer_shell_v1.h | 6 +- include/wlr/types/wlr_linux_dmabuf_v1.h | 24 ++-- include/wlr/types/wlr_linux_drm_syncobj_v1.h | 8 +- include/wlr/types/wlr_output_layer.h | 8 +- include/wlr/types/wlr_output_layout.h | 14 +-- .../wlr/types/wlr_output_swapchain_manager.h | 6 +- .../wlr/types/wlr_pointer_constraints_v1.h | 12 +- include/wlr/types/wlr_scene.h | 110 +++++++++--------- include/wlr/types/wlr_security_context_v1.h | 8 +- include/wlr/types/wlr_session_lock_v1.h | 20 ++-- include/wlr/types/wlr_shm.h | 10 +- .../wlr/types/wlr_single_pixel_buffer_v1.h | 6 +- include/wlr/types/wlr_subcompositor.h | 12 +- include/wlr/types/wlr_tearing_control_v1.h | 12 +- include/wlr/types/wlr_transient_seat_v1.h | 5 +- include/wlr/types/wlr_xdg_activation_v1.h | 25 ++-- include/wlr/types/wlr_xdg_decoration_v1.h | 12 +- include/wlr/types/wlr_xdg_shell.h | 20 ++-- include/wlr/util/addon.h | 13 ++- include/wlr/xwayland/shell.h | 24 ++-- include/wlr/xwayland/xwayland.h | 14 +-- meson.build | 1 + 35 files changed, 262 insertions(+), 249 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3d13f5fb1..f740c9359 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -237,6 +237,13 @@ used and `#undef` them after. * Document the contents and container of a `struct wl_list` with a `// content.link` and `// container.list` comment. +### Private fields + +Wrap private fields of public structures with `struct { … } WLR_PRIVATE`. This +ensures that compositor authors don't use them by accident. Within wlroots +`WLR_PRIVATE` is expanded to nothing, so private fields are accessed in the same +way as public ones. + ### Safety * Avoid string manipulation functions which don't take the size of the @@ -325,12 +332,14 @@ struct wlr_compositor { struct wl_global *global; … - struct wl_listener display_destroy; - struct { struct wl_signal new_surface; struct wl_signal destroy; } events; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; ``` diff --git a/include/wlr/render/drm_syncobj.h b/include/wlr/render/drm_syncobj.h index bf406d871..b847dbd25 100644 --- a/include/wlr/render/drm_syncobj.h +++ b/include/wlr/render/drm_syncobj.h @@ -29,9 +29,9 @@ struct wlr_drm_syncobj_timeline { int drm_fd; uint32_t handle; - // private state - - size_t n_refs; + struct { + size_t n_refs; + } WLR_PRIVATE; }; struct wlr_drm_syncobj_timeline_waiter { @@ -39,10 +39,10 @@ struct wlr_drm_syncobj_timeline_waiter { struct wl_signal ready; } events; - // private state - - int ev_fd; - struct wl_event_source *event_source; + struct { + int ev_fd; + struct wl_event_source *event_source; + } WLR_PRIVATE; }; /** diff --git a/include/wlr/render/wlr_renderer.h b/include/wlr/render/wlr_renderer.h index 4a8ccd398..debeb4e29 100644 --- a/include/wlr/render/wlr_renderer.h +++ b/include/wlr/render/wlr_renderer.h @@ -53,9 +53,9 @@ struct wlr_renderer { bool timeline; } features; - // private state - - const struct wlr_renderer_impl *impl; + struct { + const struct wlr_renderer_impl *impl; + } WLR_PRIVATE; }; /** diff --git a/include/wlr/types/wlr_alpha_modifier_v1.h b/include/wlr/types/wlr_alpha_modifier_v1.h index e400f4e56..f70191bb2 100644 --- a/include/wlr/types/wlr_alpha_modifier_v1.h +++ b/include/wlr/types/wlr_alpha_modifier_v1.h @@ -20,9 +20,9 @@ struct wlr_alpha_modifier_surface_v1_state { struct wlr_alpha_modifier_v1 { struct wl_global *global; - // private state - - struct wl_listener display_destroy; + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_alpha_modifier_v1 *wlr_alpha_modifier_v1_create(struct wl_display *display); diff --git a/include/wlr/types/wlr_buffer.h b/include/wlr/types/wlr_buffer.h index 9a655dd2b..874edf82b 100644 --- a/include/wlr/types/wlr_buffer.h +++ b/include/wlr/types/wlr_buffer.h @@ -148,12 +148,12 @@ struct wlr_client_buffer { */ struct wlr_buffer *source; - // private state + struct { + struct wl_listener source_destroy; + struct wl_listener renderer_destroy; - struct wl_listener source_destroy; - struct wl_listener renderer_destroy; - - size_t n_ignore_locks; + size_t n_ignore_locks; + } WLR_PRIVATE; }; /** diff --git a/include/wlr/types/wlr_compositor.h b/include/wlr/types/wlr_compositor.h index 6ba8d8a07..291e470c2 100644 --- a/include/wlr/types/wlr_compositor.h +++ b/include/wlr/types/wlr_compositor.h @@ -236,33 +236,33 @@ struct wlr_surface { struct wlr_addon_set addons; void *data; - // private state - - struct wl_listener role_resource_destroy; - struct { - int32_t scale; - enum wl_output_transform transform; - int width, height; - int buffer_width, buffer_height; - } previous; + struct wl_listener role_resource_destroy; - bool unmap_commit; + struct { + int32_t scale; + enum wl_output_transform transform; + int width, height; + int buffer_width, buffer_height; + } previous; - bool opaque; + bool unmap_commit; - bool handling_commit; - bool pending_rejected; + bool opaque; - int32_t preferred_buffer_scale; - bool preferred_buffer_transform_sent; - enum wl_output_transform preferred_buffer_transform; + bool handling_commit; + bool pending_rejected; - struct wl_list synced; // wlr_surface_synced.link - size_t synced_len; + int32_t preferred_buffer_scale; + bool preferred_buffer_transform_sent; + enum wl_output_transform preferred_buffer_transform; - struct wl_resource *pending_buffer_resource; - struct wl_listener pending_buffer_resource_destroy; + struct wl_list synced; // wlr_surface_synced.link + size_t synced_len; + + struct wl_resource *pending_buffer_resource; + struct wl_listener pending_buffer_resource_destroy; + } WLR_PRIVATE; }; struct wlr_renderer; diff --git a/include/wlr/types/wlr_content_type_v1.h b/include/wlr/types/wlr_content_type_v1.h index 6fa13d9be..31c51ac4c 100644 --- a/include/wlr/types/wlr_content_type_v1.h +++ b/include/wlr/types/wlr_content_type_v1.h @@ -23,9 +23,9 @@ struct wlr_content_type_manager_v1 { void *data; - // private state - - struct wl_listener display_destroy; + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_content_type_manager_v1 *wlr_content_type_manager_v1_create( diff --git a/include/wlr/types/wlr_cursor_shape_v1.h b/include/wlr/types/wlr_cursor_shape_v1.h index 55818c861..26048a52e 100644 --- a/include/wlr/types/wlr_cursor_shape_v1.h +++ b/include/wlr/types/wlr_cursor_shape_v1.h @@ -28,9 +28,9 @@ struct wlr_cursor_shape_manager_v1 { void *data; - // private state - - struct wl_listener display_destroy; + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; enum wlr_cursor_shape_manager_v1_device_type { diff --git a/include/wlr/types/wlr_damage_ring.h b/include/wlr/types/wlr_damage_ring.h index 17f5bfe89..feb64777c 100644 --- a/include/wlr/types/wlr_damage_ring.h +++ b/include/wlr/types/wlr_damage_ring.h @@ -30,9 +30,9 @@ struct wlr_damage_ring { // Difference between the current buffer and the previous one pixman_region32_t current; - // private state - - struct wl_list buffers; // wlr_damage_ring_buffer.link + struct { + struct wl_list buffers; // wlr_damage_ring_buffer.link + } WLR_PRIVATE; }; void wlr_damage_ring_init(struct wlr_damage_ring *ring); diff --git a/include/wlr/types/wlr_drm.h b/include/wlr/types/wlr_drm.h index b2a5ce5a1..a256a8deb 100644 --- a/include/wlr/types/wlr_drm.h +++ b/include/wlr/types/wlr_drm.h @@ -40,12 +40,12 @@ struct wlr_drm { struct wl_signal destroy; } events; - // private state + struct { + char *node_name; + struct wlr_drm_format_set formats; - char *node_name; - struct wlr_drm_format_set formats; - - struct wl_listener display_destroy; + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_drm_buffer *wlr_drm_buffer_try_from_resource( diff --git a/include/wlr/types/wlr_foreign_toplevel_management_v1.h b/include/wlr/types/wlr_foreign_toplevel_management_v1.h index c4abcb3dd..499d40380 100644 --- a/include/wlr/types/wlr_foreign_toplevel_management_v1.h +++ b/include/wlr/types/wlr_foreign_toplevel_management_v1.h @@ -39,10 +39,10 @@ struct wlr_foreign_toplevel_handle_v1_output { struct wlr_output *output; struct wlr_foreign_toplevel_handle_v1 *toplevel; - // private state - - struct wl_listener output_bind; - struct wl_listener output_destroy; + struct { + struct wl_listener output_bind; + struct wl_listener output_destroy; + } WLR_PRIVATE; }; struct wlr_foreign_toplevel_handle_v1 { diff --git a/include/wlr/types/wlr_fractional_scale_v1.h b/include/wlr/types/wlr_fractional_scale_v1.h index 9126360d9..09214eb83 100644 --- a/include/wlr/types/wlr_fractional_scale_v1.h +++ b/include/wlr/types/wlr_fractional_scale_v1.h @@ -20,9 +20,9 @@ struct wlr_fractional_scale_manager_v1 { struct wl_signal destroy; } events; - // private state - - struct wl_listener display_destroy; + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; void wlr_fractional_scale_v1_notify_scale( diff --git a/include/wlr/types/wlr_idle_notify_v1.h b/include/wlr/types/wlr_idle_notify_v1.h index 8dea08045..508c0cb77 100644 --- a/include/wlr/types/wlr_idle_notify_v1.h +++ b/include/wlr/types/wlr_idle_notify_v1.h @@ -19,12 +19,12 @@ struct wlr_seat; struct wlr_idle_notifier_v1 { struct wl_global *global; - // private state + struct { + bool inhibited; + struct wl_list notifications; // wlr_idle_notification_v1.link - bool inhibited; - struct wl_list notifications; // wlr_idle_notification_v1.link - - struct wl_listener display_destroy; + struct wl_listener display_destroy; + } WLR_PRIVATE; }; diff --git a/include/wlr/types/wlr_layer_shell_v1.h b/include/wlr/types/wlr_layer_shell_v1.h index 9be246e16..d46f9b9c9 100644 --- a/include/wlr/types/wlr_layer_shell_v1.h +++ b/include/wlr/types/wlr_layer_shell_v1.h @@ -114,9 +114,9 @@ struct wlr_layer_surface_v1 { void *data; - // private state - - struct wlr_surface_synced synced; + struct { + struct wlr_surface_synced synced; + } WLR_PRIVATE; }; struct wlr_layer_shell_v1 *wlr_layer_shell_v1_create(struct wl_display *display, diff --git a/include/wlr/types/wlr_linux_dmabuf_v1.h b/include/wlr/types/wlr_linux_dmabuf_v1.h index cf967f952..2193f9141 100644 --- a/include/wlr/types/wlr_linux_dmabuf_v1.h +++ b/include/wlr/types/wlr_linux_dmabuf_v1.h @@ -24,9 +24,9 @@ struct wlr_dmabuf_v1_buffer { struct wl_resource *resource; // can be NULL if the client destroyed it struct wlr_dmabuf_attributes attributes; - // private state - - struct wl_listener release; + struct { + struct wl_listener release; + } WLR_PRIVATE; }; /** @@ -55,18 +55,18 @@ struct wlr_linux_dmabuf_v1 { struct wl_signal destroy; } events; - // private state + struct { + struct wlr_linux_dmabuf_feedback_v1_compiled *default_feedback; + struct wlr_drm_format_set default_formats; // for legacy clients + struct wl_list surfaces; // wlr_linux_dmabuf_v1_surface.link - struct wlr_linux_dmabuf_feedback_v1_compiled *default_feedback; - struct wlr_drm_format_set default_formats; // for legacy clients - struct wl_list surfaces; // wlr_linux_dmabuf_v1_surface.link + int main_device_fd; // to sanity check FDs sent by clients, -1 if unavailable - int main_device_fd; // to sanity check FDs sent by clients, -1 if unavailable + struct wl_listener display_destroy; - struct wl_listener display_destroy; - - bool (*check_dmabuf_callback)(struct wlr_dmabuf_attributes *attribs, void *data); - void *check_dmabuf_callback_data; + bool (*check_dmabuf_callback)(struct wlr_dmabuf_attributes *attribs, void *data); + void *check_dmabuf_callback_data; + } WLR_PRIVATE; }; /** diff --git a/include/wlr/types/wlr_linux_drm_syncobj_v1.h b/include/wlr/types/wlr_linux_drm_syncobj_v1.h index 02cdb42a8..733350412 100644 --- a/include/wlr/types/wlr_linux_drm_syncobj_v1.h +++ b/include/wlr/types/wlr_linux_drm_syncobj_v1.h @@ -26,11 +26,11 @@ struct wlr_linux_drm_syncobj_surface_v1_state { struct wlr_linux_drm_syncobj_manager_v1 { struct wl_global *global; - // private state + struct { + int drm_fd; - int drm_fd; - - struct wl_listener display_destroy; + struct wl_listener display_destroy; + } WLR_PRIVATE; }; /** diff --git a/include/wlr/types/wlr_output_layer.h b/include/wlr/types/wlr_output_layer.h index 461e13ddc..af59ab8dc 100644 --- a/include/wlr/types/wlr_output_layer.h +++ b/include/wlr/types/wlr_output_layer.h @@ -50,10 +50,10 @@ struct wlr_output_layer { void *data; - // private state - - struct wlr_fbox src_box; - struct wlr_box dst_box; + struct { + struct wlr_fbox src_box; + struct wlr_box dst_box; + } WLR_PRIVATE; }; /** diff --git a/include/wlr/types/wlr_output_layout.h b/include/wlr/types/wlr_output_layout.h index 40c7fb93c..f2cbc6374 100644 --- a/include/wlr/types/wlr_output_layout.h +++ b/include/wlr/types/wlr_output_layout.h @@ -36,9 +36,9 @@ struct wlr_output_layout { void *data; - // private state - - struct wl_listener display_destroy; + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_output_layout_output { @@ -55,11 +55,11 @@ struct wlr_output_layout_output { struct wl_signal destroy; } events; - // private state + struct { + struct wlr_addon addon; - struct wlr_addon addon; - - struct wl_listener commit; + struct wl_listener commit; + } WLR_PRIVATE; }; struct wlr_output_layout *wlr_output_layout_create(struct wl_display *display); diff --git a/include/wlr/types/wlr_output_swapchain_manager.h b/include/wlr/types/wlr_output_swapchain_manager.h index 24a5578e5..3ea9c49b9 100644 --- a/include/wlr/types/wlr_output_swapchain_manager.h +++ b/include/wlr/types/wlr_output_swapchain_manager.h @@ -30,9 +30,9 @@ struct wlr_backend_output_state; struct wlr_output_swapchain_manager { struct wlr_backend *backend; - // private state - - struct wl_array outputs; // struct wlr_output_swapchain_manager_output + struct { + struct wl_array outputs; // struct wlr_output_swapchain_manager_output + } WLR_PRIVATE; }; /** diff --git a/include/wlr/types/wlr_pointer_constraints_v1.h b/include/wlr/types/wlr_pointer_constraints_v1.h index 2b4722f7d..75dddb818 100644 --- a/include/wlr/types/wlr_pointer_constraints_v1.h +++ b/include/wlr/types/wlr_pointer_constraints_v1.h @@ -64,13 +64,13 @@ struct wlr_pointer_constraint_v1 { void *data; - // private state + struct { + struct wl_listener surface_commit; + struct wl_listener surface_destroy; + struct wl_listener seat_destroy; - struct wl_listener surface_commit; - struct wl_listener surface_destroy; - struct wl_listener seat_destroy; - - struct wlr_surface_synced synced; + struct wlr_surface_synced synced; + } WLR_PRIVATE; }; struct wlr_pointer_constraints_v1 { diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index bcfbc72f9..dbca35e78 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -75,9 +75,9 @@ struct wlr_scene_node { struct wlr_addon_set addons; - // private state - - pixman_region32_t visible; + struct { + pixman_region32_t visible; + } WLR_PRIVATE; }; enum wlr_scene_debug_damage_option { @@ -103,16 +103,16 @@ struct wlr_scene { struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1; struct wlr_gamma_control_manager_v1 *gamma_control_manager_v1; - // private state + struct { + struct wl_listener linux_dmabuf_v1_destroy; + struct wl_listener gamma_control_manager_v1_destroy; + struct wl_listener gamma_control_manager_v1_set_gamma; - struct wl_listener linux_dmabuf_v1_destroy; - struct wl_listener gamma_control_manager_v1_destroy; - struct wl_listener gamma_control_manager_v1_set_gamma; - - enum wlr_scene_debug_damage_option debug_damage_option; - bool direct_scanout; - bool calculate_visibility; - bool highlight_transparent_region; + enum wlr_scene_debug_damage_option debug_damage_option; + bool direct_scanout; + bool calculate_visibility; + bool highlight_transparent_region; + } WLR_PRIVATE; }; /** A scene-graph node displaying a single surface. */ @@ -120,19 +120,19 @@ struct wlr_scene_surface { struct wlr_scene_buffer *buffer; struct wlr_surface *surface; - // private state + struct { + struct wlr_box clip; - struct wlr_box clip; + struct wlr_addon addon; - struct wlr_addon addon; - - struct wl_listener outputs_update; - struct wl_listener output_enter; - struct wl_listener output_leave; - struct wl_listener output_sample; - struct wl_listener frame_done; - struct wl_listener surface_destroy; - struct wl_listener surface_commit; + struct wl_listener outputs_update; + struct wl_listener output_enter; + struct wl_listener output_leave; + struct wl_listener output_sample; + struct wl_listener frame_done; + struct wl_listener surface_destroy; + struct wl_listener surface_commit; + } WLR_PRIVATE; }; /** A scene-graph node displaying a solid-colored rectangle */ @@ -185,21 +185,21 @@ struct wlr_scene_buffer { enum wl_output_transform transform; pixman_region32_t opaque_region; - // private state + struct { + uint64_t active_outputs; + struct wlr_texture *texture; + struct wlr_linux_dmabuf_feedback_v1_init_options prev_feedback_options; - uint64_t active_outputs; - struct wlr_texture *texture; - struct wlr_linux_dmabuf_feedback_v1_init_options prev_feedback_options; + bool own_buffer; + int buffer_width, buffer_height; + bool buffer_is_opaque; - bool own_buffer; - int buffer_width, buffer_height; - bool buffer_is_opaque; + struct wlr_drm_syncobj_timeline *wait_timeline; + uint64_t wait_point; - struct wlr_drm_syncobj_timeline *wait_timeline; - uint64_t wait_point; - - struct wl_listener buffer_release; - struct wl_listener renderer_destroy; + struct wl_listener buffer_release; + struct wl_listener renderer_destroy; + } WLR_PRIVATE; }; /** A viewport for an output in the scene-graph */ @@ -217,26 +217,26 @@ struct wlr_scene_output { struct wl_signal destroy; } events; - // private state + struct { + pixman_region32_t pending_commit_damage; - pixman_region32_t pending_commit_damage; + uint8_t index; + bool prev_scanout; - uint8_t index; - bool prev_scanout; + bool gamma_lut_changed; + struct wlr_gamma_control_v1 *gamma_lut; - bool gamma_lut_changed; - struct wlr_gamma_control_v1 *gamma_lut; + struct wl_listener output_commit; + struct wl_listener output_damage; + struct wl_listener output_needs_frame; - struct wl_listener output_commit; - struct wl_listener output_damage; - struct wl_listener output_needs_frame; + struct wl_list damage_highlight_regions; - struct wl_list damage_highlight_regions; + struct wl_array render_list; - struct wl_array render_list; - - struct wlr_drm_syncobj_timeline *in_timeline; - uint64_t in_point; + struct wlr_drm_syncobj_timeline *in_timeline; + uint64_t in_point; + } WLR_PRIVATE; }; struct wlr_scene_timer { @@ -249,12 +249,12 @@ struct wlr_scene_layer_surface_v1 { struct wlr_scene_tree *tree; struct wlr_layer_surface_v1 *layer_surface; - // private state - - struct wl_listener tree_destroy; - struct wl_listener layer_surface_destroy; - struct wl_listener layer_surface_map; - struct wl_listener layer_surface_unmap; + struct { + struct wl_listener tree_destroy; + struct wl_listener layer_surface_destroy; + struct wl_listener layer_surface_map; + struct wl_listener layer_surface_unmap; + } WLR_PRIVATE; }; /** diff --git a/include/wlr/types/wlr_security_context_v1.h b/include/wlr/types/wlr_security_context_v1.h index f932a2b1e..40bb7a29c 100644 --- a/include/wlr/types/wlr_security_context_v1.h +++ b/include/wlr/types/wlr_security_context_v1.h @@ -28,11 +28,11 @@ struct wlr_security_context_manager_v1 { void *data; - // private state + struct { + struct wl_list contexts; // wlr_security_context_v1.link - struct wl_list contexts; // wlr_security_context_v1.link - - struct wl_listener display_destroy; + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_security_context_v1_state { diff --git a/include/wlr/types/wlr_session_lock_v1.h b/include/wlr/types/wlr_session_lock_v1.h index 058bf0d74..2e9d80a9d 100644 --- a/include/wlr/types/wlr_session_lock_v1.h +++ b/include/wlr/types/wlr_session_lock_v1.h @@ -24,9 +24,9 @@ struct wlr_session_lock_manager_v1 { void *data; - // private state - - struct wl_listener display_destroy; + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_session_lock_v1 { @@ -42,9 +42,9 @@ struct wlr_session_lock_v1 { void *data; - // private state - - bool locked_sent; + struct { + bool locked_sent; + } WLR_PRIVATE; }; struct wlr_session_lock_surface_v1_state { @@ -79,11 +79,11 @@ struct wlr_session_lock_surface_v1 { void *data; - // private state + struct { + struct wlr_surface_synced synced; - struct wlr_surface_synced synced; - - struct wl_listener output_destroy; + struct wl_listener output_destroy; + } WLR_PRIVATE; }; struct wlr_session_lock_manager_v1 *wlr_session_lock_manager_v1_create( diff --git a/include/wlr/types/wlr_shm.h b/include/wlr/types/wlr_shm.h index 13a5ca5cd..650b7ffd0 100644 --- a/include/wlr/types/wlr_shm.h +++ b/include/wlr/types/wlr_shm.h @@ -25,12 +25,12 @@ struct wlr_renderer; struct wlr_shm { struct wl_global *global; - // private state + struct { + uint32_t *formats; + size_t formats_len; - uint32_t *formats; - size_t formats_len; - - struct wl_listener display_destroy; + struct wl_listener display_destroy; + } WLR_PRIVATE; }; /** diff --git a/include/wlr/types/wlr_single_pixel_buffer_v1.h b/include/wlr/types/wlr_single_pixel_buffer_v1.h index 3836203f8..64018a864 100644 --- a/include/wlr/types/wlr_single_pixel_buffer_v1.h +++ b/include/wlr/types/wlr_single_pixel_buffer_v1.h @@ -14,9 +14,9 @@ struct wlr_single_pixel_buffer_manager_v1 { struct wl_global *global; - // private state - - struct wl_listener display_destroy; + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; diff --git a/include/wlr/types/wlr_subcompositor.h b/include/wlr/types/wlr_subcompositor.h index 61a2cccc9..26be02959 100644 --- a/include/wlr/types/wlr_subcompositor.h +++ b/include/wlr/types/wlr_subcompositor.h @@ -23,9 +23,9 @@ struct wlr_subsurface_parent_state { int32_t x, y; struct wl_list link; - // private state - - struct wlr_surface_synced *synced; + struct { + struct wlr_surface_synced *synced; + } WLR_PRIVATE; }; struct wlr_subsurface { @@ -50,9 +50,9 @@ struct wlr_subsurface { void *data; - // private state - - struct wlr_surface_synced parent_synced; + struct { + struct wlr_surface_synced parent_synced; + } WLR_PRIVATE; }; struct wlr_subcompositor { diff --git a/include/wlr/types/wlr_tearing_control_v1.h b/include/wlr/types/wlr_tearing_control_v1.h index 76c5c6003..ddcb22bcc 100644 --- a/include/wlr/types/wlr_tearing_control_v1.h +++ b/include/wlr/types/wlr_tearing_control_v1.h @@ -30,13 +30,13 @@ struct wlr_tearing_control_v1 { struct wlr_surface *surface; - // private state + struct { + enum wp_tearing_control_v1_presentation_hint previous; + struct wlr_addon addon; + struct wlr_surface_synced synced; - enum wp_tearing_control_v1_presentation_hint previous; - struct wlr_addon addon; - struct wlr_surface_synced synced; - - struct wl_listener surface_commit; + struct wl_listener surface_commit; + } WLR_PRIVATE; }; struct wlr_tearing_control_manager_v1 { diff --git a/include/wlr/types/wlr_transient_seat_v1.h b/include/wlr/types/wlr_transient_seat_v1.h index 84d419bc1..b0d329fd5 100644 --- a/include/wlr/types/wlr_transient_seat_v1.h +++ b/include/wlr/types/wlr_transient_seat_v1.h @@ -17,8 +17,9 @@ struct wlr_transient_seat_v1 { struct wl_resource *resource; struct wlr_seat *seat; - // private state - struct wl_listener seat_destroy; + struct { + struct wl_listener seat_destroy; + } WLR_PRIVATE; }; struct wlr_transient_seat_manager_v1 { diff --git a/include/wlr/types/wlr_xdg_activation_v1.h b/include/wlr/types/wlr_xdg_activation_v1.h index f7b038b8c..6c1d5b8ad 100644 --- a/include/wlr/types/wlr_xdg_activation_v1.h +++ b/include/wlr/types/wlr_xdg_activation_v1.h @@ -28,14 +28,14 @@ struct wlr_xdg_activation_token_v1 { struct wl_signal destroy; } events; - // private state + struct { + char *token; + struct wl_resource *resource; // can be NULL + struct wl_event_source *timeout; // can be NULL - char *token; - struct wl_resource *resource; // can be NULL - struct wl_event_source *timeout; // can be NULL - - struct wl_listener seat_destroy; - struct wl_listener surface_destroy; + struct wl_listener seat_destroy; + struct wl_listener surface_destroy; + } WLR_PRIVATE; }; struct wlr_xdg_activation_v1 { @@ -49,13 +49,12 @@ struct wlr_xdg_activation_v1 { struct wl_signal new_token; // struct wlr_xdg_activation_token_v1 } events; - // private state + struct { + struct wl_display *display; + struct wl_global *global; - struct wl_display *display; - - struct wl_global *global; - - struct wl_listener display_destroy; + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_xdg_activation_v1_request_activate_event { diff --git a/include/wlr/types/wlr_xdg_decoration_v1.h b/include/wlr/types/wlr_xdg_decoration_v1.h index f1a8bc5b3..24928e915 100644 --- a/include/wlr/types/wlr_xdg_decoration_v1.h +++ b/include/wlr/types/wlr_xdg_decoration_v1.h @@ -54,13 +54,13 @@ struct wlr_xdg_toplevel_decoration_v1 { void *data; - // private state + struct { + struct wl_listener toplevel_destroy; + struct wl_listener surface_configure; + struct wl_listener surface_ack_configure; - struct wl_listener toplevel_destroy; - struct wl_listener surface_configure; - struct wl_listener surface_ack_configure; - - struct wlr_surface_synced synced; + struct wlr_surface_synced synced; + } WLR_PRIVATE; }; struct wlr_xdg_decoration_manager_v1 * diff --git a/include/wlr/types/wlr_xdg_shell.h b/include/wlr/types/wlr_xdg_shell.h index 900d504c0..723f8adf6 100644 --- a/include/wlr/types/wlr_xdg_shell.h +++ b/include/wlr/types/wlr_xdg_shell.h @@ -110,9 +110,9 @@ struct wlr_xdg_popup { struct wl_list grab_link; // wlr_xdg_popup_grab.popups - // private state - - struct wlr_surface_synced synced; + struct { + struct wlr_surface_synced synced; + } WLR_PRIVATE; }; // each seat gets a popup grab @@ -221,9 +221,9 @@ struct wlr_xdg_toplevel { struct wl_signal set_app_id; } events; - // private state - - struct wlr_surface_synced synced; + struct { + struct wlr_surface_synced synced; + } WLR_PRIVATE; }; struct wlr_xdg_surface_configure { @@ -304,11 +304,11 @@ struct wlr_xdg_surface { void *data; - // private state + struct { + struct wlr_surface_synced synced; - struct wlr_surface_synced synced; - - struct wl_listener role_resource_destroy; + struct wl_listener role_resource_destroy; + } WLR_PRIVATE; }; struct wlr_xdg_toplevel_move_event { diff --git a/include/wlr/util/addon.h b/include/wlr/util/addon.h index c64200cf9..8819acce2 100644 --- a/include/wlr/util/addon.h +++ b/include/wlr/util/addon.h @@ -12,8 +12,9 @@ #include struct wlr_addon_set { - // private state - struct wl_list addons; + struct { + struct wl_list addons; + } WLR_PRIVATE; }; struct wlr_addon; @@ -26,9 +27,11 @@ struct wlr_addon_interface { struct wlr_addon { const struct wlr_addon_interface *impl; - // private state - const void *owner; - struct wl_list link; + + struct { + const void *owner; + struct wl_list link; + } WLR_PRIVATE; }; void wlr_addon_set_init(struct wlr_addon_set *set); diff --git a/include/wlr/xwayland/shell.h b/include/wlr/xwayland/shell.h index da5cb9907..344ac2201 100644 --- a/include/wlr/xwayland/shell.h +++ b/include/wlr/xwayland/shell.h @@ -25,13 +25,13 @@ struct wlr_xwayland_shell_v1 { struct wl_signal new_surface; // struct wlr_xwayland_surface_v1 } events; - // private state + struct { + struct wl_client *client; + struct wl_list surfaces; // wlr_xwayland_surface_v1.link - struct wl_client *client; - struct wl_list surfaces; // wlr_xwayland_surface_v1.link - - struct wl_listener display_destroy; - struct wl_listener client_destroy; + struct wl_listener display_destroy; + struct wl_listener client_destroy; + } WLR_PRIVATE; }; /** @@ -41,12 +41,12 @@ struct wlr_xwayland_surface_v1 { struct wlr_surface *surface; uint64_t serial; - // private state - - struct wl_resource *resource; - struct wl_list link; - struct wlr_xwayland_shell_v1 *shell; - bool added; + struct { + struct wl_resource *resource; + struct wl_list link; + struct wlr_xwayland_shell_v1 *shell; + bool added; + } WLR_PRIVATE; }; /** diff --git a/include/wlr/xwayland/xwayland.h b/include/wlr/xwayland/xwayland.h index c10ce4cd5..339aef0ec 100644 --- a/include/wlr/xwayland/xwayland.h +++ b/include/wlr/xwayland/xwayland.h @@ -65,13 +65,13 @@ struct wlr_xwayland { void *data; - // private state - - struct wl_listener server_start; - struct wl_listener server_ready; - struct wl_listener server_destroy; - struct wl_listener seat_destroy; - struct wl_listener shell_destroy; + struct { + struct wl_listener server_start; + struct wl_listener server_ready; + struct wl_listener server_destroy; + struct wl_listener seat_destroy; + struct wl_listener shell_destroy; + } WLR_PRIVATE; }; enum wlr_xwayland_surface_decorations { diff --git a/meson.build b/meson.build index fcc562c87..caee73a46 100644 --- a/meson.build +++ b/meson.build @@ -22,6 +22,7 @@ big_endian = target_machine.endian() == 'big' add_project_arguments([ '-D_POSIX_C_SOURCE=200809L', '-DWLR_USE_UNSTABLE', + '-DWLR_PRIVATE=', '-DWLR_LITTLE_ENDIAN=@0@'.format(little_endian.to_int()), '-DWLR_BIG_ENDIAN=@0@'.format(big_endian.to_int()), ], language: 'c') From e8e76dc2954e1bc4cfe6bbf104996a932b9eb807 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 26 Oct 2024 13:04:55 +0200 Subject: [PATCH 151/519] backend/drm: check whether clipped damage is empty We were checking whether the damage region was empty before clipping. However a non-empty damage region can become empty after clipping. Instead, check whether the clipped region is empty. Fixes: 4339c37f99aa ("backend/drm: clip FB damage") --- backend/drm/atomic.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index 1844d024f..a3297bda5 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -156,18 +156,20 @@ static bool create_gamma_lut_blob(struct wlr_drm_backend *drm, bool create_fb_damage_clips_blob(struct wlr_drm_backend *drm, int width, int height, const pixman_region32_t *damage, uint32_t *blob_id) { - if (!pixman_region32_not_empty(damage)) { - *blob_id = 0; - return true; - } - pixman_region32_t clipped; pixman_region32_init(&clipped); pixman_region32_intersect_rect(&clipped, damage, 0, 0, width, height); int rects_len; const pixman_box32_t *rects = pixman_region32_rectangles(&clipped, &rects_len); - int ret = drmModeCreatePropertyBlob(drm->fd, rects, sizeof(*rects) * rects_len, blob_id); + + int ret; + if (rects_len > 0) { + ret = drmModeCreatePropertyBlob(drm->fd, rects, sizeof(*rects) * rects_len, blob_id); + } else { + ret = 0; + *blob_id = 0; + } pixman_region32_fini(&clipped); if (ret != 0) { wlr_log_errno(WLR_ERROR, "Failed to create FB_DAMAGE_CLIPS property blob"); From 4c74a8843a08a39d6278de4a26759d93155feb2c Mon Sep 17 00:00:00 2001 From: llyyr Date: Tue, 15 Oct 2024 22:10:49 +0530 Subject: [PATCH 152/519] presentation-time: bump protocol version to v2 We've actually been doing the wrong thing this whole time, for v1 of the protocol, we should set the refresh_nsec field to 0 if the output does not have a constant refresh rate. However we've been setting it to the fastest rate instead since eac7c2ad2faf49084342d9f805cf06b773723fdd which is incidentally exactly what v2 of the protocol proposes. So allow advertising v2, and fix v1 to set refresh_nsec to 0. --- include/wlr/types/wlr_presentation_time.h | 2 +- protocol/meson.build | 2 +- types/wlr_presentation_time.c | 13 ++++++++++--- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/include/wlr/types/wlr_presentation_time.h b/include/wlr/types/wlr_presentation_time.h index bedd77a6c..717665e68 100644 --- a/include/wlr/types/wlr_presentation_time.h +++ b/include/wlr/types/wlr_presentation_time.h @@ -56,7 +56,7 @@ struct wlr_presentation_event { struct wlr_backend; struct wlr_presentation *wlr_presentation_create(struct wl_display *display, - struct wlr_backend *backend); + struct wlr_backend *backend, int version); /** * Mark the current surface's buffer as sampled. * diff --git a/protocol/meson.build b/protocol/meson.build index a4476918b..3e385d4ec 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -1,5 +1,5 @@ wayland_protos = dependency('wayland-protocols', - version: '>=1.35', + version: '>=1.38', fallback: 'wayland-protocols', default_options: ['tests=false'], ) diff --git a/types/wlr_presentation_time.c b/types/wlr_presentation_time.c index 94e379a54..0e5e88ea6 100644 --- a/types/wlr_presentation_time.c +++ b/types/wlr_presentation_time.c @@ -7,7 +7,7 @@ #include #include "presentation-time-protocol.h" -#define PRESENTATION_VERSION 1 +#define PRESENTATION_VERSION 2 struct wlr_presentation_surface_state { struct wlr_presentation_feedback *feedback; @@ -172,14 +172,16 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { } struct wlr_presentation *wlr_presentation_create(struct wl_display *display, - struct wlr_backend *backend) { + struct wlr_backend *backend, int version) { + assert(version <= PRESENTATION_VERSION); + struct wlr_presentation *presentation = calloc(1, sizeof(*presentation)); if (presentation == NULL) { return NULL; } presentation->global = wl_global_create(display, &wp_presentation_interface, - PRESENTATION_VERSION, NULL, presentation_bind); + version, NULL, presentation_bind); if (presentation->global == NULL) { free(presentation); return NULL; @@ -283,6 +285,11 @@ static void feedback_handle_output_present(struct wl_listener *listener, if (output_event->presented) { struct wlr_presentation_event event = {0}; wlr_presentation_event_from_output(&event, output_event); + struct wl_resource *resource = wl_resource_from_link(feedback->resources.next); + if (wl_resource_get_version(resource) == 1 && + event.output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED) { + event.refresh = 0; + } if (!feedback->zero_copy) { event.flags &= ~WP_PRESENTATION_FEEDBACK_KIND_ZERO_COPY; } From 7717c92ed04ab0310a58391cb8955063661cbd54 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 28 Oct 2024 02:05:49 +0100 Subject: [PATCH 153/519] backend/drm: Skip plane props if buffer is not committed If our session is re-activated during scanout, restore_drm_device will reset planes and then attempt an enabling modeset commit without a buffer. The new plane transform logic requires a committed buffer to be present to calculate the boxes if they were not explicitly provided, and at least amdgpu rejects commits that try to use 0 as default. Skip updating plane props instead of segfaulting if no buffer is set. A better fix would be to not rely on restore_drm_device at all and instead require compositors to modeset in response to session activation. Fixes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3912 --- backend/drm/atomic.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index a3297bda5..b52230f90 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -439,14 +439,15 @@ static void atomic_connector_add(struct atomic *atom, if (crtc->props.vrr_enabled != 0) { atomic_add(atom, crtc->id, crtc->props.vrr_enabled, state->vrr_enabled); } + if (state->base->committed & WLR_OUTPUT_STATE_BUFFER) { + struct wlr_fbox src_box; + struct wlr_box dst_box; + output_state_get_buffer_src_box(state->base, &src_box); + output_state_get_buffer_dst_box(state->base, &dst_box); - struct wlr_fbox src_box; - struct wlr_box dst_box; - output_state_get_buffer_src_box(state->base, &src_box); - output_state_get_buffer_dst_box(state->base, &dst_box); - - set_plane_props(atom, drm, crtc->primary, state->primary_fb, crtc->id, - &dst_box, &src_box); + set_plane_props(atom, drm, crtc->primary, state->primary_fb, crtc->id, + &dst_box, &src_box); + } if (crtc->primary->props.fb_damage_clips != 0) { atomic_add(atom, crtc->primary->id, crtc->primary->props.fb_damage_clips, state->fb_damage_clips); From cf43a447cbea8958928813e0cc6e5d6348e0afea Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 24 Oct 2024 18:32:44 +0100 Subject: [PATCH 154/519] scene: Transform coordinates for direct scanout We support direct scanout when there is an output and buffer transform so long as the transforms are the same (so cancel out for the buffer contents). But we still need to apply the output transform to the destination box location and size. --- types/scene/wlr_scene.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index e4d68654a..766189485 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1865,10 +1865,12 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry, pending.buffer_src_box = buffer->src_box; } - // Translate the location from scene coordinates to output coordinates + // Translate the position from scene coordinates to output coordinates pending.buffer_dst_box.x = entry->x - scene_output->x; pending.buffer_dst_box.y = entry->y - scene_output->y; + scene_node_get_size(node, &pending.buffer_dst_box.width, &pending.buffer_dst_box.height); + transform_output_box(&pending.buffer_dst_box, data); wlr_output_state_set_buffer(&pending, buffer->buffer); if (buffer->wait_timeline != NULL) { From 9351c78d7091c189149c3a7102e6a2183bfd21fb Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 5 Aug 2024 20:15:20 +0200 Subject: [PATCH 155/519] render/vulkan: add render_pass_destroy() De-duplicate the cleanup logic. --- render/vulkan/pass.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 8e1807dfa..c5487cb34 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -79,6 +79,12 @@ static void mat3_to_mat4(const float mat3[9], float mat4[4][4]) { mat4[3][3] = 1.f; } +static void render_pass_destroy(struct wlr_vk_render_pass *pass) { + wlr_color_transform_unref(pass->color_transform); + rect_union_finish(&pass->updated_region); + free(pass); +} + static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { struct wlr_vk_render_pass *pass = get_render_pass(wlr_pass); struct wlr_vk_renderer *renderer = pass->renderer; @@ -450,10 +456,8 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { wlr_log(WLR_ERROR, "Failed to sync render buffer"); } - wlr_color_transform_unref(pass->color_transform); + render_pass_destroy(pass); wlr_buffer_unlock(render_buffer->wlr_buffer); - rect_union_finish(&pass->updated_region); - free(pass); return true; error: @@ -461,8 +465,7 @@ error: vulkan_reset_command_buffer(stage_cb); vulkan_reset_command_buffer(render_cb); wlr_buffer_unlock(render_buffer->wlr_buffer); - rect_union_finish(&pass->updated_region); - free(pass); + render_pass_destroy(pass); if (device_lost) { wl_signal_emit_mutable(&renderer->wlr_renderer.events.lost, NULL); From 3bbfae73ae0512b6135f7e1208c10cda54bae005 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 5 Aug 2024 20:51:15 +0200 Subject: [PATCH 156/519] render/vulkan: add support for explicit sync --- include/render/vulkan.h | 23 ++++-- render/vulkan/pass.c | 149 ++++++++++++++++++++++++++++++++++----- render/vulkan/renderer.c | 82 ++++++++++----------- render/vulkan/texture.c | 6 -- render/vulkan/vulkan.c | 1 + 5 files changed, 190 insertions(+), 71 deletions(-) diff --git a/include/render/vulkan.h b/include/render/vulkan.h index 29403f01f..7ad2f8339 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -40,6 +40,7 @@ struct wlr_vk_device { int drm_fd; + bool sync_file_import_export; bool implicit_sync_interop; bool sampler_ycbcr_conversion; @@ -253,6 +254,8 @@ struct wlr_vk_command_buffer { // For DMA-BUF implicit sync interop, may be NULL VkSemaphore binary_semaphore; + + struct wl_array wait_semaphores; // VkSemaphore }; #define VULKAN_COMMAND_BUFFERS_CAP 64 @@ -367,6 +370,13 @@ VkCommandBuffer vulkan_record_stage_cb(struct wlr_vk_renderer *renderer); // finished execution. bool vulkan_submit_stage_wait(struct wlr_vk_renderer *renderer); +struct wlr_vk_render_pass_texture { + struct wlr_vk_texture *texture; + + struct wlr_drm_syncobj_timeline *wait_timeline; + uint64_t wait_point; +}; + struct wlr_vk_render_pass { struct wlr_render_pass base; struct wlr_vk_renderer *renderer; @@ -378,6 +388,11 @@ struct wlr_vk_render_pass { bool failed; bool srgb_pathway; // if false, rendering via intermediate blending buffer struct wlr_color_transform *color_transform; + + struct wlr_drm_syncobj_timeline *signal_timeline; + uint64_t signal_point; + + struct wl_array textures; // struct wlr_vk_render_pass_texture }; struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *renderer, @@ -419,8 +434,10 @@ bool vulkan_wait_command_buffer(struct wlr_vk_command_buffer *cb, struct wlr_vk_renderer *renderer); bool vulkan_sync_render_buffer(struct wlr_vk_renderer *renderer, - struct wlr_vk_render_buffer *render_buffer, struct wlr_vk_command_buffer *cb); -bool vulkan_sync_foreign_texture(struct wlr_vk_texture *texture); + struct wlr_vk_render_buffer *render_buffer, struct wlr_vk_command_buffer *cb, + struct wlr_drm_syncobj_timeline *signal_timeline, uint64_t signal_point); +bool vulkan_sync_foreign_texture(struct wlr_vk_texture *texture, + int sync_file_fds[static WLR_DMABUF_MAX_PLANES]); bool vulkan_read_pixels(struct wlr_vk_renderer *vk_renderer, VkFormat src_format, VkImage src_image, @@ -450,8 +467,6 @@ struct wlr_vk_texture { // If imported from a wlr_buffer struct wlr_buffer *buffer; struct wlr_addon buffer_addon; - // For DMA-BUF implicit sync interop - VkSemaphore foreign_semaphores[WLR_DMABUF_MAX_PLANES]; struct wl_list views; // struct wlr_vk_texture_ds.link }; diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index c5487cb34..8be1cc5d9 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -1,8 +1,10 @@ #include #include #include +#include #include #include +#include #include "render/color.h" #include "render/vulkan.h" @@ -80,11 +82,65 @@ static void mat3_to_mat4(const float mat3[9], float mat4[4][4]) { } static void render_pass_destroy(struct wlr_vk_render_pass *pass) { + struct wlr_vk_render_pass_texture *pass_texture; + wl_array_for_each(pass_texture, &pass->textures) { + wlr_drm_syncobj_timeline_unref(pass_texture->wait_timeline); + } + wlr_color_transform_unref(pass->color_transform); + wlr_drm_syncobj_timeline_unref(pass->signal_timeline); rect_union_finish(&pass->updated_region); + wl_array_release(&pass->textures); free(pass); } +static VkSemaphore render_pass_wait_sync_file(struct wlr_vk_render_pass *pass, + size_t sem_index, int sync_file_fd) { + struct wlr_vk_renderer *renderer = pass->renderer; + struct wlr_vk_command_buffer *render_cb = pass->command_buffer; + VkResult res; + + VkSemaphore *wait_semaphores = render_cb->wait_semaphores.data; + size_t wait_semaphores_len = render_cb->wait_semaphores.size / sizeof(wait_semaphores[0]); + + VkSemaphore *sem_ptr; + if (sem_index >= wait_semaphores_len) { + sem_ptr = wl_array_add(&render_cb->wait_semaphores, sizeof(*sem_ptr)); + if (sem_ptr == NULL) { + return VK_NULL_HANDLE; + } + *sem_ptr = VK_NULL_HANDLE; + } else { + sem_ptr = &wait_semaphores[sem_index]; + } + + if (*sem_ptr == VK_NULL_HANDLE) { + VkSemaphoreCreateInfo semaphore_info = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + }; + res = vkCreateSemaphore(renderer->dev->dev, &semaphore_info, NULL, sem_ptr); + if (res != VK_SUCCESS) { + wlr_vk_error("vkCreateSemaphore", res); + return VK_NULL_HANDLE; + } + } + + VkImportSemaphoreFdInfoKHR import_info = { + .sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR, + .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + .flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT, + .semaphore = *sem_ptr, + .fd = sync_file_fd, + }; + res = renderer->dev->api.vkImportSemaphoreFdKHR(renderer->dev->dev, &import_info); + if (res != VK_SUCCESS) { + wlr_vk_error("vkImportSemaphoreFdKHR", res); + return VK_NULL_HANDLE; + } + + return *sem_ptr; +} + static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { struct wlr_vk_render_pass *pass = get_render_pass(wlr_pass); struct wlr_vk_renderer *renderer = pass->renderer; @@ -179,14 +235,15 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { vkCmdEndRenderPass(render_cb->vk); - // insert acquire and release barriers for dmabuf-images - uint32_t barrier_count = wl_list_length(&renderer->foreign_textures) + 1; - render_wait = calloc(barrier_count * WLR_DMABUF_MAX_PLANES, sizeof(*render_wait)); + size_t pass_textures_len = pass->textures.size / sizeof(struct wlr_vk_render_pass_texture); + size_t render_wait_cap = pass_textures_len * WLR_DMABUF_MAX_PLANES; + render_wait = calloc(render_wait_cap, sizeof(*render_wait)); if (render_wait == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); goto error; } + uint32_t barrier_count = wl_list_length(&renderer->foreign_textures) + 1; VkImageMemoryBarrier *acquire_barriers = calloc(barrier_count, sizeof(*acquire_barriers)); VkImageMemoryBarrier *release_barriers = calloc(barrier_count, sizeof(*release_barriers)); if (acquire_barriers == NULL || release_barriers == NULL) { @@ -198,7 +255,6 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { struct wlr_vk_texture *texture, *tmp_tex; size_t idx = 0; - uint32_t render_wait_len = 0; wl_list_for_each_safe(texture, tmp_tex, &renderer->foreign_textures, foreign_link) { if (!texture->transitioned) { texture->transitioned = true; @@ -236,23 +292,53 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { ++idx; - if (!vulkan_sync_foreign_texture(texture)) { - wlr_log(WLR_ERROR, "Failed to wait for foreign texture DMA-BUF fence"); + wl_list_remove(&texture->foreign_link); + texture->owned = false; + } + + uint32_t render_wait_len = 0; + struct wlr_vk_render_pass_texture *pass_texture; + wl_array_for_each(pass_texture, &pass->textures) { + int sync_file_fds[WLR_DMABUF_MAX_PLANES]; + for (size_t i = 0; i < WLR_DMABUF_MAX_PLANES; i++) { + sync_file_fds[i] = -1; + } + + if (pass_texture->wait_timeline) { + int sync_file_fd = wlr_drm_syncobj_timeline_export_sync_file(pass_texture->wait_timeline, pass_texture->wait_point); + if (sync_file_fd < 0) { + wlr_log(WLR_ERROR, "Failed to export wait timeline point as sync_file"); + continue; + } + + sync_file_fds[0] = sync_file_fd; } else { - for (size_t i = 0; i < WLR_DMABUF_MAX_PLANES; i++) { - if (texture->foreign_semaphores[i] != VK_NULL_HANDLE) { - assert(render_wait_len < barrier_count * WLR_DMABUF_MAX_PLANES); - render_wait[render_wait_len++] = (VkSemaphoreSubmitInfoKHR){ - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO_KHR, - .semaphore = texture->foreign_semaphores[i], - .stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT_KHR, - }; - } + struct wlr_vk_texture *texture = pass_texture->texture; + if (!vulkan_sync_foreign_texture(texture, sync_file_fds)) { + wlr_log(WLR_ERROR, "Failed to wait for foreign texture DMA-BUF fence"); + continue; } } - wl_list_remove(&texture->foreign_link); - texture->owned = false; + for (size_t i = 0; i < WLR_DMABUF_MAX_PLANES; i++) { + if (sync_file_fds[i] < 0) { + continue; + } + + VkSemaphore sem = render_pass_wait_sync_file(pass, render_wait_len, sync_file_fds[i]); + if (sem == VK_NULL_HANDLE) { + close(sync_file_fds[i]); + continue; + } + + render_wait[render_wait_len] = (VkSemaphoreSubmitInfoKHR){ + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO_KHR, + .semaphore = sem, + .stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT_KHR, + }; + + render_wait_len++; + } } // also add acquire/release barriers for the current render buffer @@ -452,7 +538,8 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { wl_list_insert(&stage_cb->stage_buffers, &stage_buf->link); } - if (!vulkan_sync_render_buffer(renderer, render_buffer, render_cb)) { + if (!vulkan_sync_render_buffer(renderer, render_buffer, render_cb, + pass->signal_timeline, pass->signal_point)) { wlr_log(WLR_ERROR, "Failed to sync render buffer"); } @@ -704,6 +791,28 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, texture->last_used_cb = pass->command_buffer; pixman_region32_fini(&clip); + + if (texture->dmabuf_imported || (options != NULL && options->wait_timeline != NULL)) { + struct wlr_vk_render_pass_texture *pass_texture = + wl_array_add(&pass->textures, sizeof(*pass_texture)); + if (pass_texture == NULL) { + pass->failed = true; + return; + } + + struct wlr_drm_syncobj_timeline *wait_timeline = NULL; + uint64_t wait_point = 0; + if (options != NULL && options->wait_timeline != NULL) { + wait_timeline = wlr_drm_syncobj_timeline_ref(options->wait_timeline); + wait_point = options->wait_point; + } + + *pass_texture = (struct wlr_vk_render_pass_texture){ + .texture = texture, + .wait_timeline = wait_timeline, + .wait_point = wait_point, + }; + } } static const struct wlr_render_pass_impl render_pass_impl = { @@ -967,6 +1076,10 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend if (options != NULL && options->color_transform != NULL) { pass->color_transform = wlr_color_transform_ref(options->color_transform); } + if (options != NULL && options->signal_timeline != NULL) { + pass->signal_timeline = wlr_drm_syncobj_timeline_ref(options->signal_timeline); + pass->signal_point = options->signal_point; + } rect_union_init(&pass->updated_region); diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index 327f3b465..a2fb867d6 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -921,9 +922,9 @@ static struct wlr_vk_render_buffer *get_render_buffer( return buffer; } -bool vulkan_sync_foreign_texture(struct wlr_vk_texture *texture) { +bool vulkan_sync_foreign_texture(struct wlr_vk_texture *texture, + int sync_file_fds[static WLR_DMABUF_MAX_PLANES]) { struct wlr_vk_renderer *renderer = texture->renderer; - VkResult res; struct wlr_dmabuf_attributes dmabuf = {0}; if (!wlr_buffer_get_dmabuf(texture->buffer, &dmabuf)) { @@ -960,52 +961,22 @@ bool vulkan_sync_foreign_texture(struct wlr_vk_texture *texture) { return false; } - if (texture->foreign_semaphores[i] == VK_NULL_HANDLE) { - VkSemaphoreCreateInfo semaphore_info = { - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, - }; - res = vkCreateSemaphore(renderer->dev->dev, &semaphore_info, NULL, - &texture->foreign_semaphores[i]); - if (res != VK_SUCCESS) { - close(sync_file_fd); - wlr_vk_error("vkCreateSemaphore", res); - return false; - } - } - - VkImportSemaphoreFdInfoKHR import_info = { - .sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR, - .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, - .flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT, - .semaphore = texture->foreign_semaphores[i], - .fd = sync_file_fd, - }; - res = renderer->dev->api.vkImportSemaphoreFdKHR(renderer->dev->dev, &import_info); - if (res != VK_SUCCESS) { - close(sync_file_fd); - wlr_vk_error("vkImportSemaphoreFdKHR", res); - return false; - } + sync_file_fds[i] = sync_file_fd; } return true; } bool vulkan_sync_render_buffer(struct wlr_vk_renderer *renderer, - struct wlr_vk_render_buffer *render_buffer, struct wlr_vk_command_buffer *cb) { + struct wlr_vk_render_buffer *render_buffer, struct wlr_vk_command_buffer *cb, + struct wlr_drm_syncobj_timeline *signal_timeline, uint64_t signal_point) { VkResult res; - if (!renderer->dev->implicit_sync_interop) { + if (!renderer->dev->implicit_sync_interop && signal_timeline == NULL) { // We have no choice but to block here sadly return vulkan_wait_command_buffer(cb, renderer); } - struct wlr_dmabuf_attributes dmabuf = {0}; - if (!wlr_buffer_get_dmabuf(render_buffer->wlr_buffer, &dmabuf)) { - wlr_log(WLR_ERROR, "wlr_buffer_get_dmabuf failed"); - return false; - } - // Note: vkGetSemaphoreFdKHR implicitly resets the semaphore const VkSemaphoreGetFdInfoKHR get_fence_fd_info = { .sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR, @@ -1020,17 +991,32 @@ bool vulkan_sync_render_buffer(struct wlr_vk_renderer *renderer, return false; } - for (int i = 0; i < dmabuf.n_planes; i++) { - if (!dmabuf_import_sync_file(dmabuf.fd[i], DMA_BUF_SYNC_WRITE, - sync_file_fd)) { - close(sync_file_fd); - return false; + bool ok = false; + if (signal_timeline != NULL) { + if (!wlr_drm_syncobj_timeline_import_sync_file(signal_timeline, + signal_point, sync_file_fd)) { + goto out; + } + } else { + struct wlr_dmabuf_attributes dmabuf = {0}; + if (!wlr_buffer_get_dmabuf(render_buffer->wlr_buffer, &dmabuf)) { + wlr_log(WLR_ERROR, "wlr_buffer_get_dmabuf failed"); + goto out; + } + + for (int i = 0; i < dmabuf.n_planes; i++) { + if (!dmabuf_import_sync_file(dmabuf.fd[i], DMA_BUF_SYNC_WRITE, + sync_file_fd)) { + goto out; + } } } - close(sync_file_fd); + ok = true; - return true; +out: + close(sync_file_fd); + return ok; } static const struct wlr_drm_format_set *vulkan_get_texture_formats( @@ -1073,6 +1059,11 @@ static void vulkan_destroy(struct wlr_renderer *wlr_renderer) { if (cb->binary_semaphore != VK_NULL_HANDLE) { vkDestroySemaphore(renderer->dev->dev, cb->binary_semaphore, NULL); } + VkSemaphore *sem_ptr; + wl_array_for_each(sem_ptr, &cb->wait_semaphores) { + vkDestroySemaphore(renderer->dev->dev, *sem_ptr, NULL); + } + wl_array_release(&cb->wait_semaphores); } // stage.cb automatically freed with command pool @@ -2436,6 +2427,11 @@ struct wlr_renderer *vulkan_renderer_create_for_device(struct wlr_vk_device *dev wl_list_init(&renderer->color_transforms); wl_list_init(&renderer->pipeline_layouts); + uint64_t cap_syncobj_timeline; + if (dev->drm_fd >= 0 && drmGetCap(dev->drm_fd, DRM_CAP_SYNCOBJ_TIMELINE, &cap_syncobj_timeline) == 0) { + renderer->wlr_renderer.features.timeline = dev->sync_file_import_export && cap_syncobj_timeline != 0; + } + if (!init_static_render_data(renderer)) { goto error; } diff --git a/render/vulkan/texture.c b/render/vulkan/texture.c index e3bd69793..61a6040ee 100644 --- a/render/vulkan/texture.c +++ b/render/vulkan/texture.c @@ -208,12 +208,6 @@ void vulkan_texture_destroy(struct wlr_vk_texture *texture) { free(view); } - for (size_t i = 0; i < WLR_DMABUF_MAX_PLANES; i++) { - if (texture->foreign_semaphores[i] != VK_NULL_HANDLE) { - vkDestroySemaphore(dev, texture->foreign_semaphores[i], NULL); - } - } - vkDestroyImage(dev, texture->image, NULL); for (unsigned i = 0u; i < texture->mem_count; ++i) { diff --git a/render/vulkan/vulkan.c b/render/vulkan/vulkan.c index 905feb906..00e5b4519 100644 --- a/render/vulkan/vulkan.c +++ b/render/vulkan/vulkan.c @@ -527,6 +527,7 @@ struct wlr_vk_device *vulkan_device_create(struct wlr_vk_instance *ini, wlr_log(WLR_DEBUG, "DMA-BUF sync_file import/export not supported"); } + dev->sync_file_import_export = exportable_semaphore && importable_semaphore; dev->implicit_sync_interop = exportable_semaphore && importable_semaphore && dmabuf_sync_file_import_export; if (dev->implicit_sync_interop) { From 1520be3c5c29ef2894cee790880af624c063f0a0 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Fri, 25 Oct 2024 08:16:09 +0300 Subject: [PATCH 157/519] Make all listeners private --- include/wlr/backend/session.h | 5 ++- include/wlr/render/swapchain.h | 8 +++- include/wlr/types/wlr_compositor.h | 15 ++++--- include/wlr/types/wlr_damage_ring.h | 5 ++- include/wlr/types/wlr_data_control_v1.h | 12 ++++-- include/wlr/types/wlr_data_device.h | 26 ++++++++---- include/wlr/types/wlr_drm.h | 4 +- include/wlr/types/wlr_drm_lease_v1.h | 23 +++++++---- include/wlr/types/wlr_export_dmabuf_v1.h | 12 ++++-- .../types/wlr_ext_foreign_toplevel_list_v1.h | 6 ++- .../wlr_foreign_toplevel_management_v1.h | 6 ++- include/wlr/types/wlr_fullscreen_shell_v1.h | 6 ++- include/wlr/types/wlr_gamma_control_v1.h | 12 ++++-- include/wlr/types/wlr_idle_inhibit_v1.h | 11 +++-- include/wlr/types/wlr_input_method_v2.h | 22 ++++++---- .../types/wlr_keyboard_shortcuts_inhibit_v1.h | 14 ++++--- include/wlr/types/wlr_layer_shell_v1.h | 6 ++- include/wlr/types/wlr_output.h | 11 +++-- include/wlr/types/wlr_output_management_v1.h | 14 +++++-- .../types/wlr_output_power_management_v1.h | 14 ++++--- .../wlr/types/wlr_pointer_constraints_v1.h | 6 ++- include/wlr/types/wlr_pointer_gestures_v1.h | 6 ++- include/wlr/types/wlr_presentation_time.h | 12 ++++-- include/wlr/types/wlr_primary_selection_v1.h | 16 +++++--- include/wlr/types/wlr_relative_pointer_v1.h | 14 ++++--- include/wlr/types/wlr_screencopy_v1.h | 15 ++++--- include/wlr/types/wlr_seat.h | 41 +++++++++++-------- include/wlr/types/wlr_server_decoration.h | 12 ++++-- include/wlr/types/wlr_subcompositor.h | 12 +++--- include/wlr/types/wlr_tablet_v2.h | 26 ++++++++---- include/wlr/types/wlr_tearing_control_v1.h | 5 ++- include/wlr/types/wlr_text_input_v3.h | 14 ++++--- include/wlr/types/wlr_transient_seat_v1.h | 5 ++- include/wlr/types/wlr_viewporter.h | 4 +- include/wlr/types/wlr_virtual_keyboard_v1.h | 6 ++- include/wlr/types/wlr_virtual_pointer_v1.h | 6 ++- include/wlr/types/wlr_xdg_decoration_v1.h | 6 ++- include/wlr/types/wlr_xdg_foreign_registry.h | 5 ++- include/wlr/types/wlr_xdg_foreign_v1.h | 25 +++++++---- include/wlr/types/wlr_xdg_foreign_v2.h | 25 +++++++---- include/wlr/types/wlr_xdg_output_v1.h | 16 +++++--- include/wlr/types/wlr_xdg_shell.h | 19 ++++++--- include/wlr/xwayland/server.h | 8 ++-- include/wlr/xwayland/xwayland.h | 9 ++-- 44 files changed, 365 insertions(+), 180 deletions(-) diff --git a/include/wlr/backend/session.h b/include/wlr/backend/session.h index 3179d1f54..bd218ce8b 100644 --- a/include/wlr/backend/session.h +++ b/include/wlr/backend/session.h @@ -60,13 +60,16 @@ struct wlr_session { struct wl_list devices; // wlr_device.link struct wl_event_loop *event_loop; - struct wl_listener event_loop_destroy; struct { struct wl_signal active; struct wl_signal add_drm_card; // struct wlr_session_add_event struct wl_signal destroy; } events; + + struct { + struct wl_listener event_loop_destroy; + } WLR_PRIVATE; }; struct wlr_session_add_event { diff --git a/include/wlr/render/swapchain.h b/include/wlr/render/swapchain.h index 2317f1b62..4ca1ac590 100644 --- a/include/wlr/render/swapchain.h +++ b/include/wlr/render/swapchain.h @@ -11,7 +11,9 @@ struct wlr_swapchain_slot { struct wlr_buffer *buffer; bool acquired; // waiting for release - struct wl_listener release; + struct { + struct wl_listener release; + } WLR_PRIVATE; }; struct wlr_swapchain { @@ -22,7 +24,9 @@ struct wlr_swapchain { struct wlr_swapchain_slot slots[WLR_SWAPCHAIN_CAP]; - struct wl_listener allocator_destroy; + struct { + struct wl_listener allocator_destroy; + } WLR_PRIVATE; }; struct wlr_swapchain *wlr_swapchain_create( diff --git a/include/wlr/types/wlr_compositor.h b/include/wlr/types/wlr_compositor.h index 291e470c2..d26f8e2ec 100644 --- a/include/wlr/types/wlr_compositor.h +++ b/include/wlr/types/wlr_compositor.h @@ -122,8 +122,11 @@ struct wlr_surface_output { struct wlr_output *output; struct wl_list link; // wlr_surface.current_outputs - struct wl_listener bind; - struct wl_listener destroy; + + struct { + struct wl_listener bind; + struct wl_listener destroy; + } WLR_PRIVATE; }; struct wlr_surface { @@ -271,13 +274,15 @@ struct wlr_compositor { struct wl_global *global; struct wlr_renderer *renderer; // may be NULL - struct wl_listener display_destroy; - struct wl_listener renderer_destroy; - struct { struct wl_signal new_surface; struct wl_signal destroy; } events; + + struct { + struct wl_listener display_destroy; + struct wl_listener renderer_destroy; + } WLR_PRIVATE; }; typedef void (*wlr_surface_iterator_func_t)(struct wlr_surface *surface, diff --git a/include/wlr/types/wlr_damage_ring.h b/include/wlr/types/wlr_damage_ring.h index feb64777c..cc2eaa0f2 100644 --- a/include/wlr/types/wlr_damage_ring.h +++ b/include/wlr/types/wlr_damage_ring.h @@ -19,11 +19,14 @@ struct wlr_box; struct wlr_damage_ring_buffer { struct wlr_buffer *buffer; - struct wl_listener destroy; pixman_region32_t damage; struct wlr_damage_ring *ring; struct wl_list link; // wlr_damage_ring.buffers + + struct { + struct wl_listener destroy; + } WLR_PRIVATE; }; struct wlr_damage_ring { diff --git a/include/wlr/types/wlr_data_control_v1.h b/include/wlr/types/wlr_data_control_v1.h index 4435f14a2..9ef86d7cf 100644 --- a/include/wlr/types/wlr_data_control_v1.h +++ b/include/wlr/types/wlr_data_control_v1.h @@ -21,7 +21,9 @@ struct wlr_data_control_manager_v1 { struct wl_signal new_device; // wlr_data_control_device_v1 } events; - struct wl_listener display_destroy; + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_data_control_device_v1 { @@ -33,9 +35,11 @@ struct wlr_data_control_device_v1 { struct wl_resource *selection_offer_resource; // current selection offer struct wl_resource *primary_selection_offer_resource; // current primary selection offer - struct wl_listener seat_destroy; - struct wl_listener seat_set_selection; - struct wl_listener seat_set_primary_selection; + struct { + struct wl_listener seat_destroy; + struct wl_listener seat_set_selection; + struct wl_listener seat_set_primary_selection; + } WLR_PRIVATE; }; struct wlr_data_control_manager_v1 *wlr_data_control_manager_v1_create( diff --git a/include/wlr/types/wlr_data_device.h b/include/wlr/types/wlr_data_device.h index 85d661a77..2a618d3fe 100644 --- a/include/wlr/types/wlr_data_device.h +++ b/include/wlr/types/wlr_data_device.h @@ -16,13 +16,15 @@ struct wlr_data_device_manager { struct wl_global *global; struct wl_list data_sources; - struct wl_listener display_destroy; - struct { struct wl_signal destroy; } events; void *data; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; enum wlr_data_offer_type { @@ -40,7 +42,9 @@ struct wlr_data_offer { enum wl_data_device_manager_dnd_action preferred_action; bool in_ask; - struct wl_listener source_destroy; + struct { + struct wl_listener source_destroy; + } WLR_PRIVATE; }; /** @@ -89,9 +93,11 @@ struct wlr_drag_icon { struct wl_signal destroy; } events; - struct wl_listener surface_destroy; - void *data; + + struct { + struct wl_listener surface_destroy; + } WLR_PRIVATE; }; enum wlr_drag_grab_type { @@ -124,11 +130,13 @@ struct wlr_drag { struct wl_signal destroy; } events; - struct wl_listener source_destroy; - struct wl_listener seat_client_destroy; - struct wl_listener icon_destroy; - void *data; + + struct { + struct wl_listener source_destroy; + struct wl_listener seat_client_destroy; + struct wl_listener icon_destroy; + } WLR_PRIVATE; }; struct wlr_drag_motion_event { diff --git a/include/wlr/types/wlr_drm.h b/include/wlr/types/wlr_drm.h index a256a8deb..62dfa04f5 100644 --- a/include/wlr/types/wlr_drm.h +++ b/include/wlr/types/wlr_drm.h @@ -21,7 +21,9 @@ struct wlr_drm_buffer { struct wl_resource *resource; // can be NULL if the client destroyed it struct wlr_dmabuf_attributes dmabuf; - struct wl_listener release; + struct { + struct wl_listener release; + } WLR_PRIVATE; }; /** diff --git a/include/wlr/types/wlr_drm_lease_v1.h b/include/wlr/types/wlr_drm_lease_v1.h index cda02a10f..05e381249 100644 --- a/include/wlr/types/wlr_drm_lease_v1.h +++ b/include/wlr/types/wlr_drm_lease_v1.h @@ -18,7 +18,6 @@ struct wlr_drm_lease_v1_manager { struct wl_list devices; // wlr_drm_lease_device_v1.link struct wl_display *display; - struct wl_listener display_destroy; struct { /** @@ -29,6 +28,10 @@ struct wlr_drm_lease_v1_manager { */ struct wl_signal request; } events; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_drm_lease_device_v1 { @@ -43,9 +46,11 @@ struct wlr_drm_lease_device_v1 { struct wl_list requests; // wlr_drm_lease_request_v1.link struct wl_list link; // wlr_drm_lease_v1_manager.devices - struct wl_listener backend_destroy; - void *data; + + struct { + struct wl_listener backend_destroy; + } WLR_PRIVATE; }; struct wlr_drm_lease_v1; @@ -58,9 +63,11 @@ struct wlr_drm_lease_connector_v1 { /** NULL if no client is currently leasing this connector */ struct wlr_drm_lease_v1 *active_lease; - struct wl_listener destroy; - struct wl_list link; // wlr_drm_lease_device_v1.connectors + + struct { + struct wl_listener destroy; + } WLR_PRIVATE; }; struct wlr_drm_lease_request_v1 { @@ -89,9 +96,11 @@ struct wlr_drm_lease_v1 { struct wl_list link; // wlr_drm_lease_device_v1.leases - struct wl_listener destroy; - void *data; + + struct { + struct wl_listener destroy; + } WLR_PRIVATE; }; /** diff --git a/include/wlr/types/wlr_export_dmabuf_v1.h b/include/wlr/types/wlr_export_dmabuf_v1.h index ba2d8bd85..2c5a24525 100644 --- a/include/wlr/types/wlr_export_dmabuf_v1.h +++ b/include/wlr/types/wlr_export_dmabuf_v1.h @@ -17,11 +17,13 @@ struct wlr_export_dmabuf_manager_v1 { struct wl_global *global; struct wl_list frames; // wlr_export_dmabuf_frame_v1.link - struct wl_listener display_destroy; - struct { struct wl_signal destroy; } events; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_export_dmabuf_frame_v1 { @@ -33,8 +35,10 @@ struct wlr_export_dmabuf_frame_v1 { bool cursor_locked; - struct wl_listener output_commit; - struct wl_listener output_destroy; + struct { + struct wl_listener output_commit; + struct wl_listener output_destroy; + } WLR_PRIVATE; }; struct wlr_export_dmabuf_manager_v1 *wlr_export_dmabuf_manager_v1_create( diff --git a/include/wlr/types/wlr_ext_foreign_toplevel_list_v1.h b/include/wlr/types/wlr_ext_foreign_toplevel_list_v1.h index b2e4c7b03..902418eaf 100644 --- a/include/wlr/types/wlr_ext_foreign_toplevel_list_v1.h +++ b/include/wlr/types/wlr_ext_foreign_toplevel_list_v1.h @@ -16,13 +16,15 @@ struct wlr_ext_foreign_toplevel_list_v1 { struct wl_list resources; // wl_resource_get_link() struct wl_list toplevels; // ext_foreign_toplevel_handle_v1.link - struct wl_listener display_destroy; - struct { struct wl_signal destroy; } events; void *data; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_ext_foreign_toplevel_handle_v1 { diff --git a/include/wlr/types/wlr_foreign_toplevel_management_v1.h b/include/wlr/types/wlr_foreign_toplevel_management_v1.h index 499d40380..d21c5fc70 100644 --- a/include/wlr/types/wlr_foreign_toplevel_management_v1.h +++ b/include/wlr/types/wlr_foreign_toplevel_management_v1.h @@ -18,13 +18,15 @@ struct wlr_foreign_toplevel_manager_v1 { struct wl_list resources; // wl_resource_get_link() struct wl_list toplevels; // wlr_foreign_toplevel_handle_v1.link - struct wl_listener display_destroy; - struct { struct wl_signal destroy; } events; void *data; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; enum wlr_foreign_toplevel_handle_v1_state { diff --git a/include/wlr/types/wlr_fullscreen_shell_v1.h b/include/wlr/types/wlr_fullscreen_shell_v1.h index 54e4c0550..c4b1cbc7a 100644 --- a/include/wlr/types/wlr_fullscreen_shell_v1.h +++ b/include/wlr/types/wlr_fullscreen_shell_v1.h @@ -22,9 +22,11 @@ struct wlr_fullscreen_shell_v1 { struct wl_signal present_surface; } events; - struct wl_listener display_destroy; - void *data; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_fullscreen_shell_v1_present_surface_event { diff --git a/include/wlr/types/wlr_gamma_control_v1.h b/include/wlr/types/wlr_gamma_control_v1.h index 48fce09f6..b3a70f11e 100644 --- a/include/wlr/types/wlr_gamma_control_v1.h +++ b/include/wlr/types/wlr_gamma_control_v1.h @@ -10,14 +10,16 @@ struct wlr_gamma_control_manager_v1 { struct wl_global *global; struct wl_list controls; // wlr_gamma_control_v1.link - struct wl_listener display_destroy; - struct { struct wl_signal destroy; struct wl_signal set_gamma; // struct wlr_gamma_control_manager_v1_set_gamma_event } events; void *data; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_gamma_control_manager_v1_set_gamma_event { @@ -34,9 +36,11 @@ struct wlr_gamma_control_v1 { uint16_t *table; size_t ramp_size; - struct wl_listener output_destroy_listener; - void *data; + + struct { + struct wl_listener output_destroy_listener; + } WLR_PRIVATE; }; struct wlr_gamma_control_manager_v1 *wlr_gamma_control_manager_v1_create( diff --git a/include/wlr/types/wlr_idle_inhibit_v1.h b/include/wlr/types/wlr_idle_inhibit_v1.h index 596fbe889..9a42d4fdf 100644 --- a/include/wlr/types/wlr_idle_inhibit_v1.h +++ b/include/wlr/types/wlr_idle_inhibit_v1.h @@ -27,20 +27,21 @@ struct wlr_idle_inhibit_manager_v1 { struct wl_list inhibitors; // wlr_idle_inhibit_inhibitor_v1.link struct wl_global *global; - struct wl_listener display_destroy; - struct { struct wl_signal new_inhibitor; // struct wlr_idle_inhibitor_v1 struct wl_signal destroy; } events; void *data; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_idle_inhibitor_v1 { struct wlr_surface *surface; struct wl_resource *resource; - struct wl_listener surface_destroy; struct wl_list link; // wlr_idle_inhibit_manager_v1.inhibitors @@ -49,6 +50,10 @@ struct wlr_idle_inhibitor_v1 { } events; void *data; + + struct { + struct wl_listener surface_destroy; + } WLR_PRIVATE; }; struct wlr_idle_inhibit_manager_v1 *wlr_idle_inhibit_v1_create(struct wl_display *display); diff --git a/include/wlr/types/wlr_input_method_v2.h b/include/wlr/types/wlr_input_method_v2.h index fd299cc85..02216ddad 100644 --- a/include/wlr/types/wlr_input_method_v2.h +++ b/include/wlr/types/wlr_input_method_v2.h @@ -48,14 +48,16 @@ struct wlr_input_method_v2 { struct wl_list link; - struct wl_listener seat_client_destroy; - struct { struct wl_signal commit; // struct wlr_input_method_v2 struct wl_signal new_popup_surface; // struct wlr_input_popup_surface_v2 struct wl_signal grab_keyboard; // struct wlr_input_method_keyboard_grab_v2 struct wl_signal destroy; // struct wlr_input_method_v2 } events; + + struct { + struct wl_listener seat_client_destroy; + } WLR_PRIVATE; }; struct wlr_input_popup_surface_v2 { @@ -77,25 +79,29 @@ struct wlr_input_method_keyboard_grab_v2 { struct wlr_input_method_v2 *input_method; struct wlr_keyboard *keyboard; - struct wl_listener keyboard_keymap; - struct wl_listener keyboard_repeat_info; - struct wl_listener keyboard_destroy; - struct { struct wl_signal destroy; // struct wlr_input_method_keyboard_grab_v2 } events; + + struct { + struct wl_listener keyboard_keymap; + struct wl_listener keyboard_repeat_info; + struct wl_listener keyboard_destroy; + } WLR_PRIVATE; }; struct wlr_input_method_manager_v2 { struct wl_global *global; struct wl_list input_methods; // struct wlr_input_method_v2.link - struct wl_listener display_destroy; - struct { struct wl_signal input_method; // struct wlr_input_method_v2 struct wl_signal destroy; // struct wlr_input_method_manager_v2 } events; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_input_method_manager_v2 *wlr_input_method_manager_v2_create( diff --git a/include/wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h b/include/wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h index 946e75f4e..6c9b6889f 100644 --- a/include/wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h +++ b/include/wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h @@ -27,14 +27,16 @@ struct wlr_keyboard_shortcuts_inhibit_manager_v1 { struct wl_list inhibitors; struct wl_global *global; - struct wl_listener display_destroy; - struct { struct wl_signal new_inhibitor; // struct wlr_keyboard_shortcuts_inhibitor_v1 struct wl_signal destroy; } events; void *data; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_keyboard_shortcuts_inhibitor_v1 { @@ -43,9 +45,6 @@ struct wlr_keyboard_shortcuts_inhibitor_v1 { bool active; struct wl_resource *resource; - struct wl_listener surface_destroy; - struct wl_listener seat_destroy; - // wlr_keyboard_shortcuts_inhibit_manager_v1.inhibitors struct wl_list link; @@ -54,6 +53,11 @@ struct wlr_keyboard_shortcuts_inhibitor_v1 { } events; void *data; + + struct { + struct wl_listener surface_destroy; + struct wl_listener seat_destroy; + } WLR_PRIVATE; }; /* diff --git a/include/wlr/types/wlr_layer_shell_v1.h b/include/wlr/types/wlr_layer_shell_v1.h index d46f9b9c9..4234aa72e 100644 --- a/include/wlr/types/wlr_layer_shell_v1.h +++ b/include/wlr/types/wlr_layer_shell_v1.h @@ -31,8 +31,6 @@ struct wlr_layer_shell_v1 { struct wl_global *global; - struct wl_listener display_destroy; - struct { // Note: the output may be NULL. In this case, it is your // responsibility to assign an output before returning. @@ -41,6 +39,10 @@ struct wlr_layer_shell_v1 { } events; void *data; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; enum wlr_layer_surface_v1_state_field { diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 1cc7baecd..e0ee7d092 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -48,8 +48,11 @@ struct wlr_output_cursor { bool own_texture; struct wlr_drm_syncobj_timeline *wait_timeline; uint64_t wait_point; - struct wl_listener renderer_destroy; struct wl_list link; + + struct { + struct wl_listener renderer_destroy; + } WLR_PRIVATE; }; enum wlr_output_adaptive_sync_status { @@ -226,11 +229,13 @@ struct wlr_output { struct wlr_renderer *renderer; struct wlr_swapchain *swapchain; - struct wl_listener display_destroy; - struct wlr_addon_set addons; void *data; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_output_event_damage { diff --git a/include/wlr/types/wlr_output_management_v1.h b/include/wlr/types/wlr_output_management_v1.h index 57b880dff..2835a5924 100644 --- a/include/wlr/types/wlr_output_management_v1.h +++ b/include/wlr/types/wlr_output_management_v1.h @@ -39,9 +39,11 @@ struct wlr_output_manager_v1 { struct wl_signal destroy; } events; - struct wl_listener display_destroy; - void *data; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_output_head_v1_state { @@ -67,7 +69,9 @@ struct wlr_output_head_v1 { struct wl_list resources; // wl_resource_get_link() struct wl_list mode_resources; // wl_resource_get_link() - struct wl_listener output_destroy; + struct { + struct wl_listener output_destroy; + } WLR_PRIVATE; }; struct wlr_output_configuration_v1 { @@ -89,7 +93,9 @@ struct wlr_output_configuration_head_v1 { // client state struct wl_resource *resource; // can be NULL if finalized or disabled - struct wl_listener output_destroy; + struct { + struct wl_listener output_destroy; + } WLR_PRIVATE; }; /** diff --git a/include/wlr/types/wlr_output_power_management_v1.h b/include/wlr/types/wlr_output_power_management_v1.h index 688e37a4f..ef8da8e55 100644 --- a/include/wlr/types/wlr_output_power_management_v1.h +++ b/include/wlr/types/wlr_output_power_management_v1.h @@ -8,14 +8,16 @@ struct wlr_output_power_manager_v1 { struct wl_global *global; struct wl_list output_powers; // wlr_output_power_v1.link - struct wl_listener display_destroy; - struct { struct wl_signal set_mode; // struct wlr_output_power_v1_set_mode_event struct wl_signal destroy; } events; void *data; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_output_power_v1 { @@ -24,10 +26,12 @@ struct wlr_output_power_v1 { struct wlr_output_power_manager_v1 *manager; struct wl_list link; // wlr_output_power_manager_v1.output_powers - struct wl_listener output_destroy_listener; - struct wl_listener output_commit_listener; - void *data; + + struct { + struct wl_listener output_destroy_listener; + struct wl_listener output_commit_listener; + } WLR_PRIVATE; }; struct wlr_output_power_v1_set_mode_event { diff --git a/include/wlr/types/wlr_pointer_constraints_v1.h b/include/wlr/types/wlr_pointer_constraints_v1.h index 75dddb818..e0f588065 100644 --- a/include/wlr/types/wlr_pointer_constraints_v1.h +++ b/include/wlr/types/wlr_pointer_constraints_v1.h @@ -86,9 +86,11 @@ struct wlr_pointer_constraints_v1 { struct wl_signal new_constraint; } events; - struct wl_listener display_destroy; - void *data; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_pointer_constraints_v1 *wlr_pointer_constraints_v1_create( diff --git a/include/wlr/types/wlr_pointer_gestures_v1.h b/include/wlr/types/wlr_pointer_gestures_v1.h index 5510ce21c..bd2715178 100644 --- a/include/wlr/types/wlr_pointer_gestures_v1.h +++ b/include/wlr/types/wlr_pointer_gestures_v1.h @@ -20,13 +20,15 @@ struct wlr_pointer_gestures_v1 { struct wl_list pinches; // wl_resource_get_link() struct wl_list holds; // wl_resource_get_link() - struct wl_listener display_destroy; - struct { struct wl_signal destroy; } events; void *data; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_pointer_gestures_v1 *wlr_pointer_gestures_v1_create( diff --git a/include/wlr/types/wlr_presentation_time.h b/include/wlr/types/wlr_presentation_time.h index 717665e68..9becffeee 100644 --- a/include/wlr/types/wlr_presentation_time.h +++ b/include/wlr/types/wlr_presentation_time.h @@ -26,7 +26,9 @@ struct wlr_presentation { struct wl_signal destroy; } events; - struct wl_listener display_destroy; + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_presentation_feedback { @@ -39,9 +41,11 @@ struct wlr_presentation_feedback { uint32_t output_commit_seq; bool zero_copy; - struct wl_listener output_commit; - struct wl_listener output_present; - struct wl_listener output_destroy; + struct { + struct wl_listener output_commit; + struct wl_listener output_present; + struct wl_listener output_destroy; + } WLR_PRIVATE; }; struct wlr_presentation_event { diff --git a/include/wlr/types/wlr_primary_selection_v1.h b/include/wlr/types/wlr_primary_selection_v1.h index 78b542a04..3ab2e1bdd 100644 --- a/include/wlr/types/wlr_primary_selection_v1.h +++ b/include/wlr/types/wlr_primary_selection_v1.h @@ -16,13 +16,15 @@ struct wlr_primary_selection_v1_device_manager { struct wl_global *global; struct wl_list devices; // wlr_primary_selection_v1_device.link - struct wl_listener display_destroy; - struct { struct wl_signal destroy; } events; void *data; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; /** @@ -36,11 +38,13 @@ struct wlr_primary_selection_v1_device { struct wl_list offers; // wl_resource_get_link() - struct wl_listener seat_destroy; - struct wl_listener seat_focus_change; - struct wl_listener seat_set_primary_selection; - void *data; + + struct { + struct wl_listener seat_destroy; + struct wl_listener seat_focus_change; + struct wl_listener seat_set_primary_selection; + } WLR_PRIVATE; }; struct wlr_primary_selection_v1_device_manager * diff --git a/include/wlr/types/wlr_relative_pointer_v1.h b/include/wlr/types/wlr_relative_pointer_v1.h index fcd44d529..4db7b5220 100644 --- a/include/wlr/types/wlr_relative_pointer_v1.h +++ b/include/wlr/types/wlr_relative_pointer_v1.h @@ -30,9 +30,11 @@ struct wlr_relative_pointer_manager_v1 { struct wl_signal new_relative_pointer; // struct wlr_relative_pointer_v1 } events; - struct wl_listener display_destroy_listener; - void *data; + + struct { + struct wl_listener display_destroy_listener; + } WLR_PRIVATE; }; /** @@ -51,10 +53,12 @@ struct wlr_relative_pointer_v1 { struct wl_signal destroy; } events; - struct wl_listener seat_destroy; - struct wl_listener pointer_destroy; - void *data; + + struct { + struct wl_listener seat_destroy; + struct wl_listener pointer_destroy; + } WLR_PRIVATE; }; struct wlr_relative_pointer_manager_v1 *wlr_relative_pointer_manager_v1_create( diff --git a/include/wlr/types/wlr_screencopy_v1.h b/include/wlr/types/wlr_screencopy_v1.h index c1e990fa8..acd0ce185 100644 --- a/include/wlr/types/wlr_screencopy_v1.h +++ b/include/wlr/types/wlr_screencopy_v1.h @@ -18,13 +18,15 @@ struct wlr_screencopy_manager_v1 { struct wl_global *global; struct wl_list frames; // wlr_screencopy_frame_v1.link - struct wl_listener display_destroy; - struct { struct wl_signal destroy; } events; void *data; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_screencopy_v1_client { @@ -50,11 +52,14 @@ struct wlr_screencopy_frame_v1 { struct wlr_buffer *buffer; struct wlr_output *output; - struct wl_listener output_commit; - struct wl_listener output_destroy; - struct wl_listener output_enable; void *data; + + struct { + struct wl_listener output_commit; + struct wl_listener output_destroy; + struct wl_listener output_enable; + } WLR_PRIVATE; }; struct wlr_screencopy_manager_v1 *wlr_screencopy_manager_v1_create( diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h index dcb35c62c..69ee69fdd 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -78,15 +78,17 @@ struct wlr_touch_point { struct wlr_seat_client *focus_client; double sx, sy; - struct wl_listener surface_destroy; - struct wl_listener focus_surface_destroy; - struct wl_listener client_destroy; - struct { struct wl_signal destroy; } events; struct wl_list link; + + struct { + struct wl_listener surface_destroy; + struct wl_listener focus_surface_destroy; + struct wl_listener client_destroy; + } WLR_PRIVATE; }; struct wlr_seat_pointer_grab; @@ -194,11 +196,14 @@ struct wlr_seat_pointer_state { uint32_t grab_serial; uint32_t grab_time; - struct wl_listener surface_destroy; struct { struct wl_signal focus_change; // struct wlr_seat_pointer_focus_change_event } events; + + struct { + struct wl_listener surface_destroy; + } WLR_PRIVATE; }; struct wlr_seat_keyboard_state { @@ -208,18 +213,20 @@ struct wlr_seat_keyboard_state { struct wlr_seat_client *focused_client; struct wlr_surface *focused_surface; - struct wl_listener keyboard_destroy; - struct wl_listener keyboard_keymap; - struct wl_listener keyboard_repeat_info; - - struct wl_listener surface_destroy; - struct wlr_seat_keyboard_grab *grab; struct wlr_seat_keyboard_grab *default_grab; struct { struct wl_signal focus_change; // struct wlr_seat_keyboard_focus_change_event } events; + + struct { + struct wl_listener keyboard_destroy; + struct wl_listener keyboard_keymap; + struct wl_listener keyboard_repeat_info; + + struct wl_listener surface_destroy; + } WLR_PRIVATE; }; struct wlr_seat_touch_state { @@ -262,11 +269,6 @@ struct wlr_seat { struct wlr_seat_keyboard_state keyboard_state; struct wlr_seat_touch_state touch_state; - struct wl_listener display_destroy; - struct wl_listener selection_source_destroy; - struct wl_listener primary_selection_source_destroy; - struct wl_listener drag_source_destroy; - struct { struct wl_signal pointer_grab_begin; struct wl_signal pointer_grab_end; @@ -302,6 +304,13 @@ struct wlr_seat { } events; void *data; + + struct { + struct wl_listener display_destroy; + struct wl_listener selection_source_destroy; + struct wl_listener primary_selection_source_destroy; + struct wl_listener drag_source_destroy; + } WLR_PRIVATE; }; struct wlr_seat_pointer_request_set_cursor_event { diff --git a/include/wlr/types/wlr_server_decoration.h b/include/wlr/types/wlr_server_decoration.h index 7e78c5f21..2b0e859eb 100644 --- a/include/wlr/types/wlr_server_decoration.h +++ b/include/wlr/types/wlr_server_decoration.h @@ -49,14 +49,16 @@ struct wlr_server_decoration_manager { uint32_t default_mode; // enum wlr_server_decoration_manager_mode - struct wl_listener display_destroy; - struct { struct wl_signal new_decoration; struct wl_signal destroy; } events; void *data; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_server_decoration { @@ -71,9 +73,11 @@ struct wlr_server_decoration { struct wl_signal mode; } events; - struct wl_listener surface_destroy_listener; - void *data; + + struct { + struct wl_listener surface_destroy_listener; + } WLR_PRIVATE; }; struct wlr_server_decoration_manager *wlr_server_decoration_manager_create( diff --git a/include/wlr/types/wlr_subcompositor.h b/include/wlr/types/wlr_subcompositor.h index 26be02959..251379d30 100644 --- a/include/wlr/types/wlr_subcompositor.h +++ b/include/wlr/types/wlr_subcompositor.h @@ -41,9 +41,6 @@ struct wlr_subsurface { bool synchronized; bool added; - struct wl_listener surface_client_commit; - struct wl_listener parent_destroy; - struct { struct wl_signal destroy; } events; @@ -52,17 +49,22 @@ struct wlr_subsurface { struct { struct wlr_surface_synced parent_synced; + + struct wl_listener surface_client_commit; + struct wl_listener parent_destroy; } WLR_PRIVATE; }; struct wlr_subcompositor { struct wl_global *global; - struct wl_listener display_destroy; - struct { struct wl_signal destroy; } events; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; /** diff --git a/include/wlr/types/wlr_tablet_v2.h b/include/wlr/types/wlr_tablet_v2.h index 57e067d75..dc911f101 100644 --- a/include/wlr/types/wlr_tablet_v2.h +++ b/include/wlr/types/wlr_tablet_v2.h @@ -44,13 +44,15 @@ struct wlr_tablet_manager_v2 { struct wl_list clients; // wlr_tablet_manager_client_v2.link struct wl_list seats; // wlr_tablet_seat_v2.link - struct wl_listener display_destroy; - struct { struct wl_signal destroy; } events; void *data; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_tablet_v2_tablet { @@ -59,9 +61,11 @@ struct wlr_tablet_v2_tablet { struct wlr_input_device *wlr_device; struct wl_list clients; // wlr_tablet_client_v2.tablet_link - struct wl_listener tablet_destroy; - struct wlr_tablet_client_v2 *current_client; + + struct { + struct wl_listener tablet_destroy; + } WLR_PRIVATE; }; struct wlr_tablet_v2_tablet_tool { @@ -69,11 +73,8 @@ struct wlr_tablet_v2_tablet_tool { struct wlr_tablet_tool *wlr_tool; struct wl_list clients; // wlr_tablet_tool_client_v2.tool_link - struct wl_listener tool_destroy; - struct wlr_tablet_tool_client_v2 *current_client; struct wlr_surface *focused_surface; - struct wl_listener surface_destroy; struct wlr_tablet_tool_v2_grab *grab; struct wlr_tablet_tool_v2_grab default_grab; @@ -88,6 +89,11 @@ struct wlr_tablet_v2_tablet_tool { struct { struct wl_signal set_cursor; // struct wlr_tablet_v2_event_cursor } events; + + struct { + struct wl_listener surface_destroy; + struct wl_listener tool_destroy; + } WLR_PRIVATE; }; struct wlr_tablet_v2_tablet_pad { @@ -99,8 +105,6 @@ struct wlr_tablet_v2_tablet_pad { size_t group_count; uint32_t *groups; - struct wl_listener pad_destroy; - struct wlr_tablet_pad_client_v2 *current_client; struct wlr_tablet_pad_v2_grab *grab; struct wlr_tablet_pad_v2_grab default_grab; @@ -110,6 +114,10 @@ struct wlr_tablet_v2_tablet_pad { struct wl_signal strip_feedback; // struct wlr_tablet_v2_event_feedback struct wl_signal ring_feedback; // struct wlr_tablet_v2_event_feedback } events; + + struct { + struct wl_listener pad_destroy; + } WLR_PRIVATE; }; struct wlr_tablet_v2_event_cursor { diff --git a/include/wlr/types/wlr_tearing_control_v1.h b/include/wlr/types/wlr_tearing_control_v1.h index ddcb22bcc..ff981e414 100644 --- a/include/wlr/types/wlr_tearing_control_v1.h +++ b/include/wlr/types/wlr_tearing_control_v1.h @@ -44,13 +44,16 @@ struct wlr_tearing_control_manager_v1 { struct wl_list surface_hints; // wlr_tearing_control_v1.link - struct wl_listener display_destroy; struct { struct wl_signal new_object; // struct wlr_tearing_control_v1* struct wl_signal destroy; } events; void *data; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_tearing_control_manager_v1 *wlr_tearing_control_manager_v1_create( diff --git a/include/wlr/types/wlr_text_input_v3.h b/include/wlr/types/wlr_text_input_v3.h index 37397c1f3..20396a91f 100644 --- a/include/wlr/types/wlr_text_input_v3.h +++ b/include/wlr/types/wlr_text_input_v3.h @@ -56,27 +56,31 @@ struct wlr_text_input_v3 { struct wl_list link; - struct wl_listener surface_destroy; - struct wl_listener seat_destroy; - struct { struct wl_signal enable; // struct wlr_text_input_v3 struct wl_signal commit; // struct wlr_text_input_v3 struct wl_signal disable; // struct wlr_text_input_v3 struct wl_signal destroy; // struct wlr_text_input_v3 } events; + + struct { + struct wl_listener surface_destroy; + struct wl_listener seat_destroy; + } WLR_PRIVATE; }; struct wlr_text_input_manager_v3 { struct wl_global *global; struct wl_list text_inputs; // struct wlr_text_input_v3.resource.link - struct wl_listener display_destroy; - struct { struct wl_signal text_input; // struct wlr_text_input_v3 struct wl_signal destroy; // struct wlr_input_method_manager_v3 } events; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_text_input_manager_v3 *wlr_text_input_manager_v3_create( diff --git a/include/wlr/types/wlr_transient_seat_v1.h b/include/wlr/types/wlr_transient_seat_v1.h index b0d329fd5..9aafed3b4 100644 --- a/include/wlr/types/wlr_transient_seat_v1.h +++ b/include/wlr/types/wlr_transient_seat_v1.h @@ -24,7 +24,6 @@ struct wlr_transient_seat_v1 { struct wlr_transient_seat_manager_v1 { struct wl_global *global; - struct wl_listener display_destroy; struct { /** @@ -36,6 +35,10 @@ struct wlr_transient_seat_manager_v1 { */ struct wl_signal create_seat; // struct wlr_transient_seat_v1 } events; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_transient_seat_manager_v1 *wlr_transient_seat_manager_v1_create( diff --git a/include/wlr/types/wlr_viewporter.h b/include/wlr/types/wlr_viewporter.h index e1e2ec04a..55773724a 100644 --- a/include/wlr/types/wlr_viewporter.h +++ b/include/wlr/types/wlr_viewporter.h @@ -29,7 +29,9 @@ struct wlr_viewporter { struct wl_signal destroy; } events; - struct wl_listener display_destroy; + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_viewporter *wlr_viewporter_create(struct wl_display *display); diff --git a/include/wlr/types/wlr_virtual_keyboard_v1.h b/include/wlr/types/wlr_virtual_keyboard_v1.h index cb0bb9823..babc535a5 100644 --- a/include/wlr/types/wlr_virtual_keyboard_v1.h +++ b/include/wlr/types/wlr_virtual_keyboard_v1.h @@ -16,12 +16,14 @@ struct wlr_virtual_keyboard_manager_v1 { struct wl_global *global; struct wl_list virtual_keyboards; // wlr_virtual_keyboard_v1.link - struct wl_listener display_destroy; - struct { struct wl_signal new_virtual_keyboard; // struct wlr_virtual_keyboard_v1 struct wl_signal destroy; } events; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_virtual_keyboard_v1 { diff --git a/include/wlr/types/wlr_virtual_pointer_v1.h b/include/wlr/types/wlr_virtual_pointer_v1.h index a05268547..c8804e84e 100644 --- a/include/wlr/types/wlr_virtual_pointer_v1.h +++ b/include/wlr/types/wlr_virtual_pointer_v1.h @@ -18,12 +18,14 @@ struct wlr_virtual_pointer_manager_v1 { struct wl_global *global; struct wl_list virtual_pointers; // wlr_virtual_pointer_v1.link - struct wl_listener display_destroy; - struct { struct wl_signal new_virtual_pointer; // struct wlr_virtual_pointer_v1_new_pointer_event struct wl_signal destroy; } events; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_virtual_pointer_v1 { diff --git a/include/wlr/types/wlr_xdg_decoration_v1.h b/include/wlr/types/wlr_xdg_decoration_v1.h index 24928e915..78f84eb39 100644 --- a/include/wlr/types/wlr_xdg_decoration_v1.h +++ b/include/wlr/types/wlr_xdg_decoration_v1.h @@ -14,14 +14,16 @@ struct wlr_xdg_decoration_manager_v1 { struct wl_global *global; struct wl_list decorations; // wlr_xdg_toplevel_decoration.link - struct wl_listener display_destroy; - struct { struct wl_signal new_toplevel_decoration; // struct wlr_xdg_toplevel_decoration struct wl_signal destroy; } events; void *data; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_xdg_toplevel_decoration_v1_configure { diff --git a/include/wlr/types/wlr_xdg_foreign_registry.h b/include/wlr/types/wlr_xdg_foreign_registry.h index c240c8f3a..aa33b33a6 100644 --- a/include/wlr/types/wlr_xdg_foreign_registry.h +++ b/include/wlr/types/wlr_xdg_foreign_registry.h @@ -23,10 +23,13 @@ struct wlr_xdg_foreign_registry { struct wl_list exported_surfaces; // struct wlr_xdg_foreign_exported_surface - struct wl_listener display_destroy; struct { struct wl_signal destroy; } events; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_xdg_foreign_exported { diff --git a/include/wlr/types/wlr_xdg_foreign_v1.h b/include/wlr/types/wlr_xdg_foreign_v1.h index 7b31a61ab..975828c2c 100644 --- a/include/wlr/types/wlr_xdg_foreign_v1.h +++ b/include/wlr/types/wlr_xdg_foreign_v1.h @@ -18,9 +18,6 @@ struct wlr_xdg_foreign_v1 { struct wl_list objects; // wlr_xdg_exported_v1.link or wlr_xdg_imported_v1.link } exporter, importer; - struct wl_listener foreign_registry_destroy; - struct wl_listener display_destroy; - struct wlr_xdg_foreign_registry *registry; struct { @@ -28,24 +25,34 @@ struct wlr_xdg_foreign_v1 { } events; void *data; + + struct { + struct wl_listener foreign_registry_destroy; + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_xdg_exported_v1 { struct wlr_xdg_foreign_exported base; struct wl_resource *resource; - struct wl_listener xdg_toplevel_destroy; - struct wl_list link; // wlr_xdg_foreign_v1.exporter.objects + + struct { + struct wl_listener xdg_toplevel_destroy; + } WLR_PRIVATE; }; struct wlr_xdg_imported_v1 { struct wlr_xdg_foreign_exported *exported; - struct wl_listener exported_destroyed; struct wl_resource *resource; struct wl_list link; // wlr_xdg_foreign_v1.importer.objects struct wl_list children; + + struct { + struct wl_listener exported_destroyed; + } WLR_PRIVATE; }; struct wlr_xdg_imported_child_v1 { @@ -54,8 +61,10 @@ struct wlr_xdg_imported_child_v1 { struct wl_list link; // wlr_xdg_imported_v1.children - struct wl_listener xdg_toplevel_destroy; - struct wl_listener xdg_toplevel_set_parent; + struct { + struct wl_listener xdg_toplevel_destroy; + struct wl_listener xdg_toplevel_set_parent; + } WLR_PRIVATE; }; struct wlr_xdg_foreign_v1 *wlr_xdg_foreign_v1_create( diff --git a/include/wlr/types/wlr_xdg_foreign_v2.h b/include/wlr/types/wlr_xdg_foreign_v2.h index f5e14c700..0af0be52e 100644 --- a/include/wlr/types/wlr_xdg_foreign_v2.h +++ b/include/wlr/types/wlr_xdg_foreign_v2.h @@ -18,9 +18,6 @@ struct wlr_xdg_foreign_v2 { struct wl_list objects; // wlr_xdg_exported_v2.link or wlr_xdg_imported_v2.link } exporter, importer; - struct wl_listener foreign_registry_destroy; - struct wl_listener display_destroy; - struct wlr_xdg_foreign_registry *registry; struct { @@ -28,24 +25,34 @@ struct wlr_xdg_foreign_v2 { } events; void *data; + + struct { + struct wl_listener foreign_registry_destroy; + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_xdg_exported_v2 { struct wlr_xdg_foreign_exported base; struct wl_resource *resource; - struct wl_listener xdg_toplevel_destroy; - struct wl_list link; // wlr_xdg_foreign_v2.exporter.objects + + struct { + struct wl_listener xdg_toplevel_destroy; + } WLR_PRIVATE; }; struct wlr_xdg_imported_v2 { struct wlr_xdg_foreign_exported *exported; - struct wl_listener exported_destroyed; struct wl_resource *resource; struct wl_list link; // wlr_xdg_foreign_v2.importer.objects struct wl_list children; + + struct { + struct wl_listener exported_destroyed; + } WLR_PRIVATE; }; struct wlr_xdg_imported_child_v2 { @@ -54,8 +61,10 @@ struct wlr_xdg_imported_child_v2 { struct wl_list link; // wlr_xdg_imported_v2.children - struct wl_listener xdg_toplevel_destroy; - struct wl_listener xdg_toplevel_set_parent; + struct { + struct wl_listener xdg_toplevel_destroy; + struct wl_listener xdg_toplevel_set_parent; + } WLR_PRIVATE; }; struct wlr_xdg_foreign_v2 *wlr_xdg_foreign_v2_create( diff --git a/include/wlr/types/wlr_xdg_output_v1.h b/include/wlr/types/wlr_xdg_output_v1.h index 340252ddf..4ee8ec3b8 100644 --- a/include/wlr/types/wlr_xdg_output_v1.h +++ b/include/wlr/types/wlr_xdg_output_v1.h @@ -21,8 +21,10 @@ struct wlr_xdg_output_v1 { int32_t x, y; int32_t width, height; - struct wl_listener destroy; - struct wl_listener description; + struct { + struct wl_listener destroy; + struct wl_listener description; + } WLR_PRIVATE; }; struct wlr_xdg_output_manager_v1 { @@ -35,10 +37,12 @@ struct wlr_xdg_output_manager_v1 { struct wl_signal destroy; } events; - struct wl_listener display_destroy; - struct wl_listener layout_add; - struct wl_listener layout_change; - struct wl_listener layout_destroy; + struct { + struct wl_listener display_destroy; + struct wl_listener layout_add; + struct wl_listener layout_change; + struct wl_listener layout_destroy; + } WLR_PRIVATE; }; struct wlr_xdg_output_manager_v1 *wlr_xdg_output_manager_v1_create( diff --git a/include/wlr/types/wlr_xdg_shell.h b/include/wlr/types/wlr_xdg_shell.h index 723f8adf6..3e9fd2b70 100644 --- a/include/wlr/types/wlr_xdg_shell.h +++ b/include/wlr/types/wlr_xdg_shell.h @@ -22,8 +22,6 @@ struct wlr_xdg_shell { struct wl_list popup_grabs; uint32_t ping_timeout; - struct wl_listener display_destroy; - struct { struct wl_signal new_surface; // struct wlr_xdg_surface struct wl_signal new_toplevel; // struct wlr_xdg_toplevel @@ -32,6 +30,10 @@ struct wlr_xdg_shell { } events; void *data; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_xdg_client { @@ -124,7 +126,10 @@ struct wlr_xdg_popup_grab { struct wlr_seat *seat; struct wl_list popups; struct wl_list link; // wlr_xdg_shell.popup_grabs - struct wl_listener seat_destroy; + + struct { + struct wl_listener seat_destroy; + } WLR_PRIVATE; }; enum wlr_xdg_surface_role { @@ -176,7 +181,10 @@ struct wlr_xdg_toplevel_configure { struct wlr_xdg_toplevel_requested { bool maximized, minimized, fullscreen; struct wlr_output *fullscreen_output; - struct wl_listener fullscreen_output_destroy; + + struct { + struct wl_listener fullscreen_output_destroy; + } WLR_PRIVATE; }; struct wlr_xdg_toplevel { @@ -184,7 +192,6 @@ struct wlr_xdg_toplevel { struct wlr_xdg_surface *base; struct wlr_xdg_toplevel *parent; - struct wl_listener parent_unmap; struct wlr_xdg_toplevel_state current, pending; @@ -223,6 +230,8 @@ struct wlr_xdg_toplevel { struct { struct wlr_surface_synced synced; + + struct wl_listener parent_unmap; } WLR_PRIVATE; }; diff --git a/include/wlr/xwayland/server.h b/include/wlr/xwayland/server.h index fa16a3b00..b699b8f4f 100644 --- a/include/wlr/xwayland/server.h +++ b/include/wlr/xwayland/server.h @@ -48,10 +48,12 @@ struct wlr_xwayland_server { struct wl_signal destroy; } events; - struct wl_listener client_destroy; - struct wl_listener display_destroy; - void *data; + + struct { + struct wl_listener client_destroy; + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_xwayland_server_ready_event { diff --git a/include/wlr/xwayland/xwayland.h b/include/wlr/xwayland/xwayland.h index 339aef0ec..dbd881fda 100644 --- a/include/wlr/xwayland/xwayland.h +++ b/include/wlr/xwayland/xwayland.h @@ -133,9 +133,6 @@ struct wlr_xwayland_surface { struct wlr_surface *surface; struct wlr_addon surface_addon; - struct wl_listener surface_commit; - struct wl_listener surface_map; - struct wl_listener surface_unmap; int16_t x, y; uint16_t width, height; @@ -216,6 +213,12 @@ struct wlr_xwayland_surface { } events; void *data; + + struct { + struct wl_listener surface_commit; + struct wl_listener surface_map; + struct wl_listener surface_unmap; + } WLR_PRIVATE; }; struct wlr_xwayland_surface_configure_event { From 3df1528a8fba3109a41c58f5434425d2914cd2ae Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 28 Oct 2024 17:53:12 +0100 Subject: [PATCH 158/519] backend/drm: Store viewport with framebuffer Accessing the output state viewport require a buffer, and that might not have a state with a buffer when preparing the plane properties for an atomic commit. Instead, store the properties at the same time as the fb, and use a similar mechanism to carry the state around. --- backend/drm/atomic.c | 10 ++-------- backend/drm/drm.c | 7 +++++++ include/backend/drm/drm.h | 8 ++++++++ 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index b52230f90..143f37a65 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -439,15 +439,9 @@ static void atomic_connector_add(struct atomic *atom, if (crtc->props.vrr_enabled != 0) { atomic_add(atom, crtc->id, crtc->props.vrr_enabled, state->vrr_enabled); } - if (state->base->committed & WLR_OUTPUT_STATE_BUFFER) { - struct wlr_fbox src_box; - struct wlr_box dst_box; - output_state_get_buffer_src_box(state->base, &src_box); - output_state_get_buffer_dst_box(state->base, &dst_box); - set_plane_props(atom, drm, crtc->primary, state->primary_fb, crtc->id, - &dst_box, &src_box); - } + set_plane_props(atom, drm, crtc->primary, state->primary_fb, crtc->id, + &state->primary_viewport.dst_box, &state->primary_viewport.src_box); if (crtc->primary->props.fb_damage_clips != 0) { atomic_add(atom, crtc->primary->id, crtc->primary->props.fb_damage_clips, state->fb_damage_clips); diff --git a/backend/drm/drm.c b/backend/drm/drm.c index d8724c02a..d70492af1 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -557,6 +557,7 @@ static void drm_connector_apply_commit(const struct wlr_drm_connector_state *sta struct wlr_drm_crtc *crtc = conn->crtc; drm_fb_copy(&crtc->primary->queued_fb, state->primary_fb); + crtc->primary->queued_viewport = state->primary_viewport; if (crtc->cursor != NULL) { drm_fb_copy(&crtc->cursor->queued_fb, state->cursor_fb); } @@ -673,8 +674,10 @@ static void drm_connector_state_init(struct wlr_drm_connector_state *state, struct wlr_drm_plane *primary = conn->crtc->primary; if (primary->queued_fb != NULL) { state->primary_fb = drm_fb_lock(primary->queued_fb); + state->primary_viewport = primary->queued_viewport; } else if (primary->current_fb != NULL) { state->primary_fb = drm_fb_lock(primary->current_fb); + state->primary_viewport = primary->current_viewport; } if (conn->cursor_enabled) { @@ -761,6 +764,9 @@ static bool drm_connector_state_update_primary_fb(struct wlr_drm_connector *conn return false; } + output_state_get_buffer_src_box(state->base, &state->primary_viewport.src_box); + output_state_get_buffer_dst_box(state->base, &state->primary_viewport.dst_box); + return true; } @@ -2058,6 +2064,7 @@ static void handle_page_flip(int fd, unsigned seq, struct wlr_drm_plane *plane = conn->crtc->primary; if (plane->queued_fb) { drm_fb_move(&plane->current_fb, &plane->queued_fb); + plane->current_viewport = plane->queued_viewport; } if (conn->crtc->cursor && conn->crtc->cursor->queued_fb) { drm_fb_move(&conn->crtc->cursor->current_fb, diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index 5b239a18e..3ef83e3e0 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -15,6 +15,11 @@ #include "backend/drm/properties.h" #include "backend/drm/renderer.h" +struct wlr_drm_viewport { + struct wlr_fbox src_box; + struct wlr_box dst_box; +}; + struct wlr_drm_plane { uint32_t type; uint32_t id; @@ -24,8 +29,10 @@ struct wlr_drm_plane { /* Buffer submitted to the kernel, will be presented on next vblank */ struct wlr_drm_fb *queued_fb; + struct wlr_drm_viewport queued_viewport; /* Buffer currently displayed on screen */ struct wlr_drm_fb *current_fb; + struct wlr_drm_viewport current_viewport; struct wlr_drm_format_set formats; @@ -139,6 +146,7 @@ struct wlr_drm_connector_state { bool active; drmModeModeInfo mode; struct wlr_drm_fb *primary_fb; + struct wlr_drm_viewport primary_viewport; struct wlr_drm_fb *cursor_fb; struct wlr_drm_syncobj_timeline *wait_timeline; From 0f255b46fcebceafb3e426c67777afe11a82fe1f Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 28 Oct 2024 12:51:27 +0100 Subject: [PATCH 159/519] backend/drm: Remove automatic reset on VT switch Instead of trying to restore the drm state when the session is activated again, just disconnect all outputs when the session is deactivated. The scan that triggers on session activation will rediscover the connectors. --- backend/drm/backend.c | 9 +++- backend/drm/drm.c | 102 -------------------------------------- include/backend/drm/drm.h | 1 - 3 files changed, 8 insertions(+), 104 deletions(-) diff --git a/backend/drm/backend.c b/backend/drm/backend.c index 7f93e7550..f758eb6ed 100644 --- a/backend/drm/backend.c +++ b/backend/drm/backend.c @@ -117,11 +117,18 @@ static void handle_session_active(struct wl_listener *listener, void *data) { wlr_log(WLR_INFO, "DRM FD %s", session->active ? "resumed" : "paused"); if (!session->active) { + // Disconnect any active connectors so that the client will modeset and + // rerender when the session is activated again. + struct wlr_drm_connector *conn; + wl_list_for_each(conn, &drm->connectors, link) { + if (conn->status == DRM_MODE_CONNECTED) { + wlr_output_destroy(&conn->output); + } + } return; } scan_drm_connectors(drm, NULL); - restore_drm_device(drm); } static void handle_dev_change(struct wl_listener *listener, void *data) { diff --git a/backend/drm/drm.c b/backend/drm/drm.c index d70492af1..dcd9f3af1 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -1851,108 +1851,6 @@ void scan_drm_leases(struct wlr_drm_backend *drm) { drmFree(list); } -static void build_current_connector_state(struct wlr_output_state *state, - struct wlr_drm_connector *conn) { - bool enabled = conn->status != DRM_MODE_DISCONNECTED && conn->output.enabled; - - wlr_output_state_init(state); - wlr_output_state_set_enabled(state, enabled); - if (!enabled) { - return; - } - - if (conn->output.current_mode != NULL) { - wlr_output_state_set_mode(state, conn->output.current_mode); - } else { - wlr_output_state_set_custom_mode(state, - conn->output.width, conn->output.height, conn->output.refresh); - } -} - -/** - * Check whether we need to perform a full reset after a VT switch. - * - * If any connector or plane has a different CRTC, we need to perform a full - * reset to restore our mapping. We couldn't avoid a full reset even if we - * used a single KMS atomic commit to apply our state: the kernel rejects - * commits which migrate a plane from one CRTC to another without going through - * an intermediate state where the plane is disabled. - */ -static bool skip_reset_for_restore(struct wlr_drm_backend *drm) { - struct wlr_drm_connector *conn; - wl_list_for_each(conn, &drm->connectors, link) { - drmModeConnector *drm_conn = drmModeGetConnectorCurrent(drm->fd, conn->id); - if (drm_conn == NULL) { - return false; - } - struct wlr_drm_crtc *crtc = connector_get_current_crtc(conn, drm_conn); - drmModeFreeConnector(drm_conn); - - if (crtc != NULL && conn->crtc != crtc) { - return false; - } - } - - for (size_t i = 0; i < drm->num_planes; i++) { - struct wlr_drm_plane *plane = &drm->planes[i]; - - drmModePlane *drm_plane = drmModeGetPlane(drm->fd, plane->id); - if (drm_plane == NULL) { - return false; - } - uint32_t crtc_id = drm_plane->crtc_id; - drmModeFreePlane(drm_plane); - - struct wlr_drm_crtc *crtc = NULL; - for (size_t i = 0; i < drm->num_crtcs; i++) { - if (drm->crtcs[i].id == crtc_id) { - crtc = &drm->crtcs[i]; - break; - } - } - if (crtc == NULL) { - continue; - } - - bool ok = false; - switch (plane->type) { - case DRM_PLANE_TYPE_PRIMARY: - ok = crtc->primary == plane; - break; - case DRM_PLANE_TYPE_CURSOR: - ok = crtc->cursor == plane; - break; - } - if (!ok) { - return false; - } - } - - return true; -} - -void restore_drm_device(struct wlr_drm_backend *drm) { - // The previous DRM master leaves KMS in an undefined state. We need - // to restore our own state, but be careful to avoid invalid - // configurations. The connector/CRTC mapping may have changed, so - // first disable all CRTCs, then light up the ones we were using - // before the VT switch. - // TODO: better use the atomic API to improve restoration after a VT switch - if (!skip_reset_for_restore(drm) && !drm->iface->reset(drm)) { - wlr_log(WLR_ERROR, "Failed to reset state after VT switch"); - } - - struct wlr_drm_connector *conn; - wl_list_for_each(conn, &drm->connectors, link) { - struct wlr_output_state state; - build_current_connector_state(&state, conn); - if (!drm_connector_commit_state(conn, &state, false)) { - wlr_drm_conn_log(conn, WLR_ERROR, "Failed to restore state after VT switch"); - } - wlr_output_state_finish(&state); - } -} - bool commit_drm_device(struct wlr_drm_backend *drm, const struct wlr_backend_output_state *output_states, size_t output_states_len, bool test_only) { diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index 3ef83e3e0..9d3d62272 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -225,7 +225,6 @@ void scan_drm_connectors(struct wlr_drm_backend *state, void scan_drm_leases(struct wlr_drm_backend *drm); bool commit_drm_device(struct wlr_drm_backend *drm, const struct wlr_backend_output_state *states, size_t states_len, bool test_only); -void restore_drm_device(struct wlr_drm_backend *drm); int handle_drm_event(int fd, uint32_t mask, void *data); void destroy_drm_connector(struct wlr_drm_connector *conn); bool drm_connector_is_cursor_visible(struct wlr_drm_connector *conn); From 1edd5e224f3ff616fe67e1472d34f6b2d8697359 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 29 Oct 2024 11:13:13 +0100 Subject: [PATCH 160/519] backend/drm: Remove reset from interface The reset implementations are no longer used. --- backend/drm/atomic.c | 27 --------------------------- backend/drm/legacy.c | 14 -------------- backend/drm/libliftoff.c | 1 - include/backend/drm/iface.h | 2 -- 4 files changed, 44 deletions(-) diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index 143f37a65..22ce23821 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -527,33 +527,6 @@ out: return ok; } -bool drm_atomic_reset(struct wlr_drm_backend *drm) { - struct atomic atom; - atomic_begin(&atom); - - for (size_t i = 0; i < drm->num_crtcs; i++) { - struct wlr_drm_crtc *crtc = &drm->crtcs[i]; - atomic_add(&atom, crtc->id, crtc->props.mode_id, 0); - atomic_add(&atom, crtc->id, crtc->props.active, 0); - } - - struct wlr_drm_connector *conn; - wl_list_for_each(conn, &drm->connectors, link) { - atomic_add(&atom, conn->id, conn->props.crtc_id, 0); - } - - for (size_t i = 0; i < drm->num_planes; i++) { - plane_disable(&atom, &drm->planes[i]); - } - - uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET; - bool ok = atomic_commit(&atom, drm, NULL, NULL, flags); - atomic_finish(&atom); - - return ok; -} - const struct wlr_drm_interface atomic_iface = { .commit = atomic_device_commit, - .reset = drm_atomic_reset, }; diff --git a/backend/drm/legacy.c b/backend/drm/legacy.c index 371418c6f..dddf0b1fd 100644 --- a/backend/drm/legacy.c +++ b/backend/drm/legacy.c @@ -270,20 +270,6 @@ bool drm_legacy_crtc_set_gamma(struct wlr_drm_backend *drm, return true; } -static bool legacy_reset(struct wlr_drm_backend *drm) { - bool ok = true; - for (size_t i = 0; i < drm->num_crtcs; i++) { - struct wlr_drm_crtc *crtc = &drm->crtcs[i]; - if (drmModeSetCrtc(drm->fd, crtc->id, 0, 0, 0, NULL, 0, NULL) != 0) { - wlr_log_errno(WLR_ERROR, "Failed to disable CRTC %"PRIu32, - crtc->id); - ok = false; - } - } - return ok; -} - const struct wlr_drm_interface legacy_iface = { .commit = legacy_commit, - .reset = legacy_reset, }; diff --git a/backend/drm/libliftoff.c b/backend/drm/libliftoff.c index 297528488..67b1ca527 100644 --- a/backend/drm/libliftoff.c +++ b/backend/drm/libliftoff.c @@ -508,5 +508,4 @@ const struct wlr_drm_interface liftoff_iface = { .init = init, .finish = finish, .commit = commit, - .reset = drm_atomic_reset, }; diff --git a/include/backend/drm/iface.h b/include/backend/drm/iface.h index 1ac1095a8..bc9610589 100644 --- a/include/backend/drm/iface.h +++ b/include/backend/drm/iface.h @@ -22,8 +22,6 @@ struct wlr_drm_interface { bool (*commit)(struct wlr_drm_backend *drm, const struct wlr_drm_device_state *state, struct wlr_drm_page_flip *page_flip, uint32_t flags, bool test_only); - // Turn off everything - bool (*reset)(struct wlr_drm_backend *drm); }; extern const struct wlr_drm_interface atomic_iface; From c1ce983826c86e9405d8896a0a2f15cf15c4e74a Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 29 Oct 2024 11:18:48 +0100 Subject: [PATCH 161/519] backend/drm: Store only a single plane viewport We store both queued and current buffers to be able to retain both the framebuffer currently on screen and the one queued to replace it. From a re-use perspective, we only care about the last committed framebuffer. The viewport is only stored in order to be re-used together with the last committed framebuffer, so do away with the queued/current distinction and store a single viewport updated every time a commit completes. --- backend/drm/drm.c | 7 +++---- include/backend/drm/drm.h | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index dcd9f3af1..f34c9a085 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -557,7 +557,7 @@ static void drm_connector_apply_commit(const struct wlr_drm_connector_state *sta struct wlr_drm_crtc *crtc = conn->crtc; drm_fb_copy(&crtc->primary->queued_fb, state->primary_fb); - crtc->primary->queued_viewport = state->primary_viewport; + crtc->primary->viewport = state->primary_viewport; if (crtc->cursor != NULL) { drm_fb_copy(&crtc->cursor->queued_fb, state->cursor_fb); } @@ -674,10 +674,10 @@ static void drm_connector_state_init(struct wlr_drm_connector_state *state, struct wlr_drm_plane *primary = conn->crtc->primary; if (primary->queued_fb != NULL) { state->primary_fb = drm_fb_lock(primary->queued_fb); - state->primary_viewport = primary->queued_viewport; + state->primary_viewport = primary->viewport; } else if (primary->current_fb != NULL) { state->primary_fb = drm_fb_lock(primary->current_fb); - state->primary_viewport = primary->current_viewport; + state->primary_viewport = primary->viewport; } if (conn->cursor_enabled) { @@ -1962,7 +1962,6 @@ static void handle_page_flip(int fd, unsigned seq, struct wlr_drm_plane *plane = conn->crtc->primary; if (plane->queued_fb) { drm_fb_move(&plane->current_fb, &plane->queued_fb); - plane->current_viewport = plane->queued_viewport; } if (conn->crtc->cursor && conn->crtc->cursor->queued_fb) { drm_fb_move(&conn->crtc->cursor->current_fb, diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index 9d3d62272..9409b7b10 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -29,10 +29,10 @@ struct wlr_drm_plane { /* Buffer submitted to the kernel, will be presented on next vblank */ struct wlr_drm_fb *queued_fb; - struct wlr_drm_viewport queued_viewport; /* Buffer currently displayed on screen */ struct wlr_drm_fb *current_fb; - struct wlr_drm_viewport current_viewport; + /* Viewport belonging to the last committed fb */ + struct wlr_drm_viewport viewport; struct wlr_drm_format_set formats; From ebab992b44848120346b0fc67e79170acb9eb9be Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 29 Oct 2024 15:15:41 -0400 Subject: [PATCH 162/519] wlr_scene: Add notes about wlr_scene_rect taking premultiplied colors --- include/wlr/types/wlr_scene.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index dbca35e78..0e3e7750a 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -404,6 +404,8 @@ struct wlr_scene_surface *wlr_scene_surface_try_from_buffer( /** * Add a node displaying a solid-colored rectangle to the scene-graph. + * + * The color argument must be a premultiplied color value. */ struct wlr_scene_rect *wlr_scene_rect_create(struct wlr_scene_tree *parent, int width, int height, const float color[static 4]); @@ -415,6 +417,8 @@ void wlr_scene_rect_set_size(struct wlr_scene_rect *rect, int width, int height) /** * Change the color of an existing rectangle node. + * + * The color argument must be a premultiplied color value. */ void wlr_scene_rect_set_color(struct wlr_scene_rect *rect, const float color[static 4]); From d835fa813f82501eb668708af33afc42010707cf Mon Sep 17 00:00:00 2001 From: llyyr Date: Tue, 29 Oct 2024 09:56:42 +0530 Subject: [PATCH 163/519] backend/multi: only consider backends with dmabuf cap for timeline timeline feature will never be applicable to backends without it, so don't check it. Before this commit, it would cause timeline to be set to false if libinput ended up being the last item in the list (which is the case currently). --- backend/multi/backend.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/multi/backend.c b/backend/multi/backend.c index 239524f16..bcb8a4f0c 100644 --- a/backend/multi/backend.c +++ b/backend/multi/backend.c @@ -232,8 +232,11 @@ static void multi_backend_refresh_features(struct wlr_multi_backend *multi) { struct subbackend_state *sub = NULL; wl_list_for_each(sub, &multi->backends, link) { - multi->backend.features.timeline = multi->backend.features.timeline && - sub->backend->features.timeline; + // timeline is only applicable to backends that support DMABUFs + if (backend_get_buffer_caps(sub->backend) & WLR_BUFFER_CAP_DMABUF) { + multi->backend.features.timeline = multi->backend.features.timeline && + sub->backend->features.timeline; + } } } From 3ca4bc8c096f53466ee414c115cff2c7b39323ef Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Fri, 11 Oct 2024 13:41:41 +0300 Subject: [PATCH 164/519] meson: fix file order --- types/meson.build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/types/meson.build b/types/meson.build index ec70d4b7c..8753b8a0c 100644 --- a/types/meson.build +++ b/types/meson.build @@ -44,6 +44,7 @@ wlr_files += files( 'wlr_export_dmabuf_v1.c', 'wlr_foreign_toplevel_management_v1.c', 'wlr_ext_foreign_toplevel_list_v1.c', + 'wlr_fractional_scale_v1.c', 'wlr_fullscreen_shell_v1.c', 'wlr_gamma_control_v1.c', 'wlr_idle_inhibit_v1.c', @@ -77,10 +78,10 @@ wlr_files += files( 'wlr_shm.c', 'wlr_single_pixel_buffer_v1.c', 'wlr_subcompositor.c', - 'wlr_fractional_scale_v1.c', 'wlr_switch.c', 'wlr_tablet_pad.c', 'wlr_tablet_tool.c', + 'wlr_tearing_control_v1.c', 'wlr_text_input_v3.c', 'wlr_touch.c', 'wlr_transient_seat_v1.c', @@ -94,7 +95,6 @@ wlr_files += files( 'wlr_xdg_foreign_v2.c', 'wlr_xdg_foreign_registry.c', 'wlr_xdg_output_v1.c', - 'wlr_tearing_control_v1.c', ) if features.get('drm-backend') From ea93dd5cc394042f0284b9ca7f1a22e1b992147c Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Fri, 11 Oct 2024 13:50:46 +0300 Subject: [PATCH 165/519] xdg-system-bell-v1: add implementation --- include/wlr/types/wlr_xdg_system_bell_v1.h | 34 +++++++++ protocol/meson.build | 1 + types/wlr_xdg_system_bell_v1.c | 81 ++++++++++++++++++++++ 3 files changed, 116 insertions(+) create mode 100644 include/wlr/types/wlr_xdg_system_bell_v1.h create mode 100644 types/wlr_xdg_system_bell_v1.c diff --git a/include/wlr/types/wlr_xdg_system_bell_v1.h b/include/wlr/types/wlr_xdg_system_bell_v1.h new file mode 100644 index 000000000..8cb592d14 --- /dev/null +++ b/include/wlr/types/wlr_xdg_system_bell_v1.h @@ -0,0 +1,34 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_TYPES_WLR_XDG_SYSTEM_BELL_V1_H +#define WLR_TYPES_WLR_XDG_SYSTEM_BELL_V1_H + +#include + +struct wlr_xdg_system_bell_v1 { + struct wl_global *global; + + struct { + struct wl_signal ring; // struct wlr_xdg_system_bell_v1_ring_event + } events; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; +}; + +struct wlr_xdg_system_bell_v1_ring_event { + struct wl_client *client; + struct wlr_surface *surface; // May be NULL +}; + +struct wlr_xdg_system_bell_v1 *wlr_xdg_system_bell_v1_create(struct wl_display *display, + uint32_t version); + +#endif diff --git a/protocol/meson.build b/protocol/meson.build index 3e385d4ec..f4aafaf57 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -32,6 +32,7 @@ protocols = { 'security-context-v1': wl_protocol_dir / 'staging/security-context/security-context-v1.xml', 'single-pixel-buffer-v1': wl_protocol_dir / 'staging/single-pixel-buffer/single-pixel-buffer-v1.xml', 'xdg-activation-v1': wl_protocol_dir / 'staging/xdg-activation/xdg-activation-v1.xml', + 'xdg-system-bell-v1': wl_protocol_dir / 'staging/xdg-system-bell/xdg-system-bell-v1.xml', 'xwayland-shell-v1': wl_protocol_dir / 'staging/xwayland-shell/xwayland-shell-v1.xml', 'tearing-control-v1': wl_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml', diff --git a/types/wlr_xdg_system_bell_v1.c b/types/wlr_xdg_system_bell_v1.c new file mode 100644 index 000000000..d1ad7b002 --- /dev/null +++ b/types/wlr_xdg_system_bell_v1.c @@ -0,0 +1,81 @@ +#include +#include +#include +#include +#include "xdg-system-bell-v1-protocol.h" + +#define XDG_SYSTEM_BELL_V1_VERSION 1 + +static const struct xdg_system_bell_v1_interface bell_impl; + +static struct wlr_xdg_system_bell_v1 *bell_from_resource(struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &xdg_system_bell_v1_interface, &bell_impl)); + return wl_resource_get_user_data(resource); +} + +static void bell_handle_destroy(struct wl_client *client, struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static void bell_handle_ring(struct wl_client *client, + struct wl_resource *bell_resource, struct wl_resource *surface_resource) { + struct wlr_xdg_system_bell_v1 *bell = bell_from_resource(bell_resource); + + struct wlr_surface *surface = NULL; + if (surface_resource != NULL) { + surface = wlr_surface_from_resource(surface_resource); + } + + struct wlr_xdg_system_bell_v1_ring_event event = { + .client = client, + .surface = surface, + }; + wl_signal_emit_mutable(&bell->events.ring, &event); +} + +static const struct xdg_system_bell_v1_interface bell_impl = { + .destroy = bell_handle_destroy, + .ring = bell_handle_ring, +}; + +static void bell_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id) { + struct wl_resource *resource = wl_resource_create(client, + &xdg_system_bell_v1_interface, version, id); + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &bell_impl, NULL, NULL); +} + +static void handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_xdg_system_bell_v1 *bell = wl_container_of(listener, bell, display_destroy); + wl_list_remove(&bell->display_destroy.link); + wl_global_destroy(bell->global); + free(bell); +} + +struct wlr_xdg_system_bell_v1 *wlr_xdg_system_bell_v1_create(struct wl_display *display, + uint32_t version) { + assert(version <= XDG_SYSTEM_BELL_V1_VERSION); + + struct wlr_xdg_system_bell_v1 *bell = calloc(1, sizeof(*bell)); + if (bell == NULL) { + return NULL; + } + + bell->global = wl_global_create(display, &xdg_system_bell_v1_interface, + version, NULL, bell_bind); + if (bell->global == NULL) { + free(bell); + return NULL; + } + + bell->display_destroy.notify = handle_display_destroy; + wl_display_add_destroy_listener(display, &bell->display_destroy); + + wl_signal_init(&bell->events.ring); + + return bell; +} From 3fdbfb0be82224d472ad6de3a91813064f4cd4b2 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 4 Nov 2024 19:05:38 +0100 Subject: [PATCH 166/519] buffer: add more docs --- include/wlr/types/wlr_buffer.h | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/include/wlr/types/wlr_buffer.h b/include/wlr/types/wlr_buffer.h index 874edf82b..fadc560a3 100644 --- a/include/wlr/types/wlr_buffer.h +++ b/include/wlr/types/wlr_buffer.h @@ -17,11 +17,15 @@ struct wlr_buffer; struct wlr_renderer; +/** + * Shared-memory attributes for a buffer. + */ struct wlr_shm_attributes { int fd; - uint32_t format; - int width, height, stride; - off_t offset; + uint32_t format; // FourCC code, see DRM_FORMAT_* in + int width, height; + int stride; // Number of bytes between consecutive pixel lines + off_t offset; // Offset in bytes of the first pixel in FD }; /** @@ -130,6 +134,12 @@ enum wlr_buffer_data_ptr_access_flag { */ bool wlr_buffer_begin_data_ptr_access(struct wlr_buffer *buffer, uint32_t flags, void **data, uint32_t *format, size_t *stride); +/** + * Indicate that a pointer to a buffer's underlying memory will no longer be + * used. + * + * This function must be called after wlr_buffer_begin_data_ptr_access(). + */ void wlr_buffer_end_data_ptr_access(struct wlr_buffer *buffer); /** From c5d8f6d1878ad92db0729d3fbfa2e78cf7d1fe57 Mon Sep 17 00:00:00 2001 From: mmcomando Date: Wed, 6 Nov 2024 08:52:58 +0100 Subject: [PATCH 167/519] Consider using vulkan renderer in default configuration With this change vulkan renderer can be automatically chosen in two more cases: GLES2 renderer is disabled at compile time GLES2 renderer failed to be created Main purpose of this change is to automatically choose vulkan as renderer when GLES2 renderer is not enabled. --- render/wlr_renderer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/render/wlr_renderer.c b/render/wlr_renderer.c index a580ab56d..bb7bbbeb9 100644 --- a/render/wlr_renderer.c +++ b/render/wlr_renderer.c @@ -241,7 +241,7 @@ static struct wlr_renderer *renderer_autocreate(struct wlr_backend *backend, int } } - if (strcmp(renderer_name, "vulkan") == 0) { + if ((is_auto && WLR_HAS_VULKAN_RENDERER) || strcmp(renderer_name, "vulkan") == 0) { if (!open_preferred_drm_fd(backend, &drm_fd, &own_drm_fd)) { log_creation_failure(is_auto, "Cannot create Vulkan renderer: no DRM FD available"); } else { From 8bb6935374769ba8dff0713761b914101579cb91 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 7 Nov 2024 10:09:36 +0100 Subject: [PATCH 168/519] tablet-v2: Event time should be milliseconds The event time used for zwp_tablet_pad_group_v2_send_mode_switch was tv_nsec / 1000, which is microseconds resetting every whole second. Use get_current_time_msec to get milliseconds including whole seconds. --- types/tablet_v2/wlr_tablet_v2_pad.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/types/tablet_v2/wlr_tablet_v2_pad.c b/types/tablet_v2/wlr_tablet_v2_pad.c index 052a017d8..2e0439a36 100644 --- a/types/tablet_v2/wlr_tablet_v2_pad.c +++ b/types/tablet_v2/wlr_tablet_v2_pad.c @@ -7,6 +7,7 @@ #include #include #include +#include "util/time.h" #include "tablet-v2-protocol.h" static const struct wlr_tablet_pad_v2_grab_interface default_pad_grab_interface; @@ -457,10 +458,7 @@ uint32_t wlr_send_tablet_v2_tablet_pad_enter( zwp_tablet_pad_v2_send_enter(pad_client->resource, serial, tablet_client->resource, surface->resource); - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - uint32_t time = now.tv_nsec / 1000; - + uint32_t time = get_current_time_msec(); for (size_t i = 0; i < pad->group_count; ++i) { if (pad_client->groups[i]) { zwp_tablet_pad_group_v2_send_mode_switch( From d6b47c3ab0b54443eebf6604f0b09ccb83bf2763 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 7 Nov 2024 10:22:10 +0100 Subject: [PATCH 169/519] wlr_keyboard_group: Use get_current_time_msec --- types/wlr_keyboard_group.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/types/wlr_keyboard_group.c b/types/wlr_keyboard_group.c index ef557a958..25ef66695 100644 --- a/types/wlr_keyboard_group.c +++ b/types/wlr_keyboard_group.c @@ -10,6 +10,7 @@ #include "wlr/types/wlr_keyboard.h" #include "wlr/types/wlr_keyboard_group.h" #include "wlr/util/log.h" +#include "util/time.h" struct keyboard_group_device { struct wlr_keyboard *keyboard; @@ -183,10 +184,8 @@ static void refresh_state(struct keyboard_group_device *device, wl_array_init(&keys); for (size_t i = 0; i < device->keyboard->num_keycodes; i++) { - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); struct wlr_keyboard_key_event event = { - .time_msec = (int64_t)now.tv_sec * 1000 + now.tv_nsec / 1000000, + .time_msec = get_current_time_msec(), .keycode = device->keyboard->keycodes[i], .update_state = true, .state = state From 0d728f96b7042633aff21c3208e1481e17b9442e Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 7 Nov 2024 10:22:56 +0100 Subject: [PATCH 170/519] util/time: Move NSEC_PER_SEC to header This constant can be useful whenever one has to deal with timespecs, so let's move it to the header. --- include/util/time.h | 2 ++ util/time.c | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/util/time.h b/include/util/time.h index 3f76aa47f..dd164bf2f 100644 --- a/include/util/time.h +++ b/include/util/time.h @@ -4,6 +4,8 @@ #include #include +static const long NSEC_PER_SEC = 1000000000; + /** * Get the current time, in milliseconds. */ diff --git a/util/time.c b/util/time.c index bc4a10637..1a8f32969 100644 --- a/util/time.c +++ b/util/time.c @@ -3,8 +3,6 @@ #include "util/time.h" -static const long NSEC_PER_SEC = 1000000000; - int64_t timespec_to_msec(const struct timespec *a) { return (int64_t)a->tv_sec * 1000 + a->tv_nsec / 1000000; } From 1f13bc72fea2ad9bb82909c761d4ecd35cf79f1a Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 4 Nov 2024 22:56:16 +0100 Subject: [PATCH 171/519] render/vulkan: Garbage collect unused buffers Perform a primitive garbage collection of buffers that have not been used in the past 10 seconds, an arbitrarily selected number. As garbage collection also makes span buffer allocation happen much more often, logging on allocation activity leads to a lot of log noise so get rid of that while at it. --- include/render/vulkan.h | 1 + render/vulkan/renderer.c | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/include/render/vulkan.h b/include/render/vulkan.h index 7ad2f8339..abeb11cc5 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -500,6 +500,7 @@ struct wlr_vk_shared_buffer { VkDeviceSize buf_size; void *cpu_mapping; struct wl_array allocs; // struct wlr_vk_allocation + int64_t last_used_ms; }; // Suballocated range on a buffer. diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index a2fb867d6..44eb89f96 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -28,6 +28,7 @@ #include "render/vulkan/shaders/output.frag.h" #include "types/wlr_buffer.h" #include "types/wlr_matrix.h" +#include "util/time.h" // TODO: // - simplify stage allocation, don't track allocations but use ringbuffer-like @@ -322,7 +323,6 @@ struct wlr_vk_buffer_span vulkan_get_stage_span(struct wlr_vk_renderer *r, goto error; } - wlr_log(WLR_DEBUG, "Created new vk staging buffer of size %" PRIu64, bsize); buf->buf_size = bsize; wl_list_insert(&r->stage.buffers, &buf->link); @@ -456,7 +456,7 @@ bool vulkan_wait_command_buffer(struct wlr_vk_command_buffer *cb, } static void release_command_buffer_resources(struct wlr_vk_command_buffer *cb, - struct wlr_vk_renderer *renderer) { + struct wlr_vk_renderer *renderer, int64_t now) { struct wlr_vk_texture *texture, *texture_tmp; wl_list_for_each_safe(texture, texture_tmp, &cb->destroy_textures, destroy_link) { wl_list_remove(&texture->destroy_link); @@ -467,6 +467,7 @@ static void release_command_buffer_resources(struct wlr_vk_command_buffer *cb, struct wlr_vk_shared_buffer *buf, *buf_tmp; wl_list_for_each_safe(buf, buf_tmp, &cb->stage_buffers, link) { buf->allocs.size = 0; + buf->last_used_ms = now; wl_list_remove(&buf->link); wl_list_insert(&renderer->stage.buffers, &buf->link); @@ -490,12 +491,22 @@ static struct wlr_vk_command_buffer *get_command_buffer( return NULL; } + + // Garbage collect any buffers that have remained unused for too long + int64_t now = get_current_time_msec(); + struct wlr_vk_shared_buffer *buf, *buf_tmp; + wl_list_for_each_safe(buf, buf_tmp, &renderer->stage.buffers, link) { + if (buf->allocs.size == 0 && buf->last_used_ms + 10000 < now) { + shared_buffer_destroy(renderer, buf); + } + } + // Destroy textures for completed command buffers for (size_t i = 0; i < VULKAN_COMMAND_BUFFERS_CAP; i++) { struct wlr_vk_command_buffer *cb = &renderer->command_buffers[i]; if (cb->vk != VK_NULL_HANDLE && !cb->recording && cb->timeline_point <= current_point) { - release_command_buffer_resources(cb, renderer); + release_command_buffer_resources(cb, renderer, now); } } @@ -1055,7 +1066,7 @@ static void vulkan_destroy(struct wlr_renderer *wlr_renderer) { if (cb->vk == VK_NULL_HANDLE) { continue; } - release_command_buffer_resources(cb, renderer); + release_command_buffer_resources(cb, renderer, 0); if (cb->binary_semaphore != VK_NULL_HANDLE) { vkDestroySemaphore(renderer->dev->dev, cb->binary_semaphore, NULL); } From 2c098a3e4523acdd12de7728ea8f2a05e5eebfeb Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 7 Nov 2024 10:53:38 +0100 Subject: [PATCH 172/519] build: rename meson_options.txt to meson.options This is the preferred name since Meson 1.1. --- meson_options.txt => meson.options | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename meson_options.txt => meson.options (100%) diff --git a/meson_options.txt b/meson.options similarity index 100% rename from meson_options.txt rename to meson.options From cda6fdffacb2cb20a825f8633895ecb36b3cb1a7 Mon Sep 17 00:00:00 2001 From: xurui Date: Wed, 23 Oct 2024 18:10:53 +0800 Subject: [PATCH 173/519] layer-shell-v1: use enum_is_valid functions Signed-off-by: xurui --- types/wlr_layer_shell_v1.c | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/types/wlr_layer_shell_v1.c b/types/wlr_layer_shell_v1.c index 6df4b7a8e..d4893871c 100644 --- a/types/wlr_layer_shell_v1.c +++ b/types/wlr_layer_shell_v1.c @@ -148,12 +148,8 @@ static void layer_surface_handle_set_size(struct wl_client *client, static void layer_surface_handle_set_anchor(struct wl_client *client, struct wl_resource *resource, uint32_t anchor) { - const uint32_t max_anchor = - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; - if (anchor > max_anchor) { + uint32_t version = wl_resource_get_version(resource); + if (!zwlr_layer_surface_v1_anchor_is_valid(anchor, version)) { wl_resource_post_error(resource, ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_ANCHOR, "invalid anchor %" PRIu32, anchor); @@ -223,10 +219,11 @@ static void layer_surface_handle_set_keyboard_interactivity( } surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_KEYBOARD_INTERACTIVITY; - if (wl_resource_get_version(resource) < ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND_SINCE_VERSION) { + uint32_t version = wl_resource_get_version(resource); + if (version < ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND_SINCE_VERSION) { surface->pending.keyboard_interactive = !!interactive; } else { - if (interactive > ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND) { + if (!zwlr_layer_surface_v1_keyboard_interactivity_is_valid(interactive, version)) { wl_resource_post_error(resource, ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_KEYBOARD_INTERACTIVITY, "wrong keyboard interactivity value: %" PRIu32, interactive); @@ -263,7 +260,8 @@ static void layer_surface_set_layer(struct wl_client *client, if (!surface) { return; } - if (layer > ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY) { + uint32_t version = wl_resource_get_version(surface->resource); + if (!zwlr_layer_shell_v1_layer_is_valid(layer, version)) { // XXX: this sends a zwlr_layer_shell_v1 error to a zwlr_layer_surface_v1 object wl_resource_post_error(surface->resource, ZWLR_LAYER_SHELL_V1_ERROR_INVALID_LAYER, @@ -286,14 +284,8 @@ static void layer_surface_set_exclusive_edge(struct wl_client *client, if (!surface) { return; } - switch (edge) { - case 0: - case ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP: - case ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM: - case ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT: - case ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT: - break; - default: + uint32_t version = wl_resource_get_version(surface->resource); + if (!zwlr_layer_surface_v1_anchor_is_valid(edge, version)) { wl_resource_post_error(surface->resource, ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_EXCLUSIVE_EDGE, "invalid exclusive edge %" PRIu32, edge); @@ -450,7 +442,8 @@ static void layer_shell_handle_get_layer_surface(struct wl_client *wl_client, struct wlr_surface *wlr_surface = wlr_surface_from_resource(surface_resource); - if (layer > ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY) { + uint32_t version = wl_resource_get_version(client_resource); + if (!zwlr_layer_shell_v1_layer_is_valid(layer, version)) { wl_resource_post_error(client_resource, ZWLR_LAYER_SHELL_V1_ERROR_INVALID_LAYER, "Invalid layer %" PRIu32, layer); From 81fa6c4b96d598f98bb9eaa99863835c83ebde5c Mon Sep 17 00:00:00 2001 From: xurui Date: Wed, 23 Oct 2024 18:18:28 +0800 Subject: [PATCH 174/519] wlr_linux_dmabuf_v1: use enum_is_valid functions Signed-off-by: xurui --- types/wlr_linux_dmabuf_v1.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/types/wlr_linux_dmabuf_v1.c b/types/wlr_linux_dmabuf_v1.c index 77fe76ce9..7809a29d8 100644 --- a/types/wlr_linux_dmabuf_v1.c +++ b/types/wlr_linux_dmabuf_v1.c @@ -266,10 +266,8 @@ static void params_create_common(struct wl_resource *params_resource, } /* reject unknown flags */ - uint32_t all_flags = ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT | - ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_INTERLACED | - ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_BOTTOM_FIRST; - if (flags & ~all_flags) { + uint32_t version = wl_resource_get_version(params_resource); + if (!zwp_linux_buffer_params_v1_flags_is_valid(flags, version)) { wl_resource_post_error(params_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT, "Unknown dmabuf flags %"PRIu32, flags); From 38fc4f2976df8083cf7c7e807351ce32e7a5b723 Mon Sep 17 00:00:00 2001 From: xurui Date: Wed, 23 Oct 2024 18:20:28 +0800 Subject: [PATCH 175/519] wlr_output_management_v1: use enum_is_valid functions Signed-off-by: xurui --- types/wlr_output_management_v1.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/types/wlr_output_management_v1.c b/types/wlr_output_management_v1.c index ecc32dede..551453e6b 100644 --- a/types/wlr_output_management_v1.c +++ b/types/wlr_output_management_v1.c @@ -255,8 +255,7 @@ static void config_head_handle_set_transform(struct wl_client *client, return; } - if (transform < WL_OUTPUT_TRANSFORM_NORMAL || - transform > WL_OUTPUT_TRANSFORM_FLIPPED_270) { + if (!wl_output_transform_is_valid(transform, 1)) { wl_resource_post_error(config_head_resource, ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_TRANSFORM, "invalid transform"); From 3f314bc183a62f44fcc89bc2d33201932cb27f7d Mon Sep 17 00:00:00 2001 From: xurui Date: Thu, 24 Oct 2024 18:47:33 +0800 Subject: [PATCH 176/519] output-power-management-v1: use enum_is_valid functions Signed-off-by: xurui --- types/wlr_output_power_management_v1.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/types/wlr_output_power_management_v1.c b/types/wlr_output_power_management_v1.c index 96e0b6028..25b92e40d 100644 --- a/types/wlr_output_power_management_v1.c +++ b/types/wlr_output_power_management_v1.c @@ -77,11 +77,8 @@ static void output_power_handle_set_mode(struct wl_client *client, return; } - switch (mode) { - case ZWLR_OUTPUT_POWER_V1_MODE_OFF: - case ZWLR_OUTPUT_POWER_V1_MODE_ON: - break; - default: + uint32_t version = wl_resource_get_version(output_power_resource); + if (!zwlr_output_power_v1_mode_is_valid(mode, version)) { wlr_log(WLR_ERROR, "Invalid power mode %d", mode); wl_resource_post_error(output_power_resource, ZWLR_OUTPUT_POWER_V1_ERROR_INVALID_MODE, From 63fabecee2fd43f49488f5834d8715121366b434 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 7 Nov 2024 14:45:23 +0100 Subject: [PATCH 177/519] backend/wayland: Account for shm buffer offset If we are trying directly scan-out a shm buffer, we create a temporary buffer pool to instantiate a wl_buffer from, created in accordance to our buffers size. If the buffer has an offset, it will end up out of bounds of the created pool. Extend the temporary pool by the buffer offset to compensate. Matching the original pool size does matter unless we want to optimize away the temporary pool to reduce mappings in the parent compositor. --- backend/wayland/output.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/wayland/output.c b/backend/wayland/output.c index 5b8dfb0f2..d09b74352 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -197,7 +197,7 @@ static struct wl_buffer *import_shm(struct wlr_wl_backend *wl, struct wlr_shm_attributes *shm) { enum wl_shm_format wl_shm_format = convert_drm_format_to_wl_shm(shm->format); uint32_t size = shm->stride * shm->height; - struct wl_shm_pool *pool = wl_shm_create_pool(wl->shm, shm->fd, size); + struct wl_shm_pool *pool = wl_shm_create_pool(wl->shm, shm->fd, shm->offset + size); if (pool == NULL) { return NULL; } From 9e71c88467fc41d6330bbd4b99f82f3df49ed4ac Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 7 Nov 2024 11:56:09 +0100 Subject: [PATCH 178/519] scene: unwrap wlr_client_buffer for direct scan-out Passing the wlr_client_buffer directly has a downsides because a fresh wlr_buffer pointer is passed each output commit instead of cycling through existing wlr_buffer objects: - The FDs are re-imported each time in the backend. - Any import failure is logged every output commit [1]. - The Wayland backend cannot handle import failures without roundtripping each output commit [2]. Instead, extract the source buffer from the wlr_client_buffer and pass that to the backend. [1]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4836 [2]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4841 --- types/scene/wlr_scene.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 766189485..370be2b3a 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1872,7 +1872,13 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry, scene_node_get_size(node, &pending.buffer_dst_box.width, &pending.buffer_dst_box.height); transform_output_box(&pending.buffer_dst_box, data); - wlr_output_state_set_buffer(&pending, buffer->buffer); + struct wlr_buffer *wlr_buffer = buffer->buffer; + struct wlr_client_buffer *client_buffer = wlr_client_buffer_get(wlr_buffer); + if (client_buffer != NULL && client_buffer->source != NULL) { + wlr_buffer = client_buffer->source; + } + + wlr_output_state_set_buffer(&pending, wlr_buffer); if (buffer->wait_timeline != NULL) { wlr_output_state_set_wait_timeline(&pending, buffer->wait_timeline, buffer->wait_point); } From baeecc8dbd728e509640fd1997a46b47aa718226 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 3 Oct 2024 22:33:02 +0200 Subject: [PATCH 179/519] backend/wayland: handle DMA-BUF import failures create_immed() is a bit dangerous because on failure, either the connection is closed or the buffer is silently ignored. Use create() with a roundtrip to figure out whether the import succeeded. --- backend/wayland/output.c | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/backend/wayland/output.c b/backend/wayland/output.c index d09b74352..c823ad5d4 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -175,6 +175,30 @@ static bool test_buffer(struct wlr_wl_backend *wl, } } +struct dmabuf_listener_data { + struct wl_buffer *wl_buffer; + bool done; +}; + +static void dmabuf_handle_created(void *data_, struct zwp_linux_buffer_params_v1 *params, + struct wl_buffer *buffer) { + struct dmabuf_listener_data *data = data_; + data->wl_buffer = buffer; + data->done = true; + wlr_log(WLR_DEBUG, "DMA-BUF imported into parent Wayland compositor"); +} + +static void dmabuf_handle_failed(void *data_, struct zwp_linux_buffer_params_v1 *params) { + struct dmabuf_listener_data *data = data_; + data->done = true; + wlr_log(WLR_ERROR, "Failed to import DMA-BUF into parent Wayland compositor"); +} + +static const struct zwp_linux_buffer_params_v1_listener dmabuf_listener = { + .created = dmabuf_handle_created, + .failed = dmabuf_handle_failed, +}; + static struct wl_buffer *import_dmabuf(struct wlr_wl_backend *wl, struct wlr_dmabuf_attributes *dmabuf) { uint32_t modifier_hi = dmabuf->modifier >> 32; @@ -186,11 +210,19 @@ static struct wl_buffer *import_dmabuf(struct wlr_wl_backend *wl, dmabuf->offset[i], dmabuf->stride[i], modifier_hi, modifier_lo); } - struct wl_buffer *wl_buffer = zwp_linux_buffer_params_v1_create_immed( - params, dmabuf->width, dmabuf->height, dmabuf->format, 0); + struct dmabuf_listener_data data = {0}; + zwp_linux_buffer_params_v1_add_listener(params, &dmabuf_listener, &data); + zwp_linux_buffer_params_v1_create(params, dmabuf->width, dmabuf->height, dmabuf->format, 0); + + while (!data.done) { + if (wl_display_dispatch(wl->remote_display) < 0) { + wlr_log(WLR_ERROR, "wl_display_dispatch() failed"); + break; + } + } + zwp_linux_buffer_params_v1_destroy(params); - // TODO: handle create() errors - return wl_buffer; + return data.wl_buffer; } static struct wl_buffer *import_shm(struct wlr_wl_backend *wl, From 4ec1defb3e6570506f8702ca7f333eb83608518b Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 7 Nov 2024 18:48:20 +0100 Subject: [PATCH 180/519] all: use public We don't need to use the private header in these files. --- backend/backend.c | 1 - include/render/allocator/drm_dumb.h | 2 +- include/render/allocator/gbm.h | 2 +- include/render/allocator/shm.h | 2 +- render/swapchain.c | 2 +- types/output/cursor.c | 2 +- types/output/output.c | 2 +- types/output/render.c | 2 +- 8 files changed, 7 insertions(+), 8 deletions(-) diff --git a/backend/backend.c b/backend/backend.c index e4e8c8d8e..2be6a5c5e 100644 --- a/backend/backend.c +++ b/backend/backend.c @@ -14,7 +14,6 @@ #include #include "backend/backend.h" #include "backend/multi.h" -#include "render/allocator/allocator.h" #include "types/wlr_output.h" #include "util/env.h" #include "util/time.h" diff --git a/include/render/allocator/drm_dumb.h b/include/render/allocator/drm_dumb.h index 23c150bc3..f505d651a 100644 --- a/include/render/allocator/drm_dumb.h +++ b/include/render/allocator/drm_dumb.h @@ -1,9 +1,9 @@ #ifndef RENDER_ALLOCATOR_DRM_DUMB_H #define RENDER_ALLOCATOR_DRM_DUMB_H +#include #include #include -#include "render/allocator/allocator.h" struct wlr_drm_dumb_buffer { struct wlr_buffer base; diff --git a/include/render/allocator/gbm.h b/include/render/allocator/gbm.h index 7e043faf5..7d0016eb4 100644 --- a/include/render/allocator/gbm.h +++ b/include/render/allocator/gbm.h @@ -2,9 +2,9 @@ #define RENDER_ALLOCATOR_GBM_H #include +#include #include #include -#include "render/allocator/allocator.h" struct wlr_gbm_buffer { struct wlr_buffer base; diff --git a/include/render/allocator/shm.h b/include/render/allocator/shm.h index 4b80e4753..602efdc35 100644 --- a/include/render/allocator/shm.h +++ b/include/render/allocator/shm.h @@ -1,8 +1,8 @@ #ifndef RENDER_ALLOCATOR_SHM_H #define RENDER_ALLOCATOR_SHM_H +#include #include -#include "render/allocator/allocator.h" struct wlr_shm_buffer { struct wlr_buffer base; diff --git a/render/swapchain.c b/render/swapchain.c index 233d85eb0..e87a99ead 100644 --- a/render/swapchain.c +++ b/render/swapchain.c @@ -1,9 +1,9 @@ #include #include #include +#include #include #include -#include "render/allocator/allocator.h" #include "render/drm_format_set.h" static void swapchain_handle_allocator_destroy(struct wl_listener *listener, diff --git a/types/output/cursor.c b/types/output/cursor.c index afa6b95bc..d8cdd984a 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -9,7 +10,6 @@ #include #include #include -#include "render/allocator/allocator.h" #include "types/wlr_buffer.h" #include "types/wlr_output.h" diff --git a/types/output/output.c b/types/output/output.c index f8ed118f5..c8a174959 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -3,11 +3,11 @@ #include #include #include +#include #include #include #include #include -#include "render/allocator/allocator.h" #include "types/wlr_output.h" #include "util/env.h" #include "util/global.h" diff --git a/types/output/render.c b/types/output/render.c index b71478a6f..3fb2b22ec 100644 --- a/types/output/render.c +++ b/types/output/render.c @@ -2,12 +2,12 @@ #include #include #include +#include #include #include #include #include #include "backend/backend.h" -#include "render/allocator/allocator.h" #include "render/drm_format_set.h" #include "render/wlr_renderer.h" #include "render/pixel_format.h" From 4c4d74a564f1fad15860eb60aa4392a541a993fc Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 7 Nov 2024 18:50:25 +0100 Subject: [PATCH 181/519] backend/drm: use public wlr_allocator_autocreate() We don't need any custom behavior since d8c0707e2700 ("backend/drm: return secondary backend DRM FD"). --- backend/drm/renderer.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c index 60acc57b9..08ed62451 100644 --- a/backend/drm/renderer.c +++ b/backend/drm/renderer.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -9,7 +10,6 @@ #include "backend/drm/renderer.h" #include "backend/backend.h" #include "render/drm_format_set.h" -#include "render/allocator/allocator.h" #include "render/pixel_format.h" #include "render/wlr_renderer.h" @@ -21,9 +21,7 @@ bool init_drm_renderer(struct wlr_drm_backend *drm, return false; } - uint32_t backend_caps = backend_get_buffer_caps(&drm->backend); - renderer->allocator = allocator_autocreate_with_drm_fd(backend_caps, - renderer->wlr_rend, drm->fd); + renderer->allocator = wlr_allocator_autocreate(&drm->backend, renderer->wlr_rend); if (renderer->allocator == NULL) { wlr_log(WLR_ERROR, "Failed to create allocator"); wlr_renderer_destroy(renderer->wlr_rend); From c0945b661369dd11d5e80a6cee038146dd6098ce Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 7 Nov 2024 18:55:01 +0100 Subject: [PATCH 182/519] render/allocator: drop allocator_autocreate_with_drm_fd() This is now unused. --- include/render/allocator/allocator.h | 9 --------- render/allocator/allocator.c | 25 +++++++++---------------- 2 files changed, 9 insertions(+), 25 deletions(-) delete mode 100644 include/render/allocator/allocator.h diff --git a/include/render/allocator/allocator.h b/include/render/allocator/allocator.h deleted file mode 100644 index 5f8bd2a1b..000000000 --- a/include/render/allocator/allocator.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef RENDER_ALLOCATOR_ALLOCATOR_H -#define RENDER_ALLOCATOR_ALLOCATOR_H - -#include - -struct wlr_allocator *allocator_autocreate_with_drm_fd( - uint32_t backend_caps, struct wlr_renderer *renderer, int drm_fd); - -#endif diff --git a/render/allocator/allocator.c b/render/allocator/allocator.c index 27b08fc82..5e426c3c8 100644 --- a/render/allocator/allocator.c +++ b/render/allocator/allocator.c @@ -9,7 +9,6 @@ #include #include #include "backend/backend.h" -#include "render/allocator/allocator.h" #include "render/allocator/drm_dumb.h" #include "render/allocator/shm.h" #include "render/wlr_renderer.h" @@ -91,11 +90,17 @@ static int reopen_drm_node(int drm_fd, bool allow_render_node) { return new_fd; } -struct wlr_allocator *allocator_autocreate_with_drm_fd( - uint32_t backend_caps, struct wlr_renderer *renderer, - int drm_fd) { +struct wlr_allocator *wlr_allocator_autocreate(struct wlr_backend *backend, + struct wlr_renderer *renderer) { + uint32_t backend_caps = backend_get_buffer_caps(backend); uint32_t renderer_caps = renderer->render_buffer_caps; + // Note, drm_fd may be negative if unavailable + int drm_fd = wlr_backend_get_drm_fd(backend); + if (drm_fd < 0) { + drm_fd = wlr_renderer_get_drm_fd(renderer); + } + struct wlr_allocator *alloc = NULL; uint32_t gbm_caps = WLR_BUFFER_CAP_DMABUF; @@ -145,18 +150,6 @@ struct wlr_allocator *allocator_autocreate_with_drm_fd( return NULL; } -struct wlr_allocator *wlr_allocator_autocreate(struct wlr_backend *backend, - struct wlr_renderer *renderer) { - uint32_t backend_caps = backend_get_buffer_caps(backend); - // Note, drm_fd may be negative if unavailable - int drm_fd = wlr_backend_get_drm_fd(backend); - if (drm_fd < 0) { - drm_fd = wlr_renderer_get_drm_fd(renderer); - } - - return allocator_autocreate_with_drm_fd(backend_caps, renderer, drm_fd); -} - void wlr_allocator_destroy(struct wlr_allocator *alloc) { if (alloc == NULL) { return; From 014023c14f76a19c48a9505c80e64bcda1cb9117 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 7 Nov 2024 17:23:28 +0100 Subject: [PATCH 183/519] backend/drm: Set timeline support based on capability We assumed that all atomic backends supported syncobj, but gud does not. Instead, query DRM_CAP_SYNCOBJ_TIMELINE when using the atomic backend. --- backend/drm/backend.c | 1 - backend/drm/drm.c | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/drm/backend.c b/backend/drm/backend.c index f758eb6ed..5fe77777d 100644 --- a/backend/drm/backend.c +++ b/backend/drm/backend.c @@ -271,7 +271,6 @@ struct wlr_backend *wlr_drm_backend_create(struct wlr_session *session, } } - drm->backend.features.timeline = drm->iface != &legacy_iface; if (drm->parent) { drm->backend.features.timeline = drm->backend.features.timeline && drm->mgpu_renderer.wlr_rend->features.timeline; diff --git a/backend/drm/drm.c b/backend/drm/drm.c index f34c9a085..caa126371 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -124,6 +124,7 @@ bool check_drm_features(struct wlr_drm_backend *drm) { drm->supports_tearing_page_flips = drmGetCap(drm->fd, DRM_CAP_ASYNC_PAGE_FLIP, &cap) == 0 && cap == 1; } else { drm->supports_tearing_page_flips = drmGetCap(drm->fd, DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP, &cap) == 0 && cap == 1; + drm->backend.features.timeline = drmGetCap(drm->fd, DRM_CAP_SYNCOBJ_TIMELINE, &cap) == 0 && cap == 1; } if (env_parse_bool("WLR_DRM_NO_MODIFIERS")) { From 70d3635985464052b7081420ff93dbb7a715d44f Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 7 Nov 2024 15:40:43 +0100 Subject: [PATCH 184/519] drm_format_set: Add wlr_drm_format_set_remove wlr_drm_format_set_remove lets you remove a modifier from the specified format, useful for filtering implicit modifiers. --- include/wlr/render/drm_format_set.h | 3 +++ render/drm_format_set.c | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/include/wlr/render/drm_format_set.h b/include/wlr/render/drm_format_set.h index cc38bbd23..16dcfe9fd 100644 --- a/include/wlr/render/drm_format_set.h +++ b/include/wlr/render/drm_format_set.h @@ -69,6 +69,9 @@ void wlr_drm_format_set_finish(struct wlr_drm_format_set *set); const struct wlr_drm_format *wlr_drm_format_set_get( const struct wlr_drm_format_set *set, uint32_t format); +bool wlr_drm_format_set_remove(struct wlr_drm_format_set *set, uint32_t format, + uint64_t modifier); + bool wlr_drm_format_set_has(const struct wlr_drm_format_set *set, uint32_t format, uint64_t modifier); diff --git a/render/drm_format_set.c b/render/drm_format_set.c index dc57ad9bc..9749a0093 100644 --- a/render/drm_format_set.c +++ b/render/drm_format_set.c @@ -86,6 +86,23 @@ bool wlr_drm_format_set_add(struct wlr_drm_format_set *set, uint32_t format, return true; } +bool wlr_drm_format_set_remove(struct wlr_drm_format_set *set, uint32_t format, + uint64_t modifier) { + struct wlr_drm_format *fmt = format_set_get(set, format); + if (fmt == NULL) { + return false; + } + + for (size_t idx = 0; idx < fmt->len; idx++) { + if (fmt->modifiers[idx] == modifier) { + memmove(&fmt->modifiers[idx], &fmt->modifiers[idx+1], fmt->len - idx - 1); + fmt->len--; + return true; + } + } + return false; +} + void wlr_drm_format_init(struct wlr_drm_format *fmt, uint32_t format) { *fmt = (struct wlr_drm_format){ .format = format, From 55f15d1abd741efef63100a5104392487f3b88d0 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 7 Nov 2024 23:10:39 +0100 Subject: [PATCH 185/519] backend/drm: Ensure renderer is set to NULL on error If init_drm_renderer failed, it would destroy the renderer but would not set it to NULL, leading to use-after-free. NULL the renderer after destroying it. --- backend/drm/renderer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c index 08ed62451..ce59c90a7 100644 --- a/backend/drm/renderer.c +++ b/backend/drm/renderer.c @@ -25,6 +25,7 @@ bool init_drm_renderer(struct wlr_drm_backend *drm, if (renderer->allocator == NULL) { wlr_log(WLR_ERROR, "Failed to create allocator"); wlr_renderer_destroy(renderer->wlr_rend); + renderer->wlr_rend = NULL; return false; } From 1dd05437bf413e2f21ae9272e47c5c8ccb3f2730 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 7 Nov 2024 22:05:15 +0100 Subject: [PATCH 186/519] backend/drm: Bail if renderer cannot import DMA-BUFs We create a renderer for the sole purpose of blitting buffers from a primary renderer that we might not be able to scan-out from. If we end up with the pixman renderer, it either won't work becuase it cannot import dmabufs from the primary renderer, or won't have any effect because the primary renderer already uses dumb buffers. We test for DMA-BUF capabilities specifically to make it clear what our interest is, rather than focusing too much on the pixman renderer. --- backend/drm/renderer.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c index ce59c90a7..b07984984 100644 --- a/backend/drm/renderer.c +++ b/backend/drm/renderer.c @@ -20,6 +20,12 @@ bool init_drm_renderer(struct wlr_drm_backend *drm, wlr_log(WLR_ERROR, "Failed to create renderer"); return false; } + if (wlr_renderer_get_texture_formats(renderer->wlr_rend, WLR_BUFFER_CAP_DMABUF) == NULL) { + wlr_log(WLR_ERROR, "Renderer did not support importing DMA-BUFs"); + wlr_renderer_destroy(renderer->wlr_rend); + renderer->wlr_rend = NULL; + return false; + } renderer->allocator = wlr_allocator_autocreate(&drm->backend, renderer->wlr_rend); if (renderer->allocator == NULL) { From b2bb111f032737fff034c7d79ded44fb36ea6778 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Fri, 30 Aug 2024 01:34:29 +0200 Subject: [PATCH 187/519] backend/drm: Check for mgpu_renderer instead of parent The presence of the renderer is what matters with respect to blitting. Having a parent without the need to blit will be allowed later. --- backend/drm/backend.c | 4 ++-- backend/drm/drm.c | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/backend/drm/backend.c b/backend/drm/backend.c index 5fe77777d..d93655ce7 100644 --- a/backend/drm/backend.c +++ b/backend/drm/backend.c @@ -53,7 +53,7 @@ static void backend_destroy(struct wlr_backend *backend) { wl_list_remove(&drm->dev_change.link); wl_list_remove(&drm->dev_remove.link); - if (drm->parent) { + if (drm->mgpu_renderer.wlr_rend) { finish_drm_renderer(&drm->mgpu_renderer); } @@ -271,7 +271,7 @@ struct wlr_backend *wlr_drm_backend_create(struct wlr_session *session, } } - if (drm->parent) { + if (drm->mgpu_renderer.wlr_rend) { drm->backend.features.timeline = drm->backend.features.timeline && drm->mgpu_renderer.wlr_rend->features.timeline; } diff --git a/backend/drm/drm.c b/backend/drm/drm.c index caa126371..9316d7b2c 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -722,7 +722,7 @@ static bool drm_connector_state_update_primary_fb(struct wlr_drm_connector *conn assert(state->wait_timeline == NULL); struct wlr_buffer *local_buf; - if (drm->parent) { + if (drm->mgpu_renderer.wlr_rend) { struct wlr_drm_format format = {0}; if (!drm_plane_pick_render_format(plane, &format, &drm->mgpu_renderer)) { wlr_log(WLR_ERROR, "Failed to pick primary plane format"); @@ -776,7 +776,7 @@ static bool drm_connector_set_pending_layer_fbs(struct wlr_drm_connector *conn, struct wlr_drm_backend *drm = conn->backend; struct wlr_drm_crtc *crtc = conn->crtc; - if (!crtc || drm->parent) { + if (!crtc || drm->mgpu_renderer.wlr_rend) { return false; } @@ -835,7 +835,7 @@ static bool drm_connector_prepare(struct wlr_drm_connector_state *conn_state, bo return false; } - if (test_only && conn->backend->parent) { + if (test_only && conn->backend->mgpu_renderer.wlr_rend) { // If we're running as a secondary GPU, we can't perform an atomic // commit without blitting a buffer. return true; @@ -905,7 +905,7 @@ static bool drm_connector_commit_state(struct wlr_drm_connector *conn, goto out; } - if (test_only && conn->backend->parent) { + if (test_only && conn->backend->mgpu_renderer.wlr_rend) { // If we're running as a secondary GPU, we can't perform an atomic // commit without blitting a buffer. ok = true; @@ -1102,7 +1102,7 @@ static bool drm_connector_set_cursor(struct wlr_output *output, } struct wlr_buffer *local_buf; - if (drm->parent) { + if (drm->mgpu_renderer.wlr_rend) { struct wlr_drm_format format = {0}; if (!drm_plane_pick_render_format(plane, &format, &drm->mgpu_renderer)) { wlr_log(WLR_ERROR, "Failed to pick cursor plane format"); @@ -1216,7 +1216,7 @@ static const struct wlr_drm_format_set *drm_connector_get_cursor_formats( if (!plane) { return NULL; } - if (conn->backend->parent) { + if (conn->backend->mgpu_renderer.wlr_rend) { return &conn->backend->mgpu_formats; } return &plane->formats; @@ -1245,7 +1245,7 @@ static const struct wlr_drm_format_set *drm_connector_get_primary_formats( if (!drm_connector_alloc_crtc(conn)) { return NULL; } - if (conn->backend->parent) { + if (conn->backend->mgpu_renderer.wlr_rend) { return &conn->backend->mgpu_formats; } return &conn->crtc->primary->formats; @@ -1900,7 +1900,7 @@ bool commit_drm_device(struct wlr_drm_backend *drm, modeset |= output_state->base.allow_reconfiguration; } - if (test_only && drm->parent) { + if (test_only && drm->mgpu_renderer.wlr_rend) { // If we're running as a secondary GPU, we can't perform an atomic // commit without blitting a buffer. ok = true; @@ -1978,7 +1978,7 @@ static void handle_page_flip(int fd, unsigned seq, * data between the GPUs, even if we were using the direct scanout * interface. */ - if (!drm->parent) { + if (!drm->mgpu_renderer.wlr_rend) { present_flags |= WLR_OUTPUT_PRESENT_ZERO_COPY; } From 66ddd62e42c8f8f60ab69b6c16f476daa7a63afe Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 7 Nov 2024 16:28:15 +0100 Subject: [PATCH 188/519] backend/drm: Move mgpu renderer setup to function This helps readability a bit and will make error handling in a coming commit easier. --- backend/drm/backend.c | 69 ++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/backend/drm/backend.c b/backend/drm/backend.c index d93655ce7..fd1701438 100644 --- a/backend/drm/backend.c +++ b/backend/drm/backend.c @@ -172,6 +172,39 @@ static void handle_parent_destroy(struct wl_listener *listener, void *data) { backend_destroy(&drm->backend); } +static bool init_mgpu_renderer(struct wlr_drm_backend *drm) { + if (!init_drm_renderer(drm, &drm->mgpu_renderer)) { + wlr_log(WLR_ERROR, "Failed to initialize renderer"); + return false; + } + + // We'll perform a multi-GPU copy for all submitted buffers, we need + // to be able to texture from them + struct wlr_renderer *renderer = drm->mgpu_renderer.wlr_rend; + const struct wlr_drm_format_set *texture_formats = + wlr_renderer_get_texture_formats(renderer, WLR_BUFFER_CAP_DMABUF); + if (texture_formats == NULL) { + wlr_log(WLR_ERROR, "Failed to query renderer texture formats"); + return false; + } + + // Forbid implicit modifiers, because their meaning changes from one + // GPU to another. + for (size_t i = 0; i < texture_formats->len; i++) { + const struct wlr_drm_format *fmt = &texture_formats->formats[i]; + for (size_t j = 0; j < fmt->len; j++) { + uint64_t mod = fmt->modifiers[j]; + if (mod == DRM_FORMAT_MOD_INVALID) { + continue; + } + wlr_drm_format_set_add(&drm->mgpu_formats, fmt->format, mod); + } + } + drm->backend.features.timeline = drm->backend.features.timeline && + drm->mgpu_renderer.wlr_rend->features.timeline; + return true; +} + struct wlr_backend *wlr_drm_backend_create(struct wlr_session *session, struct wlr_device *dev, struct wlr_backend *parent) { assert(session && dev); @@ -241,39 +274,8 @@ struct wlr_backend *wlr_drm_backend_create(struct wlr_session *session, goto error_event; } - if (drm->parent) { - if (!init_drm_renderer(drm, &drm->mgpu_renderer)) { - wlr_log(WLR_ERROR, "Failed to initialize renderer"); - goto error_resources; - } - - // We'll perform a multi-GPU copy for all submitted buffers, we need - // to be able to texture from them - struct wlr_renderer *renderer = drm->mgpu_renderer.wlr_rend; - const struct wlr_drm_format_set *texture_formats = - wlr_renderer_get_texture_formats(renderer, WLR_BUFFER_CAP_DMABUF); - if (texture_formats == NULL) { - wlr_log(WLR_ERROR, "Failed to query renderer texture formats"); - goto error_mgpu_renderer; - } - - // Forbid implicit modifiers, because their meaning changes from one - // GPU to another. - for (size_t i = 0; i < texture_formats->len; i++) { - const struct wlr_drm_format *fmt = &texture_formats->formats[i]; - for (size_t j = 0; j < fmt->len; j++) { - uint64_t mod = fmt->modifiers[j]; - if (mod == DRM_FORMAT_MOD_INVALID) { - continue; - } - wlr_drm_format_set_add(&drm->mgpu_formats, fmt->format, mod); - } - } - } - - if (drm->mgpu_renderer.wlr_rend) { - drm->backend.features.timeline = drm->backend.features.timeline && - drm->mgpu_renderer.wlr_rend->features.timeline; + if (drm->parent && !init_mgpu_renderer(drm)) { + goto error_mgpu_renderer; } drm->session_destroy.notify = handle_session_destroy; @@ -283,7 +285,6 @@ struct wlr_backend *wlr_drm_backend_create(struct wlr_session *session, error_mgpu_renderer: finish_drm_renderer(&drm->mgpu_renderer); -error_resources: finish_drm_resources(drm); error_event: wl_list_remove(&drm->session_active.link); From 0108506c770753604d6b77d24d1aa7f68feb7a23 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 7 Nov 2024 16:28:49 +0100 Subject: [PATCH 189/519] backend/drm: Allow proceeding with render-less mgpu If an mgpu device does not have a renderer, continue without one rather than ignoring it entirely. It is not guaranteed that we will be able to scan out to it in any particular configuration, but that is true for any output regardless, and having the output visible is not harmful even if it cannot light up. To proceed safely, we strip implicit modifier support from all planes, while avoiding duplication of the same logic for mdgpu_formats. This helps GUD and DisplayLink scenarios. --- backend/drm/backend.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/backend/drm/backend.c b/backend/drm/backend.c index fd1701438..ba0e8651c 100644 --- a/backend/drm/backend.c +++ b/backend/drm/backend.c @@ -13,6 +13,7 @@ #include #include "backend/drm/drm.h" #include "backend/drm/fb.h" +#include "render/drm_format_set.h" struct wlr_drm_backend *get_drm_backend_from_backend( struct wlr_backend *wlr_backend) { @@ -172,10 +173,25 @@ static void handle_parent_destroy(struct wl_listener *listener, void *data) { backend_destroy(&drm->backend); } +static void sanitize_mgpu_modifiers(struct wlr_drm_format_set *set) { + for (size_t idx = 0; idx < set->len; idx++) { + // Implicit modifiers are not well-defined across devices, so strip + // them from all formats in multi-gpu scenarios. + struct wlr_drm_format *fmt = &set->formats[idx]; + wlr_drm_format_set_remove(set, fmt->format, DRM_FORMAT_MOD_INVALID); + } +} + static bool init_mgpu_renderer(struct wlr_drm_backend *drm) { if (!init_drm_renderer(drm, &drm->mgpu_renderer)) { - wlr_log(WLR_ERROR, "Failed to initialize renderer"); - return false; + wlr_log(WLR_INFO, "Failed to initialize mgpu blit renderer" + ", falling back to scanning out from primary GPU"); + + for (uint32_t plane_idx = 0; plane_idx < drm->num_planes; plane_idx++) { + struct wlr_drm_plane *plane = &drm->planes[plane_idx]; + sanitize_mgpu_modifiers(&plane->formats); + } + return true; } // We'll perform a multi-GPU copy for all submitted buffers, we need @@ -188,18 +204,8 @@ static bool init_mgpu_renderer(struct wlr_drm_backend *drm) { return false; } - // Forbid implicit modifiers, because their meaning changes from one - // GPU to another. - for (size_t i = 0; i < texture_formats->len; i++) { - const struct wlr_drm_format *fmt = &texture_formats->formats[i]; - for (size_t j = 0; j < fmt->len; j++) { - uint64_t mod = fmt->modifiers[j]; - if (mod == DRM_FORMAT_MOD_INVALID) { - continue; - } - wlr_drm_format_set_add(&drm->mgpu_formats, fmt->format, mod); - } - } + wlr_drm_format_set_copy(&drm->mgpu_formats, texture_formats); + sanitize_mgpu_modifiers(&drm->mgpu_formats); drm->backend.features.timeline = drm->backend.features.timeline && drm->mgpu_renderer.wlr_rend->features.timeline; return true; From f440c601280c13c65fe81368f1542ef77f806587 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 7 Nov 2024 22:27:03 +0100 Subject: [PATCH 190/519] backend/drm: Remove redundant error logs wlr_renderer and wlr_allocator will print out errors as they go, and end with a final error if they fail to create anything. The caller of this function will also log when it fails. Skip the redundant errors emitted here. --- backend/drm/renderer.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c index b07984984..1253d22bd 100644 --- a/backend/drm/renderer.c +++ b/backend/drm/renderer.c @@ -17,7 +17,6 @@ bool init_drm_renderer(struct wlr_drm_backend *drm, struct wlr_drm_renderer *renderer) { renderer->wlr_rend = renderer_autocreate_with_drm_fd(drm->fd); if (!renderer->wlr_rend) { - wlr_log(WLR_ERROR, "Failed to create renderer"); return false; } if (wlr_renderer_get_texture_formats(renderer->wlr_rend, WLR_BUFFER_CAP_DMABUF) == NULL) { @@ -29,7 +28,6 @@ bool init_drm_renderer(struct wlr_drm_backend *drm, renderer->allocator = wlr_allocator_autocreate(&drm->backend, renderer->wlr_rend); if (renderer->allocator == NULL) { - wlr_log(WLR_ERROR, "Failed to create allocator"); wlr_renderer_destroy(renderer->wlr_rend); renderer->wlr_rend = NULL; return false; From c3acef0dc01dd69fbcd59252636f9437912ab3fd Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sun, 10 Nov 2024 00:22:17 +0100 Subject: [PATCH 191/519] scene: Only unwrap client buffer when underlying buffer is held Client buffers backed by wl_shm is aggressively released, in which case we are not allowed to access it. Locking an already released buffer and later unlocking it will also re-trigger release, confusing clients. As a quick workaround, guard the unwrap by checking if the buffer is locked, which will be the case for non-wl_shm buffers. --- types/scene/wlr_scene.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 370be2b3a..6d14299ca 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1874,7 +1874,7 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry, struct wlr_buffer *wlr_buffer = buffer->buffer; struct wlr_client_buffer *client_buffer = wlr_client_buffer_get(wlr_buffer); - if (client_buffer != NULL && client_buffer->source != NULL) { + if (client_buffer != NULL && client_buffer->source != NULL && client_buffer->source->n_locks > 0) { wlr_buffer = client_buffer->source; } From 8e36040e88bba6560d6c8d673692cf81b4f73337 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 26 Oct 2024 19:17:55 +0200 Subject: [PATCH 192/519] render/drm_syncobj: de-duplicate drm_syncobj timeline init --- render/drm_syncobj.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/render/drm_syncobj.c b/render/drm_syncobj.c index f7120df30..f23fd638d 100644 --- a/render/drm_syncobj.c +++ b/render/drm_syncobj.c @@ -12,36 +12,45 @@ #include #endif -struct wlr_drm_syncobj_timeline *wlr_drm_syncobj_timeline_create(int drm_fd) { +static struct wlr_drm_syncobj_timeline *timeline_create(int drm_fd, uint32_t handle) { struct wlr_drm_syncobj_timeline *timeline = calloc(1, sizeof(*timeline)); if (timeline == NULL) { return NULL; } + timeline->drm_fd = drm_fd; timeline->n_refs = 1; + timeline->handle = handle; - if (drmSyncobjCreate(drm_fd, 0, &timeline->handle) != 0) { + return timeline; +} + +struct wlr_drm_syncobj_timeline *wlr_drm_syncobj_timeline_create(int drm_fd) { + uint32_t handle = 0; + if (drmSyncobjCreate(drm_fd, 0, &handle) != 0) { wlr_log_errno(WLR_ERROR, "drmSyncobjCreate failed"); - free(timeline); return NULL; } + struct wlr_drm_syncobj_timeline *timeline = timeline_create(drm_fd, handle); + if (timeline == NULL) { + drmSyncobjDestroy(drm_fd, handle); + } + return timeline; } struct wlr_drm_syncobj_timeline *wlr_drm_syncobj_timeline_import(int drm_fd, int drm_syncobj_fd) { - struct wlr_drm_syncobj_timeline *timeline = calloc(1, sizeof(*timeline)); - if (timeline == NULL) { + uint32_t handle = 0; + if (drmSyncobjFDToHandle(drm_fd, drm_syncobj_fd, &handle) != 0) { + wlr_log_errno(WLR_ERROR, "drmSyncobjFDToHandle failed"); return NULL; } - timeline->drm_fd = drm_fd; - timeline->n_refs = 1; - if (drmSyncobjFDToHandle(drm_fd, drm_syncobj_fd, &timeline->handle) != 0) { - wlr_log_errno(WLR_ERROR, "drmSyncobjFDToHandle failed"); - free(timeline); - return NULL; + struct wlr_drm_syncobj_timeline *timeline = timeline_create(drm_fd, handle); + if (timeline == NULL) { + drmSyncobjDestroy(drm_fd, handle); } return timeline; From ca29f43a5443aece804a14c7fb5e25273bab822f Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 26 Oct 2024 19:19:06 +0200 Subject: [PATCH 193/519] render/drm_syncobj: add addon set --- include/wlr/render/drm_syncobj.h | 3 +++ render/drm_syncobj.c | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/include/wlr/render/drm_syncobj.h b/include/wlr/render/drm_syncobj.h index b847dbd25..bea86d191 100644 --- a/include/wlr/render/drm_syncobj.h +++ b/include/wlr/render/drm_syncobj.h @@ -4,6 +4,7 @@ #include #include #include +#include /** * A synchronization timeline. @@ -29,6 +30,8 @@ struct wlr_drm_syncobj_timeline { int drm_fd; uint32_t handle; + struct wlr_addon_set addons; + struct { size_t n_refs; } WLR_PRIVATE; diff --git a/render/drm_syncobj.c b/render/drm_syncobj.c index f23fd638d..9e5569fb4 100644 --- a/render/drm_syncobj.c +++ b/render/drm_syncobj.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "config.h" @@ -22,6 +23,8 @@ static struct wlr_drm_syncobj_timeline *timeline_create(int drm_fd, uint32_t han timeline->n_refs = 1; timeline->handle = handle; + wlr_addon_set_init(&timeline->addons); + return timeline; } @@ -72,6 +75,7 @@ void wlr_drm_syncobj_timeline_unref(struct wlr_drm_syncobj_timeline *timeline) { return; } + wlr_addon_set_finish(&timeline->addons); drmSyncobjDestroy(timeline->drm_fd, timeline->handle); free(timeline); } From e9a6b3b85dfb3f4c43cc901b6936a7c0e27ea0ee Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 26 Oct 2024 19:19:41 +0200 Subject: [PATCH 194/519] backend/wayland: add support for explicit sync wl_buffer.release event delivery becomes undefined when using the linux-drm-syncobj-v1 protocol, so we need to wait for buffer release via a timeline point instead. The protocol requires both wait and signal timelines to be set, so we need to create one when the compositor only supplies a wait timeline. --- backend/wayland/backend.c | 15 ++++ backend/wayland/meson.build | 1 + backend/wayland/output.c | 165 ++++++++++++++++++++++++++++++++++-- include/backend/wayland.h | 19 +++++ 4 files changed, 195 insertions(+), 5 deletions(-) diff --git a/backend/wayland/backend.c b/backend/wayland/backend.c index 9a74e6919..475d44222 100644 --- a/backend/wayland/backend.c +++ b/backend/wayland/backend.c @@ -21,6 +21,7 @@ #include "drm-client-protocol.h" #include "linux-dmabuf-v1-client-protocol.h" +#include "linux-drm-syncobj-v1-client-protocol.h" #include "pointer-gestures-unstable-v1-client-protocol.h" #include "presentation-time-client-protocol.h" #include "xdg-activation-v1-client-protocol.h" @@ -411,6 +412,9 @@ static void registry_global(void *data, struct wl_registry *registry, } else if (strcmp(iface, wp_viewporter_interface.name) == 0) { wl->viewporter = wl_registry_bind(registry, name, &wp_viewporter_interface, 1); + } else if (strcmp(iface, wp_linux_drm_syncobj_manager_v1_interface.name) == 0) { + wl->drm_syncobj_manager_v1 = wl_registry_bind(registry, name, + &wp_linux_drm_syncobj_manager_v1_interface, 1); } } @@ -484,6 +488,11 @@ static void backend_destroy(struct wlr_backend *backend) { destroy_wl_buffer(buffer); } + struct wlr_wl_drm_syncobj_timeline *timeline, *tmp_timeline; + wl_list_for_each_safe(timeline, tmp_timeline, &wl->drm_syncobj_timelines, link) { + destroy_wl_drm_syncobj_timeline(timeline); + } + wlr_backend_finish(backend); wl_list_remove(&wl->event_loop_destroy.link); @@ -518,6 +527,9 @@ static void backend_destroy(struct wlr_backend *backend) { if (wl->zwp_linux_dmabuf_v1) { zwp_linux_dmabuf_v1_destroy(wl->zwp_linux_dmabuf_v1); } + if (wl->drm_syncobj_manager_v1) { + wp_linux_drm_syncobj_manager_v1_destroy(wl->drm_syncobj_manager_v1); + } if (wl->legacy_drm != NULL) { wl_drm_destroy(wl->legacy_drm); } @@ -592,6 +604,7 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_event_loop *loop, wl_list_init(&wl->outputs); wl_list_init(&wl->seats); wl_list_init(&wl->buffers); + wl_list_init(&wl->drm_syncobj_timelines); if (remote_display != NULL) { wl->remote_display = remote_display; @@ -624,6 +637,8 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_event_loop *loop, goto error_registry; } + wl->backend.features.timeline = wl->drm_syncobj_manager_v1 != NULL; + wl_display_roundtrip(wl->remote_display); // process initial event bursts struct zwp_linux_dmabuf_feedback_v1 *linux_dmabuf_feedback_v1 = NULL; diff --git a/backend/wayland/meson.build b/backend/wayland/meson.build index fcf9fdd8f..9235b7fe3 100644 --- a/backend/wayland/meson.build +++ b/backend/wayland/meson.build @@ -15,6 +15,7 @@ wlr_files += files( client_protos = [ 'drm', 'linux-dmabuf-v1', + 'linux-drm-syncobj-v1', 'pointer-gestures-unstable-v1', 'presentation-time', 'relative-pointer-unstable-v1', diff --git a/backend/wayland/output.c b/backend/wayland/output.c index c823ad5d4..5bc05f947 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -20,6 +21,7 @@ #include "types/wlr_output.h" #include "linux-dmabuf-v1-client-protocol.h" +#include "linux-drm-syncobj-v1-client-protocol.h" #include "presentation-time-client-protocol.h" #include "viewporter-client-protocol.h" #include "xdg-activation-v1-client-protocol.h" @@ -31,7 +33,9 @@ static const uint32_t SUPPORTED_OUTPUT_STATE = WLR_OUTPUT_STATE_BUFFER | WLR_OUTPUT_STATE_ENABLED | WLR_OUTPUT_STATE_MODE | - WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED; + WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED | + WLR_OUTPUT_STATE_WAIT_TIMELINE | + WLR_OUTPUT_STATE_SIGNAL_TIMELINE; static size_t last_output_num = 0; @@ -140,19 +144,40 @@ void destroy_wl_buffer(struct wlr_wl_buffer *buffer) { if (!buffer->released) { wlr_buffer_unlock(buffer->buffer); } + wlr_drm_syncobj_timeline_unref(buffer->fallback_signal_timeline); free(buffer); } +static void buffer_release(struct wlr_wl_buffer *buffer) { + if (buffer->released) { + return; + } + buffer->released = true; + wlr_buffer_unlock(buffer->buffer); // might free buffer +} + static void buffer_handle_release(void *data, struct wl_buffer *wl_buffer) { struct wlr_wl_buffer *buffer = data; - buffer->released = true; - wlr_buffer_unlock(buffer->buffer); // might free buffer + if (buffer->has_drm_syncobj_waiter) { + return; + } + buffer_release(buffer); } static const struct wl_buffer_listener buffer_listener = { .release = buffer_handle_release, }; +static void buffer_handle_drm_syncobj_ready(struct wl_listener *listener, void *data) { + struct wlr_wl_buffer *buffer = wl_container_of(listener, buffer, drm_syncobj_ready); + + wl_list_remove(&buffer->drm_syncobj_ready.link); + wlr_drm_syncobj_timeline_waiter_finish(&buffer->drm_syncobj_waiter); + buffer->has_drm_syncobj_waiter = false; + + buffer_release(buffer); +} + static void buffer_handle_buffer_destroy(struct wl_listener *listener, void *data) { struct wlr_wl_buffer *buffer = @@ -293,6 +318,58 @@ static struct wlr_wl_buffer *get_or_create_wl_buffer(struct wlr_wl_backend *wl, return create_wl_buffer(wl, wlr_buffer); } +void destroy_wl_drm_syncobj_timeline(struct wlr_wl_drm_syncobj_timeline *timeline) { + wp_linux_drm_syncobj_timeline_v1_destroy(timeline->wl); + wlr_addon_finish(&timeline->addon); + wl_list_remove(&timeline->link); + free(timeline); +} + +static void drm_syncobj_timeline_addon_destroy(struct wlr_addon *addon) { + struct wlr_wl_drm_syncobj_timeline *timeline = wl_container_of(addon, timeline, addon); + destroy_wl_drm_syncobj_timeline(timeline); +} + +static const struct wlr_addon_interface drm_syncobj_timeline_addon_impl = { + .name = "wlr_wl_drm_syncobj_timeline", + .destroy = drm_syncobj_timeline_addon_destroy, +}; + +static struct wlr_wl_drm_syncobj_timeline *get_or_create_drm_syncobj_timeline( + struct wlr_wl_backend *wl, struct wlr_drm_syncobj_timeline *wlr_timeline) { + struct wlr_addon *addon = + wlr_addon_find(&wlr_timeline->addons, wl, &drm_syncobj_timeline_addon_impl); + if (addon != NULL) { + struct wlr_wl_drm_syncobj_timeline *timeline = wl_container_of(addon, timeline, addon); + return timeline; + } + + struct wlr_wl_drm_syncobj_timeline *timeline = calloc(1, sizeof(*timeline)); + if (timeline == NULL) { + return NULL; + } + + timeline->base = wlr_timeline; + + int fd = wlr_drm_syncobj_timeline_export(wlr_timeline); + if (fd < 0) { + free(timeline); + return NULL; + } + + timeline->wl = wp_linux_drm_syncobj_manager_v1_import_timeline(wl->drm_syncobj_manager_v1, fd); + close(fd); + if (timeline->wl == NULL) { + free(timeline); + return NULL; + } + + wlr_addon_init(&timeline->addon, &wlr_timeline->addons, wl, &drm_syncobj_timeline_addon_impl); + wl_list_insert(&wl->drm_syncobj_timelines, &timeline->link); + + return timeline; +} + static bool update_title(struct wlr_wl_output *output, const char *title) { struct wlr_output *wlr_output = &output->wlr_output; @@ -387,6 +464,21 @@ static bool output_test(struct wlr_output *wlr_output, return false; } + if ((state->committed & WLR_OUTPUT_STATE_SIGNAL_TIMELINE) && + !(state->committed & WLR_OUTPUT_STATE_WAIT_TIMELINE)) { + wlr_log(WLR_DEBUG, "Signal timeline requires a wait timeline"); + return false; + } + + if ((state->committed & WLR_OUTPUT_STATE_WAIT_TIMELINE) || + (state->committed & WLR_OUTPUT_STATE_SIGNAL_TIMELINE)) { + struct wlr_dmabuf_attributes dmabuf; + if (!wlr_buffer_get_dmabuf(state->buffer, &dmabuf)) { + wlr_log(WLR_DEBUG, "Wait/signal timelines require DMA-BUFs"); + return false; + } + } + if (state->committed & WLR_OUTPUT_STATE_LAYERS) { // If we can't use a sub-surface for a layer, then we can't use a // sub-surface for any layer underneath @@ -660,6 +752,7 @@ static bool output_commit(struct wlr_output *wlr_output, } } + struct wlr_wl_buffer *buffer = NULL; if (state->committed & WLR_OUTPUT_STATE_BUFFER) { const pixman_region32_t *damage = NULL; if (state->committed & WLR_OUTPUT_STATE_DAMAGE) { @@ -667,8 +760,7 @@ static bool output_commit(struct wlr_output *wlr_output, } struct wlr_buffer *wlr_buffer = state->buffer; - struct wlr_wl_buffer *buffer = - get_or_create_wl_buffer(output->backend, wlr_buffer); + buffer = get_or_create_wl_buffer(output->backend, wlr_buffer); if (buffer == NULL) { return false; } @@ -677,6 +769,66 @@ static bool output_commit(struct wlr_output *wlr_output, damage_surface(output->surface, damage); } + if (state->committed & WLR_OUTPUT_STATE_WAIT_TIMELINE) { + struct wlr_wl_drm_syncobj_timeline *wait_timeline = + get_or_create_drm_syncobj_timeline(output->backend, state->wait_timeline); + + struct wlr_wl_drm_syncobj_timeline *signal_timeline; + uint64_t signal_point; + if (state->committed & WLR_OUTPUT_STATE_SIGNAL_TIMELINE) { + signal_timeline = get_or_create_drm_syncobj_timeline(output->backend, state->signal_timeline); + signal_point = state->signal_point; + } else { + if (buffer->fallback_signal_timeline == NULL) { + buffer->fallback_signal_timeline = + wlr_drm_syncobj_timeline_create(output->backend->drm_fd); + if (buffer->fallback_signal_timeline == NULL) { + return false; + } + } + signal_timeline = + get_or_create_drm_syncobj_timeline(output->backend, buffer->fallback_signal_timeline); + signal_point = ++buffer->fallback_signal_point; + } + + if (wait_timeline == NULL || signal_timeline == NULL) { + return false; + } + + if (output->drm_syncobj_surface_v1 == NULL) { + output->drm_syncobj_surface_v1 = wp_linux_drm_syncobj_manager_v1_get_surface( + output->backend->drm_syncobj_manager_v1, output->surface); + if (output->drm_syncobj_surface_v1 == NULL) { + return false; + } + } + + uint32_t wait_point_hi = state->wait_point >> 32; + uint32_t wait_point_lo = state->wait_point & UINT32_MAX; + uint32_t signal_point_hi = signal_point >> 32; + uint32_t signal_point_lo = signal_point & UINT32_MAX; + + wp_linux_drm_syncobj_surface_v1_set_acquire_point(output->drm_syncobj_surface_v1, + wait_timeline->wl, wait_point_hi, wait_point_lo); + wp_linux_drm_syncobj_surface_v1_set_release_point(output->drm_syncobj_surface_v1, + signal_timeline->wl, signal_point_hi, signal_point_lo); + + if (!wlr_drm_syncobj_timeline_waiter_init(&buffer->drm_syncobj_waiter, + signal_timeline->base, signal_point, 0, output->backend->event_loop)) { + return false; + } + buffer->has_drm_syncobj_waiter = true; + + buffer->drm_syncobj_ready.notify = buffer_handle_drm_syncobj_ready; + wl_signal_add(&buffer->drm_syncobj_waiter.events.ready, + &buffer->drm_syncobj_ready); + } else { + if (output->drm_syncobj_surface_v1 != NULL) { + wp_linux_drm_syncobj_surface_v1_destroy(output->drm_syncobj_surface_v1); + output->drm_syncobj_surface_v1 = NULL; + } + } + if ((state->committed & WLR_OUTPUT_STATE_LAYERS) && !commit_layers(output, state->layers, state->layers_len)) { return false; @@ -801,6 +953,9 @@ static void output_destroy(struct wlr_output *wlr_output) { wl_callback_destroy(output->unmap_callback); } + if (output->drm_syncobj_surface_v1) { + wp_linux_drm_syncobj_surface_v1_destroy(output->drm_syncobj_surface_v1); + } if (output->zxdg_toplevel_decoration_v1) { zxdg_toplevel_decoration_v1_destroy(output->zxdg_toplevel_decoration_v1); } diff --git a/include/backend/wayland.h b/include/backend/wayland.h index aa266eef8..46e8df95c 100644 --- a/include/backend/wayland.h +++ b/include/backend/wayland.h @@ -14,6 +14,7 @@ #include #include #include +#include struct wlr_wl_backend { struct wlr_backend backend; @@ -40,6 +41,8 @@ struct wlr_wl_backend { struct wp_presentation *presentation; struct wl_shm *shm; struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1; + struct wp_linux_drm_syncobj_manager_v1 *drm_syncobj_manager_v1; + struct wl_list drm_syncobj_timelines; // wlr_wl_drm_syncobj_timeline.link struct zwp_relative_pointer_manager_v1 *zwp_relative_pointer_manager_v1; struct wl_list seats; // wlr_wl_seat.link struct zwp_tablet_manager_v2 *tablet_manager; @@ -58,6 +61,20 @@ struct wlr_wl_buffer { bool released; struct wl_list link; // wlr_wl_backend.buffers struct wl_listener buffer_destroy; + + bool has_drm_syncobj_waiter; + struct wlr_drm_syncobj_timeline_waiter drm_syncobj_waiter; + struct wl_listener drm_syncobj_ready; + + struct wlr_drm_syncobj_timeline *fallback_signal_timeline; + uint64_t fallback_signal_point; +}; + +struct wlr_wl_drm_syncobj_timeline { + struct wlr_drm_syncobj_timeline *base; + struct wlr_addon addon; + struct wl_list link; // wlr_wl_backend.drm_syncobj_timelines + struct wp_linux_drm_syncobj_timeline_v1 *wl; }; struct wlr_wl_presentation_feedback { @@ -88,6 +105,7 @@ struct wlr_wl_output { struct xdg_surface *xdg_surface; struct xdg_toplevel *xdg_toplevel; struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1; + struct wp_linux_drm_syncobj_surface_v1 *drm_syncobj_surface_v1; struct wl_list presentation_feedbacks; char *title; @@ -190,6 +208,7 @@ bool create_wl_seat(struct wl_seat *wl_seat, struct wlr_wl_backend *wl, uint32_t global_name); void destroy_wl_seat(struct wlr_wl_seat *seat); void destroy_wl_buffer(struct wlr_wl_buffer *buffer); +void destroy_wl_drm_syncobj_timeline(struct wlr_wl_drm_syncobj_timeline *timeline); extern const struct wlr_pointer_impl wl_pointer_impl; extern const struct wlr_tablet_pad_impl wl_tablet_pad_impl; From 2c3053370c274f86411dda1c5fced6b1416d8ad1 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 7 Nov 2024 11:40:19 +0100 Subject: [PATCH 195/519] output-management-v1: only create custom mode object for enabled heads We were always creating a custom mode object when the output didn't have a fixed mode. This is important to handle two cases: - Virtual outputs with no concept of fixed modes. - DRM outputs with a list of fixed modes but with a custom mode set. However, in the case where an output didn't have a fixed mode and was disabled, we were also creating the custom mode object. Clients would then see a "ghost" mode: a mode object with no properties at all. Fix this by only creating the custom mode object if the output is enabled. Fixes: 5de9e1a99d66 ("wlr-output-management: Send custom modes to clients") Closes: https://github.com/swaywm/sway/issues/8420 --- types/wlr_output_management_v1.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/types/wlr_output_management_v1.c b/types/wlr_output_management_v1.c index 551453e6b..32f6b4ffe 100644 --- a/types/wlr_output_management_v1.c +++ b/types/wlr_output_management_v1.c @@ -866,7 +866,7 @@ static void manager_send_head(struct wlr_output_manager_v1 *manager, head_send_mode(head, head_resource, mode); } - if (output->current_mode == NULL) { + if (head->state.mode == NULL && head->state.enabled) { // Output doesn't have a fixed mode set. Send a virtual one. head_send_mode(head, head_resource, NULL); } @@ -926,7 +926,7 @@ static bool manager_update_head(struct wlr_output_manager_v1 *manager, } } - if (next->mode == NULL && !head_has_custom_mode_resources(head)) { + if (next->mode == NULL && next->enabled && !head_has_custom_mode_resources(head)) { struct wl_resource *resource; wl_resource_for_each(resource, &head->resources) { head_send_mode(head, resource, NULL); From 85e2b662f195bb2999cb906d691f4ab8580b20dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Mon, 11 Nov 2024 23:29:45 +0100 Subject: [PATCH 196/519] renderer: Drop mention of wlr_renderer_begin_with_buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It got removed in 3ed1268f ("render: Nuke old read pixels API") Signed-off-by: Guido Günther --- include/render/wlr_renderer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/render/wlr_renderer.h b/include/render/wlr_renderer.h index d9823dc8b..f6661384b 100644 --- a/include/render/wlr_renderer.h +++ b/include/render/wlr_renderer.h @@ -9,7 +9,7 @@ struct wlr_renderer *renderer_autocreate_with_drm_fd(int drm_fd); /** * Get the supported render formats. Buffers allocated with a format from this - * list may be attached via wlr_renderer_begin_with_buffer. + * list may be used with wlr_renderer_begin_buffer_pass(). */ const struct wlr_drm_format_set *wlr_renderer_get_render_formats( struct wlr_renderer *renderer); From 0b720ae5ea161619d8092ae8d44fd7e65117818e Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Tue, 12 Nov 2024 10:33:18 +0300 Subject: [PATCH 197/519] seat: drop last_event --- include/wlr/types/wlr_seat.h | 1 - types/seat/wlr_seat_keyboard.c | 3 --- types/seat/wlr_seat_pointer.c | 5 ----- types/seat/wlr_seat_touch.c | 3 --- 4 files changed, 12 deletions(-) diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h index 69ee69fdd..74b4341dd 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -250,7 +250,6 @@ struct wlr_seat { char *name; uint32_t capabilities; uint32_t accumulated_capabilities; - struct timespec last_event; struct wlr_data_source *selection_source; uint32_t selection_serial; diff --git a/types/seat/wlr_seat_keyboard.c b/types/seat/wlr_seat_keyboard.c index cae4c0db1..56f83b7f4 100644 --- a/types/seat/wlr_seat_keyboard.c +++ b/types/seat/wlr_seat_keyboard.c @@ -319,19 +319,16 @@ bool wlr_seat_keyboard_has_grab(struct wlr_seat *seat) { void wlr_seat_keyboard_notify_modifiers(struct wlr_seat *seat, const struct wlr_keyboard_modifiers *modifiers) { - clock_gettime(CLOCK_MONOTONIC, &seat->last_event); struct wlr_seat_keyboard_grab *grab = seat->keyboard_state.grab; grab->interface->modifiers(grab, modifiers); } void wlr_seat_keyboard_notify_key(struct wlr_seat *seat, uint32_t time, uint32_t key, uint32_t state) { - clock_gettime(CLOCK_MONOTONIC, &seat->last_event); struct wlr_seat_keyboard_grab *grab = seat->keyboard_state.grab; grab->interface->key(grab, time, key, state); } - static void seat_client_send_keymap(struct wlr_seat_client *client, struct wlr_keyboard *keyboard) { if (!keyboard) { diff --git a/types/seat/wlr_seat_pointer.c b/types/seat/wlr_seat_pointer.c index defe14e89..41729dcd9 100644 --- a/types/seat/wlr_seat_pointer.c +++ b/types/seat/wlr_seat_pointer.c @@ -464,15 +464,12 @@ void wlr_seat_pointer_notify_clear_focus(struct wlr_seat *wlr_seat) { void wlr_seat_pointer_notify_motion(struct wlr_seat *wlr_seat, uint32_t time, double sx, double sy) { - clock_gettime(CLOCK_MONOTONIC, &wlr_seat->last_event); struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab; grab->interface->motion(grab, time, sx, sy); } uint32_t wlr_seat_pointer_notify_button(struct wlr_seat *wlr_seat, uint32_t time, uint32_t button, enum wl_pointer_button_state state) { - clock_gettime(CLOCK_MONOTONIC, &wlr_seat->last_event); - struct wlr_seat_pointer_state* pointer_state = &wlr_seat->pointer_state; if (state == WL_POINTER_BUTTON_STATE_PRESSED) { @@ -527,14 +524,12 @@ void wlr_seat_pointer_notify_axis(struct wlr_seat *wlr_seat, uint32_t time, enum wl_pointer_axis orientation, double value, int32_t value_discrete, enum wl_pointer_axis_source source, enum wl_pointer_axis_relative_direction relative_direction) { - clock_gettime(CLOCK_MONOTONIC, &wlr_seat->last_event); struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab; grab->interface->axis(grab, time, orientation, value, value_discrete, source, relative_direction); } void wlr_seat_pointer_notify_frame(struct wlr_seat *wlr_seat) { - clock_gettime(CLOCK_MONOTONIC, &wlr_seat->last_event); struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab; if (grab->interface->frame) { grab->interface->frame(grab); diff --git a/types/seat/wlr_seat_touch.c b/types/seat/wlr_seat_touch.c index 3927bd230..6aac48841 100644 --- a/types/seat/wlr_seat_touch.c +++ b/types/seat/wlr_seat_touch.c @@ -181,7 +181,6 @@ struct wlr_touch_point *wlr_seat_touch_get_point( uint32_t wlr_seat_touch_notify_down(struct wlr_seat *seat, struct wlr_surface *surface, uint32_t time, int32_t touch_id, double sx, double sy) { - clock_gettime(CLOCK_MONOTONIC, &seat->last_event); struct wlr_seat_touch_grab *grab = seat->touch_state.grab; struct wlr_touch_point *point = touch_point_create(seat, touch_id, surface, sx, sy); @@ -207,7 +206,6 @@ uint32_t wlr_seat_touch_notify_down(struct wlr_seat *seat, uint32_t wlr_seat_touch_notify_up(struct wlr_seat *seat, uint32_t time, int32_t touch_id) { - clock_gettime(CLOCK_MONOTONIC, &seat->last_event); struct wlr_seat_touch_grab *grab = seat->touch_state.grab; struct wlr_touch_point *point = wlr_seat_touch_get_point(seat, touch_id); if (!point) { @@ -221,7 +219,6 @@ uint32_t wlr_seat_touch_notify_up(struct wlr_seat *seat, uint32_t time, void wlr_seat_touch_notify_motion(struct wlr_seat *seat, uint32_t time, int32_t touch_id, double sx, double sy) { - clock_gettime(CLOCK_MONOTONIC, &seat->last_event); struct wlr_seat_touch_grab *grab = seat->touch_state.grab; struct wlr_touch_point *point = wlr_seat_touch_get_point(seat, touch_id); if (!point) { From f233d25e86cc9a5e114f93e0161a2a4137493f46 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Sat, 16 Nov 2024 22:05:01 +0300 Subject: [PATCH 198/519] tinywl: don't pass surface to focus_toplevel() It's not required. --- tinywl/tinywl.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/tinywl/tinywl.c b/tinywl/tinywl.c index 6cf08b119..6b91d9a97 100644 --- a/tinywl/tinywl.c +++ b/tinywl/tinywl.c @@ -109,7 +109,7 @@ struct tinywl_keyboard { struct wl_listener destroy; }; -static void focus_toplevel(struct tinywl_toplevel *toplevel, struct wlr_surface *surface) { +static void focus_toplevel(struct tinywl_toplevel *toplevel) { /* Note: this function only deals with keyboard focus. */ if (toplevel == NULL) { return; @@ -117,6 +117,7 @@ static void focus_toplevel(struct tinywl_toplevel *toplevel, struct wlr_surface struct tinywl_server *server = toplevel->server; struct wlr_seat *seat = server->seat; struct wlr_surface *prev_surface = seat->keyboard_state.focused_surface; + struct wlr_surface *surface = toplevel->xdg_toplevel->base->surface; if (prev_surface == surface) { /* Don't re-focus an already focused surface. */ return; @@ -146,7 +147,7 @@ static void focus_toplevel(struct tinywl_toplevel *toplevel, struct wlr_surface * clients without additional work on your part. */ if (keyboard != NULL) { - wlr_seat_keyboard_notify_enter(seat, toplevel->xdg_toplevel->base->surface, + wlr_seat_keyboard_notify_enter(seat, surface, keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); } } @@ -188,7 +189,7 @@ static bool handle_keybinding(struct tinywl_server *server, xkb_keysym_t sym) { } struct tinywl_toplevel *next_toplevel = wl_container_of(server->toplevels.prev, next_toplevel, link); - focus_toplevel(next_toplevel, next_toplevel->xdg_toplevel->base->surface); + focus_toplevel(next_toplevel); break; default: return false; @@ -520,16 +521,16 @@ static void server_cursor_button(struct wl_listener *listener, void *data) { /* Notify the client with pointer focus that a button press has occurred */ wlr_seat_pointer_notify_button(server->seat, event->time_msec, event->button, event->state); - double sx, sy; - struct wlr_surface *surface = NULL; - struct tinywl_toplevel *toplevel = desktop_toplevel_at(server, - server->cursor->x, server->cursor->y, &surface, &sx, &sy); if (event->state == WL_POINTER_BUTTON_STATE_RELEASED) { /* If you released any buttons, we exit interactive move/resize mode. */ reset_cursor_mode(server); } else { /* Focus that client if the button was _pressed_ */ - focus_toplevel(toplevel, surface); + double sx, sy; + struct wlr_surface *surface = NULL; + struct tinywl_toplevel *toplevel = desktop_toplevel_at(server, + server->cursor->x, server->cursor->y, &surface, &sx, &sy); + focus_toplevel(toplevel); } } @@ -662,7 +663,7 @@ static void xdg_toplevel_map(struct wl_listener *listener, void *data) { wl_list_insert(&toplevel->server->toplevels, &toplevel->link); - focus_toplevel(toplevel, toplevel->xdg_toplevel->base->surface); + focus_toplevel(toplevel); } static void xdg_toplevel_unmap(struct wl_listener *listener, void *data) { From 2ff95e5c970b3653a5154cbb2351487a6643d177 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Sat, 16 Nov 2024 20:04:58 +0300 Subject: [PATCH 199/519] meson: use kwargs for wayland deps --- backend/wayland/meson.build | 3 +-- meson.build | 13 +++++++++---- protocol/meson.build | 5 ++++- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/backend/wayland/meson.build b/backend/wayland/meson.build index 9235b7fe3..b606a9bb9 100644 --- a/backend/wayland/meson.build +++ b/backend/wayland/meson.build @@ -1,6 +1,5 @@ wayland_client = dependency('wayland-client', - fallback: 'wayland', - default_options: wayland_project_options, + kwargs: wayland_kwargs, ) wlr_deps += wayland_client diff --git a/meson.build b/meson.build index caee73a46..453969507 100644 --- a/meson.build +++ b/meson.build @@ -83,11 +83,16 @@ internal_features = { } internal_config = configuration_data() -wayland_project_options = ['tests=false', 'documentation=false'] +wayland_kwargs = { + 'version': '>=1.23', + 'fallback': 'wayland', + 'default_options': [ + 'tests=false', + 'documentation=false', + ], +} wayland_server = dependency('wayland-server', - version: '>=1.23', - fallback: 'wayland', - default_options: wayland_project_options, + kwargs: wayland_kwargs, ) drm = dependency('libdrm', diff --git a/protocol/meson.build b/protocol/meson.build index f4aafaf57..2889fb033 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -5,7 +5,10 @@ wayland_protos = dependency('wayland-protocols', ) wl_protocol_dir = wayland_protos.get_variable('pkgdatadir') -wayland_scanner_dep = dependency('wayland-scanner', native: true) +wayland_scanner_dep = dependency('wayland-scanner', + kwargs: wayland_kwargs, + native: true, +) wayland_scanner = find_program( wayland_scanner_dep.get_variable('wayland_scanner'), native: true, From 24597bb971a1c7cd29fceaa6c9ada8f96686c083 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Sat, 16 Nov 2024 20:05:15 +0300 Subject: [PATCH 200/519] meson: require wayland >=1.23.1 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 453969507..690b77b35 100644 --- a/meson.build +++ b/meson.build @@ -84,7 +84,7 @@ internal_features = { internal_config = configuration_data() wayland_kwargs = { - 'version': '>=1.23', + 'version': '>=1.23.1', 'fallback': 'wayland', 'default_options': [ 'tests=false', From 0d6cc471e95c1632b234fc9152659d24fe5982f1 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Fri, 22 Nov 2024 09:55:51 -0500 Subject: [PATCH 201/519] scene: Assert all signals are clear when destroying --- types/scene/wlr_scene.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 6d14299ca..43e77ecd2 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -126,6 +126,12 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) { scene_buffer_set_texture(scene_buffer, NULL); pixman_region32_fini(&scene_buffer->opaque_region); wlr_drm_syncobj_timeline_unref(scene_buffer->wait_timeline); + + assert(wl_list_empty(&scene_buffer->events.output_leave.listener_list)); + assert(wl_list_empty(&scene_buffer->events.output_enter.listener_list)); + assert(wl_list_empty(&scene_buffer->events.outputs_update.listener_list)); + assert(wl_list_empty(&scene_buffer->events.output_sample.listener_list)); + assert(wl_list_empty(&scene_buffer->events.frame_done.listener_list)); } else if (node->type == WLR_SCENE_NODE_TREE) { struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); @@ -150,6 +156,8 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) { } } + assert(wl_list_empty(&node->events.destroy.listener_list)); + wl_list_remove(&node->link); pixman_region32_fini(&node->visible); free(node); @@ -1657,6 +1665,8 @@ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) { scene_node_output_update(&scene_output->scene->tree.node, &scene_output->scene->outputs, scene_output, NULL); + assert(wl_list_empty(&scene_output->events.destroy.listener_list)); + struct highlight_region *damage, *tmp_damage; wl_list_for_each_safe(damage, tmp_damage, &scene_output->damage_highlight_regions, link) { highlight_region_destroy(damage); From b2c3c371fcb39578464c5604e6010ff5f656cdc2 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 22 Nov 2024 19:21:00 +0100 Subject: [PATCH 202/519] scene: assert on bad wlr_scene_buffer params We assert in wlr_renderer if these are invalid. Assert earlier to make debugging easier. --- types/scene/wlr_scene.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 43e77ecd2..58d1cc3f0 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1015,6 +1015,7 @@ void wlr_scene_buffer_set_source_box(struct wlr_scene_buffer *scene_buffer, } if (box != NULL) { + assert(box->x >= 0 && box->y >= 0 && box->width >= 0 && box->height >= 0); scene_buffer->src_box = *box; } else { scene_buffer->src_box = (struct wlr_fbox){0}; @@ -1029,6 +1030,7 @@ void wlr_scene_buffer_set_dest_size(struct wlr_scene_buffer *scene_buffer, return; } + assert(width >= 0 && height >= 0); scene_buffer->dst_width = width; scene_buffer->dst_height = height; scene_node_update(&scene_buffer->node, NULL); @@ -1057,6 +1059,7 @@ void wlr_scene_buffer_set_opacity(struct wlr_scene_buffer *scene_buffer, return; } + assert(opacity >= 0 && opacity <= 1); scene_buffer->opacity = opacity; scene_node_update(&scene_buffer->node, NULL); } From 9aca9858654bd5adc0a25116e554bb9e7767ab16 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Sat, 23 Nov 2024 00:49:53 +0300 Subject: [PATCH 203/519] tinywl: don't pass time to cursor mode motion handlers It's unused. --- tinywl/tinywl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tinywl/tinywl.c b/tinywl/tinywl.c index 6b91d9a97..915938485 100644 --- a/tinywl/tinywl.c +++ b/tinywl/tinywl.c @@ -378,7 +378,7 @@ static void reset_cursor_mode(struct tinywl_server *server) { server->grabbed_toplevel = NULL; } -static void process_cursor_move(struct tinywl_server *server, uint32_t time) { +static void process_cursor_move(struct tinywl_server *server) { /* Move the grabbed toplevel to the new position. */ struct tinywl_toplevel *toplevel = server->grabbed_toplevel; wlr_scene_node_set_position(&toplevel->scene_tree->node, @@ -386,7 +386,7 @@ static void process_cursor_move(struct tinywl_server *server, uint32_t time) { server->cursor->y - server->grab_y); } -static void process_cursor_resize(struct tinywl_server *server, uint32_t time) { +static void process_cursor_resize(struct tinywl_server *server) { /* * Resizing the grabbed toplevel can be a little bit complicated, because we * could be resizing from any corner or edge. This not only resizes the @@ -440,10 +440,10 @@ static void process_cursor_resize(struct tinywl_server *server, uint32_t time) { static void process_cursor_motion(struct tinywl_server *server, uint32_t time) { /* If the mode is non-passthrough, delegate to those functions. */ if (server->cursor_mode == TINYWL_CURSOR_MOVE) { - process_cursor_move(server, time); + process_cursor_move(server); return; } else if (server->cursor_mode == TINYWL_CURSOR_RESIZE) { - process_cursor_resize(server, time); + process_cursor_resize(server); return; } From 3e651b4642de779f208094831d2ae7d9f419b417 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 23 Nov 2024 15:29:44 +0100 Subject: [PATCH 204/519] backend/drm: fix drmModePageFlip() when disabling CRTC on legacy uAPI drmModePageFlip() will fail with EBUSY on a disabled CRTC. We can't fix this by clearing the DRM_MODE_PAGE_FLIP_EVENT when performing a commit which disables CRTCs, because some device-wide commits might also page-flip other enabled CRTCs (and skipping the page-flip event would mess up our buffer tracking). Fix this by immediately completing page-flips which disable a CRTC on the legacy uAPI. Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3918 --- backend/drm/drm.c | 10 ++++++++++ backend/drm/legacy.c | 6 ++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 9316d7b2c..4fd7f14e7 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -582,6 +582,16 @@ static void drm_connector_apply_commit(const struct wlr_drm_connector_state *sta conn->cursor_enabled = false; conn->crtc = NULL; + + // Legacy uAPI doesn't support requesting page-flip events when + // turning off a CRTC + if (page_flip != NULL && conn->backend->iface == &legacy_iface) { + drm_page_flip_pop(page_flip, crtc->id); + conn->pending_page_flip = NULL; + if (page_flip->connectors_len == 0) { + drm_page_flip_destroy(page_flip); + } + } } } diff --git a/backend/drm/legacy.c b/backend/drm/legacy.c index dddf0b1fd..d77c9d342 100644 --- a/backend/drm/legacy.c +++ b/backend/drm/legacy.c @@ -150,7 +150,7 @@ static bool legacy_crtc_commit(const struct wlr_drm_connector_state *state, state->base->adaptive_sync_enabled ? "enabled" : "disabled"); } - if (cursor != NULL && drm_connector_is_cursor_visible(conn)) { + if (cursor != NULL && state->active && drm_connector_is_cursor_visible(conn)) { struct wlr_drm_fb *cursor_fb = state->cursor_fb; if (cursor_fb == NULL) { wlr_drm_conn_log(conn, WLR_DEBUG, "Failed to acquire cursor FB"); @@ -192,7 +192,9 @@ static bool legacy_crtc_commit(const struct wlr_drm_connector_state *state, } } - if (flags & DRM_MODE_PAGE_FLIP_EVENT) { + // Legacy uAPI doesn't support requesting page-flip events when + // turning off a CRTC + if (state->active && (flags & DRM_MODE_PAGE_FLIP_EVENT)) { if (drmModePageFlip(drm->fd, crtc->id, fb_id, flags, page_flip)) { wlr_drm_conn_log_errno(conn, WLR_ERROR, "drmModePageFlip failed"); return false; From c0d4d7217b56a0535e5828a8ce592dca7102e116 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Fri, 22 Nov 2024 20:37:39 +0300 Subject: [PATCH 205/519] Add missing destroy events on automatically destroyed objects --- include/wlr/types/wlr_drm_lease_v1.h | 2 ++ include/wlr/types/wlr_pointer_constraints_v1.h | 2 ++ include/wlr/types/wlr_transient_seat_v1.h | 2 ++ include/wlr/types/wlr_xdg_system_bell_v1.h | 1 + include/wlr/xwayland/xwayland.h | 1 + types/wlr_drm_lease_v1.c | 3 +++ types/wlr_pointer_constraints_v1.c | 3 +++ types/wlr_transient_seat_v1.c | 3 ++- types/wlr_xdg_system_bell_v1.c | 2 ++ xwayland/xwayland.c | 3 +++ 10 files changed, 21 insertions(+), 1 deletion(-) diff --git a/include/wlr/types/wlr_drm_lease_v1.h b/include/wlr/types/wlr_drm_lease_v1.h index 05e381249..752f6efa7 100644 --- a/include/wlr/types/wlr_drm_lease_v1.h +++ b/include/wlr/types/wlr_drm_lease_v1.h @@ -20,6 +20,8 @@ struct wlr_drm_lease_v1_manager { struct wl_display *display; struct { + struct wl_signal destroy; + /** * Upon receiving this signal, call * wlr_drm_lease_device_v1_grant_lease_request() to grant a lease of the diff --git a/include/wlr/types/wlr_pointer_constraints_v1.h b/include/wlr/types/wlr_pointer_constraints_v1.h index e0f588065..4c541d879 100644 --- a/include/wlr/types/wlr_pointer_constraints_v1.h +++ b/include/wlr/types/wlr_pointer_constraints_v1.h @@ -78,6 +78,8 @@ struct wlr_pointer_constraints_v1 { struct wl_list constraints; // wlr_pointer_constraint_v1.link struct { + struct wl_signal destroy; + /** * Called when a new pointer constraint is created. * diff --git a/include/wlr/types/wlr_transient_seat_v1.h b/include/wlr/types/wlr_transient_seat_v1.h index 9aafed3b4..d996016bf 100644 --- a/include/wlr/types/wlr_transient_seat_v1.h +++ b/include/wlr/types/wlr_transient_seat_v1.h @@ -26,6 +26,8 @@ struct wlr_transient_seat_manager_v1 { struct wl_global *global; struct { + struct wl_signal destroy; + /** * Upon receiving this signal, call * wlr_transient_seat_v1_ready() to pass a newly created seat diff --git a/include/wlr/types/wlr_xdg_system_bell_v1.h b/include/wlr/types/wlr_xdg_system_bell_v1.h index 8cb592d14..c58bcbbc5 100644 --- a/include/wlr/types/wlr_xdg_system_bell_v1.h +++ b/include/wlr/types/wlr_xdg_system_bell_v1.h @@ -15,6 +15,7 @@ struct wlr_xdg_system_bell_v1 { struct wl_global *global; struct { + struct wl_signal destroy; struct wl_signal ring; // struct wlr_xdg_system_bell_v1_ring_event } events; diff --git a/include/wlr/xwayland/xwayland.h b/include/wlr/xwayland/xwayland.h index dbd881fda..fcfa5be75 100644 --- a/include/wlr/xwayland/xwayland.h +++ b/include/wlr/xwayland/xwayland.h @@ -51,6 +51,7 @@ struct wlr_xwayland { struct wlr_seat *seat; struct { + struct wl_signal destroy; struct wl_signal ready; struct wl_signal new_surface; // struct wlr_xwayland_surface struct wl_signal remove_startup_info; // struct wlr_xwayland_remove_startup_info_event diff --git a/types/wlr_drm_lease_v1.c b/types/wlr_drm_lease_v1.c index d3c9ba1ff..52010ea0d 100644 --- a/types/wlr_drm_lease_v1.c +++ b/types/wlr_drm_lease_v1.c @@ -682,6 +682,8 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { display_destroy); wlr_log(WLR_DEBUG, "Destroying wlr_drm_lease_v1_manager"); + wl_signal_emit_mutable(&manager->events.destroy, NULL); + struct wlr_drm_lease_device_v1 *device, *tmp; wl_list_for_each_safe(device, tmp, &manager->devices, link) { drm_lease_device_v1_destroy(device); @@ -718,6 +720,7 @@ struct wlr_drm_lease_v1_manager *wlr_drm_lease_v1_manager_create( manager->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &manager->display_destroy); + wl_signal_init(&manager->events.destroy); wl_signal_init(&manager->events.request); return manager; diff --git a/types/wlr_pointer_constraints_v1.c b/types/wlr_pointer_constraints_v1.c index 078037293..4560c273b 100644 --- a/types/wlr_pointer_constraints_v1.c +++ b/types/wlr_pointer_constraints_v1.c @@ -322,6 +322,7 @@ static void pointer_constraints_bind(struct wl_client *client, void *data, static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_pointer_constraints_v1 *pointer_constraints = wl_container_of(listener, pointer_constraints, display_destroy); + wl_signal_emit_mutable(&pointer_constraints->events.destroy, NULL); wl_list_remove(&pointer_constraints->display_destroy.link); wl_global_destroy(pointer_constraints->global); free(pointer_constraints); @@ -345,6 +346,8 @@ struct wlr_pointer_constraints_v1 *wlr_pointer_constraints_v1_create( pointer_constraints->global = wl_global; wl_list_init(&pointer_constraints->constraints); + + wl_signal_init(&pointer_constraints->events.destroy); wl_signal_init(&pointer_constraints->events.new_constraint); pointer_constraints->display_destroy.notify = handle_display_destroy; diff --git a/types/wlr_transient_seat_v1.c b/types/wlr_transient_seat_v1.c index 379ce0a37..717b36dc0 100644 --- a/types/wlr_transient_seat_v1.c +++ b/types/wlr_transient_seat_v1.c @@ -114,7 +114,7 @@ static const struct ext_transient_seat_manager_v1_interface manager_impl = { static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_transient_seat_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); - + wl_signal_emit_mutable(&manager->events.destroy, NULL); wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); @@ -154,6 +154,7 @@ struct wlr_transient_seat_manager_v1 *wlr_transient_seat_manager_v1_create( manager->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &manager->display_destroy); + wl_signal_init(&manager->events.destroy); wl_signal_init(&manager->events.create_seat); return manager; diff --git a/types/wlr_xdg_system_bell_v1.c b/types/wlr_xdg_system_bell_v1.c index d1ad7b002..8a8850540 100644 --- a/types/wlr_xdg_system_bell_v1.c +++ b/types/wlr_xdg_system_bell_v1.c @@ -51,6 +51,7 @@ static void bell_bind(struct wl_client *client, void *data, static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_xdg_system_bell_v1 *bell = wl_container_of(listener, bell, display_destroy); + wl_signal_emit_mutable(&bell->events.destroy, NULL); wl_list_remove(&bell->display_destroy.link); wl_global_destroy(bell->global); free(bell); @@ -75,6 +76,7 @@ struct wlr_xdg_system_bell_v1 *wlr_xdg_system_bell_v1_create(struct wl_display * bell->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &bell->display_destroy); + wl_signal_init(&bell->events.destroy); wl_signal_init(&bell->events.ring); return bell; diff --git a/xwayland/xwayland.c b/xwayland/xwayland.c index 23c21b438..7fbc33ce1 100644 --- a/xwayland/xwayland.c +++ b/xwayland/xwayland.c @@ -79,6 +79,8 @@ void wlr_xwayland_destroy(struct wlr_xwayland *xwayland) { return; } + wl_signal_emit_mutable(&xwayland->events.destroy, NULL); + wl_list_remove(&xwayland->server_destroy.link); wl_list_remove(&xwayland->server_start.link); wl_list_remove(&xwayland->server_ready.link); @@ -105,6 +107,7 @@ struct wlr_xwayland *wlr_xwayland_create_with_server(struct wl_display *wl_displ xwayland->wl_display = wl_display; xwayland->compositor = compositor; + wl_signal_init(&xwayland->events.destroy); wl_signal_init(&xwayland->events.new_surface); wl_signal_init(&xwayland->events.ready); wl_signal_init(&xwayland->events.remove_startup_info); From e21899037a97dcade40149f05ab54229c0018644 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Tue, 19 Nov 2024 10:13:23 +0900 Subject: [PATCH 206/519] wlr_keyboard: don't emit key event for duplicated keycodes This fixes the memory leak in wlr_keyboard_group.keys. The leak happened because wlr_keyboard.keycodes never contains duplicated keycodes while wlr_keyboard_group.keys can, so calling wlr_keyboard_finish() for all the wlr_keyboards in wlr_keyboard_group doesn't always free all the keys in wlr_keyboard_group.keys. --- include/types/wlr_keyboard.h | 2 +- types/wlr_keyboard.c | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/include/types/wlr_keyboard.h b/include/types/wlr_keyboard.h index 7cd26e8a5..bdae09bdd 100644 --- a/include/types/wlr_keyboard.h +++ b/include/types/wlr_keyboard.h @@ -1,6 +1,6 @@ #include -void keyboard_key_update(struct wlr_keyboard *keyboard, +bool keyboard_key_update(struct wlr_keyboard *keyboard, struct wlr_keyboard_key_event *event); bool keyboard_modifier_update(struct wlr_keyboard *keyboard); diff --git a/types/wlr_keyboard.c b/types/wlr_keyboard.c index 81d45a624..5eff3ba39 100644 --- a/types/wlr_keyboard.c +++ b/types/wlr_keyboard.c @@ -66,8 +66,9 @@ bool keyboard_modifier_update(struct wlr_keyboard *keyboard) { return true; } -void keyboard_key_update(struct wlr_keyboard *keyboard, +bool keyboard_key_update(struct wlr_keyboard *keyboard, struct wlr_keyboard_key_event *event) { + size_t old_num_keycodes = keyboard->num_keycodes; if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { set_add(keyboard->keycodes, &keyboard->num_keycodes, WLR_KEYBOARD_KEYS_CAP, event->keycode); @@ -78,6 +79,8 @@ void keyboard_key_update(struct wlr_keyboard *keyboard, } assert(keyboard->num_keycodes <= WLR_KEYBOARD_KEYS_CAP); + + return old_num_keycodes != keyboard->num_keycodes; } void wlr_keyboard_notify_modifiers(struct wlr_keyboard *keyboard, @@ -99,8 +102,9 @@ void wlr_keyboard_notify_modifiers(struct wlr_keyboard *keyboard, void wlr_keyboard_notify_key(struct wlr_keyboard *keyboard, struct wlr_keyboard_key_event *event) { - keyboard_key_update(keyboard, event); - wl_signal_emit_mutable(&keyboard->events.key, event); + if (keyboard_key_update(keyboard, event)) { + wl_signal_emit_mutable(&keyboard->events.key, event); + } if (keyboard->xkb_state == NULL) { return; From 2424b1ecddd78e6a18ebfb31ac3bbac52e4dcc0c Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 28 Nov 2024 20:00:36 +0100 Subject: [PATCH 207/519] render/drm_format_set: fix corruption in wlr_drm_format_set_remove() A single byte was moved instead of a full uint64_t. This results in corrupted lists containing bogus modifiers. --- render/drm_format_set.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/render/drm_format_set.c b/render/drm_format_set.c index 9749a0093..58d65290a 100644 --- a/render/drm_format_set.c +++ b/render/drm_format_set.c @@ -95,7 +95,7 @@ bool wlr_drm_format_set_remove(struct wlr_drm_format_set *set, uint32_t format, for (size_t idx = 0; idx < fmt->len; idx++) { if (fmt->modifiers[idx] == modifier) { - memmove(&fmt->modifiers[idx], &fmt->modifiers[idx+1], fmt->len - idx - 1); + memmove(&fmt->modifiers[idx], &fmt->modifiers[idx+1], (fmt->len - idx - 1) * sizeof(fmt->modifiers[0])); fmt->len--; return true; } From c6dd5e3c2ef82ddba4d1877498f594ceeffb3939 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 28 Nov 2024 20:08:44 +0100 Subject: [PATCH 208/519] backend/drm: check buffer format for multi-GPU Fixes on-screen corruption when displaying a fullscreen client with an implicit modifier on the secondary GPU. What happens here: - Client allocates a buffer with an INVALID modifier on primary GPU. - Compositor attempts to scan-out this buffer on an output connected to secondary GPU. - Buffer is imported to secondary GPU, and is interpreted as if it had the secondary GPU's implicit tiling, even though it has the primary GPU's implicit tiling. We need to forbid cross-device imports with implicit modifiers. The mgpu_formats list is stripped from any INVALID modifier so checking that fixes the bug. Using the mgpu_formats list has an additional benefit: the buffer is rejected in the test commit if it doesn't have a format supported by the multi-GPU renderer. Requires this Mesa bugfix: https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/31725 --- backend/drm/drm.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 4fd7f14e7..52bcae06b 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -845,6 +845,22 @@ static bool drm_connector_prepare(struct wlr_drm_connector_state *conn_state, bo return false; } + if ((state->committed & WLR_OUTPUT_STATE_BUFFER) && conn->backend->mgpu_renderer.wlr_rend) { + struct wlr_dmabuf_attributes dmabuf; + if (!wlr_buffer_get_dmabuf(state->buffer, &dmabuf)) { + wlr_drm_conn_log(conn, WLR_DEBUG, "Buffer is not a DMA-BUF"); + return false; + } + + if (!wlr_drm_format_set_has(&conn->backend->mgpu_formats, dmabuf.format, dmabuf.modifier)) { + wlr_drm_conn_log(conn, WLR_DEBUG, + "Buffer format 0x%"PRIX32" with modifier 0x%"PRIX64" cannot be " + "imported into multi-GPU renderer", + dmabuf.format, dmabuf.modifier); + return false; + } + } + if (test_only && conn->backend->mgpu_renderer.wlr_rend) { // If we're running as a secondary GPU, we can't perform an atomic // commit without blitting a buffer. From be3d2b74cf4e6cfc9644a5c819be8ccd4ac81a1c Mon Sep 17 00:00:00 2001 From: Consolatis <40171-Consolatis@users.noreply.gitlab.freedesktop.org> Date: Fri, 22 Nov 2024 18:29:37 +0100 Subject: [PATCH 209/519] dnd: ensure internal dnd handlers are unlinked on xwm_destroy() Fixes #3925 --- include/xwayland/xwm.h | 1 + xwayland/selection/dnd.c | 13 +++++++++++++ xwayland/xwm.c | 4 ++++ 3 files changed, 18 insertions(+) diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h index 8b4bbdb54..df1a3285b 100644 --- a/include/xwayland/xwm.h +++ b/include/xwayland/xwm.h @@ -161,6 +161,7 @@ void xwm_set_cursor(struct wlr_xwm *xwm, const uint8_t *pixels, uint32_t stride, int xwm_handle_selection_event(struct wlr_xwm *xwm, xcb_generic_event_t *event); int xwm_handle_selection_client_message(struct wlr_xwm *xwm, xcb_client_message_event_t *ev); +void xwm_seat_unlink_drag_handlers(struct wlr_xwm *xwm); void xwm_set_seat(struct wlr_xwm *xwm, struct wlr_seat *seat); diff --git a/xwayland/selection/dnd.c b/xwayland/selection/dnd.c index 166e4e637..5918b813a 100644 --- a/xwayland/selection/dnd.c +++ b/xwayland/selection/dnd.c @@ -315,6 +315,7 @@ static void seat_handle_drag_source_destroy(struct wl_listener *listener, wl_container_of(listener, xwm, seat_drag_source_destroy); wl_list_remove(&xwm->seat_drag_source_destroy.link); + wl_list_init(&xwm->seat_drag_source_destroy.link); xwm->drag_focus = NULL; } @@ -337,3 +338,15 @@ void xwm_seat_handle_start_drag(struct wlr_xwm *xwm, struct wlr_drag *drag) { xwm->seat_drag_source_destroy.notify = seat_handle_drag_source_destroy; } } + +void xwm_seat_unlink_drag_handlers(struct wlr_xwm *xwm) { + wl_list_remove(&xwm->seat_drag_source_destroy.link); + + if (!xwm->drag) { + return; + } + wl_list_remove(&xwm->seat_drag_focus.link); + wl_list_remove(&xwm->seat_drag_motion.link); + wl_list_remove(&xwm->seat_drag_drop.link); + wl_list_remove(&xwm->seat_drag_destroy.link); +} diff --git a/xwayland/xwm.c b/xwayland/xwm.c index b3e11ef41..ee9a28233 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -1968,6 +1968,8 @@ void xwm_destroy(struct wlr_xwm *xwm) { xwm_selection_finish(&xwm->primary_selection); xwm_selection_finish(&xwm->dnd_selection); + xwm_seat_unlink_drag_handlers(xwm); + if (xwm->seat) { if (xwm->seat->selection_source && data_source_is_xwayland(xwm->seat->selection_source)) { @@ -2262,6 +2264,8 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) { wl_list_init(&xwm->surfaces_in_stack_order); wl_list_init(&xwm->unpaired_surfaces); wl_list_init(&xwm->pending_startup_ids); + wl_list_init(&xwm->seat_drag_source_destroy.link); + xwm->ping_timeout = 10000; xwm->xcb_conn = xcb_connect_to_fd(wm_fd, NULL); From 631e5be0d7a7e4c7086b9778bc8fac809f96d336 Mon Sep 17 00:00:00 2001 From: Tycho Andersen Date: Wed, 27 Nov 2024 08:16:16 -0700 Subject: [PATCH 210/519] xwayland: don't fail on SIGCHLD SIGCHLD here isn't fatal: we have other means of notifying that things were successful or failure, and it causes many compositors to have to do a bunch of extra work: https://github.com/qtile/qtile/issues/5101 https://github.com/flacjacket/pywlroots/pull/207#issuecomment-2502680133 https://github.com/djpohly/dwl/pull/212 Signed-off-by: Tycho Andersen --- xwayland/server.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/xwayland/server.c b/xwayland/server.c index 4f92879d1..0f3956fcb 100644 --- a/xwayland/server.c +++ b/xwayland/server.c @@ -257,6 +257,17 @@ static int xserver_handle_ready(int fd, uint32_t mask, void *data) { if (errno == EINTR) { continue; } + + /* If some application has installed a SIGCHLD handler, they + * may race and waitpid() on our child, which will cause this + * waitpid() to fail. We have a signal from the + * notify pipe that things are ready, so this waitpid() is only + * to prevent zombies, which will have already been reaped by + * the application's SIGCHLD handler. + */ + if (errno == ECHILD) { + break; + } wlr_log_errno(WLR_ERROR, "waitpid for Xwayland fork failed"); goto error; } From 9649fbe443a96680f47e550ebaf37bba0fe44c9e Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 6 Dec 2024 10:27:24 +0100 Subject: [PATCH 211/519] render/vulkan: fix crash on OOM Closes: https://github.com/swaywm/sway/issues/8485 --- render/vulkan/renderer.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index 44eb89f96..337ded3a2 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -269,6 +269,8 @@ struct wlr_vk_buffer_span vulkan_get_stage_span(struct wlr_vk_renderer *r, goto error_alloc; } + wl_list_init(&buf->link); + VkResult res; VkBufferCreateInfo buf_info = { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, From c7acfe906bae81f395a12b3351c320a56b559fc2 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 5 Dec 2024 20:21:56 +0100 Subject: [PATCH 212/519] xwayland: listen to drag focus destroy signal The wlr_drag takes care of resetting the focused wlr_surface when it's destroyed, however we store the wlr_xwayland_surface, which may be destroyed before. --- include/xwayland/xwm.h | 1 + xwayland/selection/dnd.c | 53 ++++++++++++++++++++++++++++------------ xwayland/xwm.c | 1 + 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h index df1a3285b..ec81fa3c4 100644 --- a/include/xwayland/xwm.h +++ b/include/xwayland/xwm.h @@ -149,6 +149,7 @@ struct wlr_xwm { struct wl_listener seat_drag_drop; struct wl_listener seat_drag_destroy; struct wl_listener seat_drag_source_destroy; + struct wl_listener drag_focus_destroy; }; struct wlr_xwm *xwm_create(struct wlr_xwayland *wlr_xwayland, int wm_fd); diff --git a/xwayland/selection/dnd.c b/xwayland/selection/dnd.c index 5918b813a..536abe0c2 100644 --- a/xwayland/selection/dnd.c +++ b/xwayland/selection/dnd.c @@ -234,6 +234,37 @@ int xwm_handle_selection_client_message(struct wlr_xwm *xwm, } } +static void xwm_set_drag_focus(struct wlr_xwm *xwm, struct wlr_xwayland_surface *focus); + +static void drag_focus_handle_destroy(struct wl_listener *listener, void *data) { + struct wlr_xwm *xwm = wl_container_of(listener, xwm, drag_focus_destroy); + xwm_set_drag_focus(xwm, NULL); +} + +static void xwm_set_drag_focus(struct wlr_xwm *xwm, struct wlr_xwayland_surface *focus) { + if (focus == xwm->drag_focus) { + return; + } + + if (xwm->drag_focus != NULL) { + wlr_data_source_dnd_action(xwm->drag->source, + WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE); + xwm_dnd_send_leave(xwm); + } + + wl_list_remove(&xwm->drag_focus_destroy.link); + wl_list_init(&xwm->drag_focus_destroy.link); + + xwm->drag_focus = focus; + + if (xwm->drag_focus != NULL) { + xwm->drag_focus_destroy.notify = drag_focus_handle_destroy; + wl_signal_add(&xwm->drag_focus->events.destroy, &xwm->drag_focus_destroy); + + xwm_dnd_send_enter(xwm); + } +} + static void seat_handle_drag_focus(struct wl_listener *listener, void *data) { struct wlr_drag *drag = data; struct wlr_xwm *xwm = wl_container_of(listener, xwm, seat_drag_focus); @@ -250,21 +281,7 @@ static void seat_handle_drag_focus(struct wl_listener *listener, void *data) { } } - if (focus == xwm->drag_focus) { - return; - } - - if (xwm->drag_focus != NULL) { - wlr_data_source_dnd_action(drag->source, - WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE); - xwm_dnd_send_leave(xwm); - } - - xwm->drag_focus = focus; - - if (xwm->drag_focus != NULL) { - xwm_dnd_send_enter(xwm); - } + xwm_set_drag_focus(xwm, focus); } static void seat_handle_drag_motion(struct wl_listener *listener, void *data) { @@ -316,10 +333,15 @@ static void seat_handle_drag_source_destroy(struct wl_listener *listener, wl_list_remove(&xwm->seat_drag_source_destroy.link); wl_list_init(&xwm->seat_drag_source_destroy.link); + wl_list_remove(&xwm->drag_focus_destroy.link); + wl_list_init(&xwm->drag_focus_destroy.link); xwm->drag_focus = NULL; } void xwm_seat_handle_start_drag(struct wlr_xwm *xwm, struct wlr_drag *drag) { + wl_list_remove(&xwm->drag_focus_destroy.link); + wl_list_init(&xwm->drag_focus_destroy.link); + xwm->drag = drag; xwm->drag_focus = NULL; @@ -341,6 +363,7 @@ void xwm_seat_handle_start_drag(struct wlr_xwm *xwm, struct wlr_drag *drag) { void xwm_seat_unlink_drag_handlers(struct wlr_xwm *xwm) { wl_list_remove(&xwm->seat_drag_source_destroy.link); + wl_list_remove(&xwm->drag_focus_destroy.link); if (!xwm->drag) { return; diff --git a/xwayland/xwm.c b/xwayland/xwm.c index ee9a28233..f7537dacb 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -2265,6 +2265,7 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) { wl_list_init(&xwm->unpaired_surfaces); wl_list_init(&xwm->pending_startup_ids); wl_list_init(&xwm->seat_drag_source_destroy.link); + wl_list_init(&xwm->drag_focus_destroy.link); xwm->ping_timeout = 10000; From a7ebe7c0267b54621e0aa802632db644d3093f37 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 5 Dec 2024 20:23:29 +0100 Subject: [PATCH 213/519] xwayland: remove loop to find drag focus surface We can just use wlr_xwayland_surface_try_from_wlr_surface() here instead. --- xwayland/selection/dnd.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/xwayland/selection/dnd.c b/xwayland/selection/dnd.c index 536abe0c2..ab1f4d6a6 100644 --- a/xwayland/selection/dnd.c +++ b/xwayland/selection/dnd.c @@ -271,14 +271,7 @@ static void seat_handle_drag_focus(struct wl_listener *listener, void *data) { struct wlr_xwayland_surface *focus = NULL; if (drag->focus != NULL) { - // TODO: check for subsurfaces? - struct wlr_xwayland_surface *surface; - wl_list_for_each(surface, &xwm->surfaces, link) { - if (surface->surface == drag->focus) { - focus = surface; - break; - } - } + focus = wlr_xwayland_surface_try_from_wlr_surface(drag->focus); } xwm_set_drag_focus(xwm, focus); From 71943b3b1ea0ea4584337a145746026567c51a89 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 6 Dec 2024 16:42:26 +0100 Subject: [PATCH 214/519] data-device: reset focused surface when destroyed The surface may be destroyed before the struct wlr_seat_client. Spotted by Consolatis. --- include/wlr/types/wlr_data_device.h | 1 + types/data_device/wlr_drag.c | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/include/wlr/types/wlr_data_device.h b/include/wlr/types/wlr_data_device.h index 2a618d3fe..c36b1a5cb 100644 --- a/include/wlr/types/wlr_data_device.h +++ b/include/wlr/types/wlr_data_device.h @@ -135,6 +135,7 @@ struct wlr_drag { struct { struct wl_listener source_destroy; struct wl_listener seat_client_destroy; + struct wl_listener focus_destroy; struct wl_listener icon_destroy; } WLR_PRIVATE; }; diff --git a/types/data_device/wlr_drag.c b/types/data_device/wlr_drag.c index 9ccd53c94..6338a4388 100644 --- a/types/data_device/wlr_drag.c +++ b/types/data_device/wlr_drag.c @@ -19,6 +19,14 @@ static void drag_handle_seat_client_destroy(struct wl_listener *listener, wl_list_remove(&drag->seat_client_destroy.link); } +static void drag_set_focus(struct wlr_drag *drag, + struct wlr_surface *surface, double sx, double sy); + +static void drag_handle_focus_destroy(struct wl_listener *listener, void *data) { + struct wlr_drag *drag = wl_container_of(listener, drag, focus_destroy); + drag_set_focus(drag, NULL, 0, 0); +} + static void drag_set_focus(struct wlr_drag *drag, struct wlr_surface *surface, double sx, double sy) { if (drag->focus == surface) { @@ -48,9 +56,12 @@ static void drag_set_focus(struct wlr_drag *drag, } drag->focus_client = NULL; - drag->focus = NULL; } + wl_list_remove(&drag->focus_destroy.link); + wl_list_init(&drag->focus_destroy.link); + drag->focus = NULL; + if (!surface) { goto out; } @@ -99,6 +110,8 @@ static void drag_set_focus(struct wlr_drag *drag, drag->focus = surface; drag->focus_client = focus_client; + drag->focus_destroy.notify = drag_handle_focus_destroy; + wl_signal_add(&surface->events.destroy, &drag->focus_destroy); drag->seat_client_destroy.notify = drag_handle_seat_client_destroy; wl_signal_add(&focus_client->events.destroy, &drag->seat_client_destroy); @@ -150,6 +163,7 @@ static void drag_destroy(struct wlr_drag *drag) { if (drag->source) { wl_list_remove(&drag->source_destroy.link); } + wl_list_remove(&drag->focus_destroy.link); if (drag->icon != NULL) { drag_icon_destroy(drag->icon); @@ -410,6 +424,8 @@ struct wlr_drag *wlr_drag_create(struct wlr_seat_client *seat_client, wl_signal_init(&drag->events.drop); wl_signal_init(&drag->events.destroy); + wl_list_init(&drag->focus_destroy.link); + drag->seat = seat_client->seat; drag->seat_client = seat_client; From 546c5d000d2696f423cd870780bf2378b6cc6a6b Mon Sep 17 00:00:00 2001 From: Consolatis <40171-Consolatis@users.noreply.gitlab.freedesktop.org> Date: Thu, 28 Nov 2024 04:46:33 +0100 Subject: [PATCH 215/519] xwayland: fix xdg->xwayland drag-and-drop As struct wlr_drag is destroyed on drop and in the process resets the focus, a xwayland dnd listener would also reset xwm->drag_focus. This prevents the xcb replies from being processed and also prevents the transfer if a compositor would not additionally request new focus in its wlr_drag destroy handler (which is something usually only done when in a focus-follows-mouse setting). This patch creates a new xwm->drop_focus pointer which is a copy of xwm->drag_focus at drop time. The xcb reply handler and transfer logic now use the new xwm->drop_focus for their authorization checks. --- include/xwayland/xwm.h | 2 ++ xwayland/selection/dnd.c | 27 ++++++++++++++++++++++++--- xwayland/selection/outgoing.c | 5 ++++- xwayland/xwm.c | 1 + 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h index ec81fa3c4..dc304b567 100644 --- a/include/xwayland/xwm.h +++ b/include/xwayland/xwm.h @@ -128,6 +128,7 @@ struct wlr_xwm { struct wlr_drag *drag; struct wlr_xwayland_surface *drag_focus; + struct wlr_xwayland_surface *drop_focus; const xcb_query_extension_reply_t *xfixes; const xcb_query_extension_reply_t *xres; @@ -150,6 +151,7 @@ struct wlr_xwm { struct wl_listener seat_drag_destroy; struct wl_listener seat_drag_source_destroy; struct wl_listener drag_focus_destroy; + struct wl_listener drop_focus_destroy; }; struct wlr_xwm *xwm_create(struct wlr_xwayland *wlr_xwayland, int wm_fd); diff --git a/xwayland/selection/dnd.c b/xwayland/selection/dnd.c index ab1f4d6a6..cdbcd1e9c 100644 --- a/xwayland/selection/dnd.c +++ b/xwayland/selection/dnd.c @@ -212,10 +212,10 @@ int xwm_handle_selection_client_message(struct wlr_xwm *xwm, bool performed = data->data32[1] & 1; xcb_atom_t action_atom = data->data32[2]; - if (xwm->drag_focus == NULL || - target_window != xwm->drag_focus->window_id) { + if (xwm->drop_focus == NULL || + target_window != xwm->drop_focus->window_id) { wlr_log(WLR_DEBUG, "ignoring XdndFinished client message because " - "it doesn't match the finished drag focus window ID"); + "it doesn't match the finished drop focus window ID"); return 1; } @@ -241,6 +241,13 @@ static void drag_focus_handle_destroy(struct wl_listener *listener, void *data) xwm_set_drag_focus(xwm, NULL); } +static void drop_focus_handle_destroy(struct wl_listener *listener, void *data) { + struct wlr_xwm *xwm = wl_container_of(listener, xwm, drop_focus_destroy); + wl_list_remove(&xwm->drop_focus_destroy.link); + wl_list_init(&xwm->drop_focus_destroy.link); + xwm->drop_focus = NULL; +} + static void xwm_set_drag_focus(struct wlr_xwm *xwm, struct wlr_xwayland_surface *focus) { if (focus == xwm->drag_focus) { return; @@ -299,6 +306,12 @@ static void seat_handle_drag_drop(struct wl_listener *listener, void *data) { } wlr_log(WLR_DEBUG, "Wayland drag dropped over an Xwayland window"); + + xwm->drop_focus = xwm->drag_focus; + xwm->drop_focus_destroy.notify = drop_focus_handle_destroy; + wl_list_remove(&xwm->drop_focus_destroy.link); + wl_signal_add(&xwm->drop_focus->events.destroy, &xwm->drop_focus_destroy); + xwm_dnd_send_drop(xwm, event->time); } @@ -329,14 +342,21 @@ static void seat_handle_drag_source_destroy(struct wl_listener *listener, wl_list_remove(&xwm->drag_focus_destroy.link); wl_list_init(&xwm->drag_focus_destroy.link); xwm->drag_focus = NULL; + + wl_list_remove(&xwm->drop_focus_destroy.link); + wl_list_init(&xwm->drop_focus_destroy.link); + xwm->drop_focus = NULL; } void xwm_seat_handle_start_drag(struct wlr_xwm *xwm, struct wlr_drag *drag) { wl_list_remove(&xwm->drag_focus_destroy.link); wl_list_init(&xwm->drag_focus_destroy.link); + wl_list_remove(&xwm->drop_focus_destroy.link); + wl_list_init(&xwm->drop_focus_destroy.link); xwm->drag = drag; xwm->drag_focus = NULL; + xwm->drop_focus = NULL; if (drag != NULL) { wl_signal_add(&drag->events.focus, &xwm->seat_drag_focus); @@ -357,6 +377,7 @@ void xwm_seat_handle_start_drag(struct wlr_xwm *xwm, struct wlr_drag *drag) { void xwm_seat_unlink_drag_handlers(struct wlr_xwm *xwm) { wl_list_remove(&xwm->seat_drag_source_destroy.link); wl_list_remove(&xwm->drag_focus_destroy.link); + wl_list_remove(&xwm->drop_focus_destroy.link); if (!xwm->drag) { return; diff --git a/xwayland/selection/outgoing.c b/xwayland/selection/outgoing.c index 4763da004..795a4768b 100644 --- a/xwayland/selection/outgoing.c +++ b/xwayland/selection/outgoing.c @@ -410,8 +410,11 @@ void xwm_handle_selection_request(struct wlr_xwm *xwm, return; } + bool dnd_allowed = selection == &xwm->dnd_selection + && (xwm->drag_focus != NULL || xwm->drop_focus != NULL); + // No xwayland surface focused, deny access to clipboard - if (xwm->focus_surface == NULL && xwm->drag_focus == NULL) { + if (xwm->focus_surface == NULL && !dnd_allowed) { if (wlr_log_get_verbosity() >= WLR_DEBUG) { char *selection_name = xwm_get_atom_name(xwm, selection->atom); wlr_log(WLR_DEBUG, "denying read access to selection %u (%s): " diff --git a/xwayland/xwm.c b/xwayland/xwm.c index f7537dacb..c651287b3 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -2266,6 +2266,7 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) { wl_list_init(&xwm->pending_startup_ids); wl_list_init(&xwm->seat_drag_source_destroy.link); wl_list_init(&xwm->drag_focus_destroy.link); + wl_list_init(&xwm->drop_focus_destroy.link); xwm->ping_timeout = 10000; From 2b4f30dc1da9f09e1e626221e430263567766fdc Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 15 Dec 2024 13:56:09 +0100 Subject: [PATCH 216/519] render/gles2: validate shaders at build time Use glslang to validate GLSL shaders at build time. This is optional: if glslang is not found, shader validation is skipped. --- render/gles2/meson.build | 2 ++ render/gles2/shaders/meson.build | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/render/gles2/meson.build b/render/gles2/meson.build index 2a6db9647..a16ec4061 100644 --- a/render/gles2/meson.build +++ b/render/gles2/meson.build @@ -4,6 +4,8 @@ if not (glesv2.found() and internal_features['egl']) subdir_done() endif +glslang = find_program('glslang', 'glslangValidator', native: true, required: false, disabler: true) + features += { 'gles2-renderer': true } wlr_deps += glesv2 diff --git a/render/gles2/shaders/meson.build b/render/gles2/shaders/meson.build index 79454d9aa..64e4e93fb 100644 --- a/render/gles2/shaders/meson.build +++ b/render/gles2/shaders/meson.build @@ -9,6 +9,14 @@ shaders = [ ] foreach name : shaders + custom_target( + 'gles2-' + name, + input: name, + output: name + '_check', + command: [glslang, '@INPUT@'], + build_by_default: true, + ) + output = name.underscorify() + '_src.h' var = name.underscorify() + '_src' wlr_files += custom_target( From b97106ddcbca4d04a708bd5c5a624745cda934e4 Mon Sep 17 00:00:00 2001 From: M Stoeckl Date: Tue, 26 Nov 2024 18:55:11 -0500 Subject: [PATCH 217/519] wlr_cursor: use default shape if requested shape missing --- types/wlr_cursor.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/types/wlr_cursor.c b/types/wlr_cursor.c index a2489fbd1..7c2f56ace 100644 --- a/types/wlr_cursor.c +++ b/types/wlr_cursor.c @@ -590,9 +590,15 @@ static void cursor_output_cursor_update(struct wlr_cursor_output_cursor *output_ wlr_xcursor_manager_load(manager, scale); struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor(manager, name, scale); if (xcursor == NULL) { - wlr_log(WLR_DEBUG, "XCursor theme is missing '%s' cursor", name); - wlr_output_cursor_set_buffer(output_cursor->output_cursor, NULL, 0, 0); - return; + /* Try the default cursor: better the wrong image than an invisible + * (and therefore practically unusable) cursor */ + wlr_log(WLR_DEBUG, "XCursor theme is missing '%s' cursor, falling back to 'default'", name); + xcursor = wlr_xcursor_manager_get_xcursor(manager, "default", scale); + if (xcursor == NULL) { + wlr_log(WLR_DEBUG, "XCursor theme is missing a 'default' cursor"); + wlr_output_cursor_set_buffer(output_cursor->output_cursor, NULL, 0, 0); + return; + } } output_cursor->xcursor = xcursor; From 776f2c4e4d8802e54a9fb7b89ea9b51b418c4e49 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 13 Dec 2024 13:08:18 +0100 Subject: [PATCH 218/519] output: adjust warning when hardware cursors are unsupported We'd print "cursor texture size exceeds hardware limitations" when some hardware doesn't support cursors at all. Change the message to better indicate the cause. --- types/output/cursor.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/types/output/cursor.c b/types/output/cursor.c index d8cdd984a..dcb605fac 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -195,6 +195,10 @@ static struct wlr_buffer *render_cursor_buffer(struct wlr_output_cursor *cursor) size_t sizes_len = 0; const struct wlr_output_cursor_size *sizes = output->impl->get_cursor_sizes(cursor->output, &sizes_len); + if (sizes_len == 0) { + wlr_log(WLR_DEBUG, "Hardware cursor not supported"); + return NULL; + } bool found = false; for (size_t i = 0; i < sizes_len; i++) { From 41e2331843686ef0b4c5b30d5e14ce59137f6c91 Mon Sep 17 00:00:00 2001 From: Consolatis <40171-Consolatis@users.noreply.gitlab.freedesktop.org> Date: Thu, 21 Nov 2024 22:49:47 +0100 Subject: [PATCH 219/519] xwm: add missing NET_WM_STATE states https://specifications.freedesktop.org/wm-spec/latest/ar01s05.html#id-1.6.8 --- include/wlr/xwayland/xwayland.h | 35 ++++++++ include/xwayland/xwm.h | 7 ++ xwayland/xwm.c | 155 +++++++++++++++++++++++++++++++- 3 files changed, 194 insertions(+), 3 deletions(-) diff --git a/include/wlr/xwayland/xwayland.h b/include/wlr/xwayland/xwayland.h index fcfa5be75..4238c1f07 100644 --- a/include/wlr/xwayland/xwayland.h +++ b/include/wlr/xwayland/xwayland.h @@ -178,6 +178,13 @@ struct wlr_xwayland_surface { bool maximized_vert, maximized_horz; bool minimized; bool withdrawn; + bool sticky; + bool shaded; + bool skip_taskbar; + bool skip_pager; + bool above; + bool below; + bool demands_attention; bool has_alpha; @@ -191,6 +198,13 @@ struct wlr_xwayland_surface { struct wl_signal request_fullscreen; struct wl_signal request_activate; struct wl_signal request_close; + struct wl_signal request_sticky; + struct wl_signal request_shaded; + struct wl_signal request_skip_taskbar; + struct wl_signal request_skip_pager; + struct wl_signal request_above; + struct wl_signal request_below; + struct wl_signal request_demands_attention; struct wl_signal associate; struct wl_signal dissociate; @@ -292,6 +306,27 @@ void wlr_xwayland_surface_set_maximized(struct wlr_xwayland_surface *surface, void wlr_xwayland_surface_set_fullscreen(struct wlr_xwayland_surface *surface, bool fullscreen); +void wlr_xwayland_surface_set_sticky( + struct wlr_xwayland_surface *surface, bool sticky); + +void wlr_xwayland_surface_set_shaded( + struct wlr_xwayland_surface *surface, bool shaded); + +void wlr_xwayland_surface_set_skip_taskbar( + struct wlr_xwayland_surface *surface, bool skip_taskbar); + +void wlr_xwayland_surface_set_skip_pager( + struct wlr_xwayland_surface *surface, bool skip_pager); + +void wlr_xwayland_surface_set_above( + struct wlr_xwayland_surface *surface, bool above); + +void wlr_xwayland_surface_set_below( + struct wlr_xwayland_surface *surface, bool below); + +void wlr_xwayland_surface_set_demands_attention( + struct wlr_xwayland_surface *surface, bool demands_attention); + void wlr_xwayland_set_seat(struct wlr_xwayland *xwayland, struct wlr_seat *seat); diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h index dc304b567..2a31902ca 100644 --- a/include/xwayland/xwm.h +++ b/include/xwayland/xwm.h @@ -48,6 +48,13 @@ enum atom_name { NET_WM_STATE_MAXIMIZED_VERT, NET_WM_STATE_MAXIMIZED_HORZ, NET_WM_STATE_HIDDEN, + NET_WM_STATE_STICKY, + NET_WM_STATE_SHADED, + NET_WM_STATE_SKIP_TASKBAR, + NET_WM_STATE_SKIP_PAGER, + NET_WM_STATE_ABOVE, + NET_WM_STATE_BELOW, + NET_WM_STATE_DEMANDS_ATTENTION, NET_WM_PING, WM_CHANGE_STATE, WM_STATE, diff --git a/xwayland/xwm.c b/xwayland/xwm.c index c651287b3..e77ad1a11 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -47,6 +47,13 @@ static const char *const atom_map[ATOM_LAST] = { [NET_WM_STATE_MAXIMIZED_VERT] = "_NET_WM_STATE_MAXIMIZED_VERT", [NET_WM_STATE_MAXIMIZED_HORZ] = "_NET_WM_STATE_MAXIMIZED_HORZ", [NET_WM_STATE_HIDDEN] = "_NET_WM_STATE_HIDDEN", + [NET_WM_STATE_STICKY] = "_NET_WM_STATE_STICKY", + [NET_WM_STATE_SHADED] = "_NET_WM_STATE_SHADED", + [NET_WM_STATE_SKIP_TASKBAR] = "_NET_WM_STATE_SKIP_TASKBAR", + [NET_WM_STATE_SKIP_PAGER] = "_NET_WM_STATE_SKIP_PAGER", + [NET_WM_STATE_ABOVE] = "_NET_WM_STATE_ABOVE", + [NET_WM_STATE_BELOW] = "_NET_WM_STATE_BELOW", + [NET_WM_STATE_DEMANDS_ATTENTION] = "_NET_WM_STATE_DEMANDS_ATTENTION", [NET_WM_PING] = "_NET_WM_PING", [WM_CHANGE_STATE] = "WM_CHANGE_STATE", [WM_STATE] = "WM_STATE", @@ -211,6 +218,13 @@ static struct wlr_xwayland_surface *xwayland_surface_create( wl_signal_init(&surface->events.request_fullscreen); wl_signal_init(&surface->events.request_activate); wl_signal_init(&surface->events.request_close); + wl_signal_init(&surface->events.request_sticky); + wl_signal_init(&surface->events.request_shaded); + wl_signal_init(&surface->events.request_skip_taskbar); + wl_signal_init(&surface->events.request_skip_pager); + wl_signal_init(&surface->events.request_above); + wl_signal_init(&surface->events.request_below); + wl_signal_init(&surface->events.request_demands_attention); wl_signal_init(&surface->events.associate); wl_signal_init(&surface->events.dissociate); wl_signal_init(&surface->events.set_class); @@ -269,8 +283,7 @@ static void xwm_set_net_active_window(struct wlr_xwm *xwm, */ xcb_void_cookie_t xwm_send_event_with_size(xcb_connection_t *c, uint8_t propagate, xcb_window_t destination, - uint32_t event_mask, const void *event, uint32_t length) -{ + uint32_t event_mask, const void *event, uint32_t length) { if (length == 32) { return xcb_send_event(c, propagate, destination, event_mask, event); } else if (length < 32) { @@ -474,7 +487,7 @@ static void xsurface_set_net_wm_state(struct wlr_xwayland_surface *xsurface) { return; } - uint32_t property[6]; + uint32_t property[13]; size_t i = 0; if (xsurface->modal) { property[i++] = xwm->atoms[NET_WM_STATE_MODAL]; @@ -491,6 +504,27 @@ static void xsurface_set_net_wm_state(struct wlr_xwayland_surface *xsurface) { if (xsurface->minimized) { property[i++] = xwm->atoms[NET_WM_STATE_HIDDEN]; } + if (xsurface->sticky) { + property[i++] = xwm->atoms[NET_WM_STATE_STICKY]; + } + if (xsurface->shaded) { + property[i++] = xwm->atoms[NET_WM_STATE_SHADED]; + } + if (xsurface->skip_taskbar) { + property[i++] = xwm->atoms[NET_WM_STATE_SKIP_TASKBAR]; + } + if (xsurface->skip_pager) { + property[i++] = xwm->atoms[NET_WM_STATE_SKIP_PAGER]; + } + if (xsurface->above) { + property[i++] = xwm->atoms[NET_WM_STATE_ABOVE]; + } + if (xsurface->below) { + property[i++] = xwm->atoms[NET_WM_STATE_BELOW]; + } + if (xsurface->demands_attention) { + property[i++] = xwm->atoms[NET_WM_STATE_DEMANDS_ATTENTION]; + } if (xsurface == xwm->focus_surface) { property[i++] = xwm->atoms[NET_WM_STATE_FOCUSED]; } @@ -886,6 +920,20 @@ static void read_surface_net_wm_state(struct wlr_xwm *xwm, xsurface->maximized_horz = true; } else if (atom[i] == xwm->atoms[NET_WM_STATE_HIDDEN]) { xsurface->minimized = true; + } else if (atom[i] == xwm->atoms[NET_WM_STATE_STICKY]) { + xsurface->sticky = true; + } else if (atom[i] == xwm->atoms[NET_WM_STATE_SHADED]) { + xsurface->shaded = true; + } else if (atom[i] == xwm->atoms[NET_WM_STATE_SKIP_TASKBAR]) { + xsurface->skip_taskbar = true; + } else if (atom[i] == xwm->atoms[NET_WM_STATE_SKIP_PAGER]) { + xsurface->skip_pager = true; + } else if (atom[i] == xwm->atoms[NET_WM_STATE_ABOVE]) { + xsurface->above = true; + } else if (atom[i] == xwm->atoms[NET_WM_STATE_BELOW]) { + xsurface->below = true; + } else if (atom[i] == xwm->atoms[NET_WM_STATE_DEMANDS_ATTENTION]) { + xsurface->demands_attention = true; } } } @@ -1412,6 +1460,13 @@ static void xwm_handle_net_wm_state_message(struct wlr_xwm *xwm, bool maximized_vert = xsurface->maximized_vert; bool maximized_horz = xsurface->maximized_horz; bool minimized = xsurface->minimized; + bool sticky = xsurface->sticky; + bool shaded = xsurface->shaded; + bool skip_taskbar = xsurface->skip_taskbar; + bool skip_pager = xsurface->skip_pager; + bool above = xsurface->above; + bool below = xsurface->below; + bool demands_attention = xsurface->demands_attention; uint32_t action = client_message->data.data32[0]; for (size_t i = 0; i < 2; ++i) { @@ -1428,6 +1483,20 @@ static void xwm_handle_net_wm_state_message(struct wlr_xwm *xwm, changed = update_state(action, &xsurface->maximized_horz); } else if (property == xwm->atoms[NET_WM_STATE_HIDDEN]) { changed = update_state(action, &xsurface->minimized); + } else if (property == xwm->atoms[NET_WM_STATE_STICKY]) { + changed = update_state(action, &xsurface->sticky); + } else if (property == xwm->atoms[NET_WM_STATE_SHADED]) { + changed = update_state(action, &xsurface->shaded); + } else if (property == xwm->atoms[NET_WM_STATE_SKIP_TASKBAR]) { + changed = update_state(action, &xsurface->skip_taskbar); + } else if (property == xwm->atoms[NET_WM_STATE_SKIP_PAGER]) { + changed = update_state(action, &xsurface->skip_pager); + } else if (property == xwm->atoms[NET_WM_STATE_ABOVE]) { + changed = update_state(action, &xsurface->above); + } else if (property == xwm->atoms[NET_WM_STATE_BELOW]) { + changed = update_state(action, &xsurface->below); + } else if (property == xwm->atoms[NET_WM_STATE_DEMANDS_ATTENTION]) { + changed = update_state(action, &xsurface->demands_attention); } else if (property != XCB_ATOM_NONE && wlr_log_get_verbosity() >= WLR_DEBUG) { char *prop_name = xwm_get_atom_name(xwm, property); wlr_log(WLR_DEBUG, "Unhandled NET_WM_STATE property change " @@ -1458,6 +1527,34 @@ static void xwm_handle_net_wm_state_message(struct wlr_xwm *xwm, }; wl_signal_emit_mutable(&xsurface->events.request_minimize, &minimize_event); } + + if (sticky != xsurface->sticky) { + wl_signal_emit_mutable(&xsurface->events.request_sticky, NULL); + } + + if (shaded != xsurface->shaded) { + wl_signal_emit_mutable(&xsurface->events.request_shaded, NULL); + } + + if (skip_taskbar != xsurface->skip_taskbar) { + wl_signal_emit_mutable(&xsurface->events.request_skip_taskbar, NULL); + } + + if (skip_pager != xsurface->skip_pager) { + wl_signal_emit_mutable(&xsurface->events.request_skip_pager, NULL); + } + + if (above != xsurface->above) { + wl_signal_emit_mutable(&xsurface->events.request_above, NULL); + } + + if (below != xsurface->below) { + wl_signal_emit_mutable(&xsurface->events.request_below, NULL); + } + + if (demands_attention != xsurface->demands_attention) { + wl_signal_emit_mutable(&xsurface->events.request_demands_attention, NULL); + } } static void xwm_handle_wm_protocols_message(struct wlr_xwm *xwm, @@ -2326,6 +2423,13 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) { xwm->atoms[NET_WM_STATE_MAXIMIZED_VERT], xwm->atoms[NET_WM_STATE_MAXIMIZED_HORZ], xwm->atoms[NET_WM_STATE_HIDDEN], + xwm->atoms[NET_WM_STATE_STICKY], + xwm->atoms[NET_WM_STATE_SHADED], + xwm->atoms[NET_WM_STATE_SKIP_TASKBAR], + xwm->atoms[NET_WM_STATE_SKIP_PAGER], + xwm->atoms[NET_WM_STATE_ABOVE], + xwm->atoms[NET_WM_STATE_BELOW], + xwm->atoms[NET_WM_STATE_DEMANDS_ATTENTION], xwm->atoms[NET_CLIENT_LIST], xwm->atoms[NET_CLIENT_LIST_STACKING], }; @@ -2406,6 +2510,51 @@ void wlr_xwayland_surface_set_fullscreen(struct wlr_xwayland_surface *surface, xwm_schedule_flush(surface->xwm); } +void wlr_xwayland_surface_set_sticky(struct wlr_xwayland_surface *surface, bool sticky) { + surface->sticky = sticky; + xsurface_set_net_wm_state(surface); + xwm_schedule_flush(surface->xwm); +} + +void wlr_xwayland_surface_set_shaded(struct wlr_xwayland_surface *surface, bool shaded) { + surface->shaded = shaded; + xsurface_set_net_wm_state(surface); + xwm_schedule_flush(surface->xwm); +} + +void wlr_xwayland_surface_set_skip_taskbar(struct wlr_xwayland_surface *surface, + bool skip_taskbar) { + surface->skip_taskbar = skip_taskbar; + xsurface_set_net_wm_state(surface); + xwm_schedule_flush(surface->xwm); +} + +void wlr_xwayland_surface_set_skip_pager(struct wlr_xwayland_surface *surface, + bool skip_pager) { + surface->skip_pager = skip_pager; + xsurface_set_net_wm_state(surface); + xwm_schedule_flush(surface->xwm); +} + +void wlr_xwayland_surface_set_above(struct wlr_xwayland_surface *surface, bool above) { + surface->above = above; + xsurface_set_net_wm_state(surface); + xwm_schedule_flush(surface->xwm); +} + +void wlr_xwayland_surface_set_below(struct wlr_xwayland_surface *surface, bool below) { + surface->below = below; + xsurface_set_net_wm_state(surface); + xwm_schedule_flush(surface->xwm); +} + +void wlr_xwayland_surface_set_demands_attention(struct wlr_xwayland_surface *surface, + bool demands_attention) { + surface->demands_attention = demands_attention; + xsurface_set_net_wm_state(surface); + xwm_schedule_flush(surface->xwm); +} + bool xwm_atoms_contains(struct wlr_xwm *xwm, xcb_atom_t *atoms, size_t num_atoms, enum atom_name needle) { xcb_atom_t atom = xwm->atoms[needle]; From 648c64b7e46340f0e62002893d4d4c8a207a7fe7 Mon Sep 17 00:00:00 2001 From: Consolatis <40171-Consolatis@users.noreply.gitlab.freedesktop.org> Date: Sat, 14 Dec 2024 04:17:40 +0100 Subject: [PATCH 220/519] wlr-foreign-toplevel: slight refactor --- types/wlr_foreign_toplevel_management_v1.c | 51 ++++++---------------- 1 file changed, 14 insertions(+), 37 deletions(-) diff --git a/types/wlr_foreign_toplevel_management_v1.c b/types/wlr_foreign_toplevel_management_v1.c index 23935d489..7a4594a21 100644 --- a/types/wlr_foreign_toplevel_management_v1.c +++ b/types/wlr_foreign_toplevel_management_v1.c @@ -395,60 +395,37 @@ static void toplevel_send_state(struct wlr_foreign_toplevel_handle_v1 *toplevel) toplevel_update_idle_source(toplevel); } -void wlr_foreign_toplevel_handle_v1_set_maximized( - struct wlr_foreign_toplevel_handle_v1 *toplevel, bool maximized) { - if (maximized == !!(toplevel->state & - WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED)) { +static void set_state(struct wlr_foreign_toplevel_handle_v1 *toplevel, + bool new_state_val, enum wlr_foreign_toplevel_handle_v1_state state) { + if (new_state_val == !!(toplevel->state & state)) { return; } - if (maximized) { - toplevel->state |= WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED; + if (new_state_val) { + toplevel->state |= state; } else { - toplevel->state &= ~WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED; + toplevel->state &= ~state; } toplevel_send_state(toplevel); } +void wlr_foreign_toplevel_handle_v1_set_maximized( + struct wlr_foreign_toplevel_handle_v1 *toplevel, bool maximized) { + set_state(toplevel, maximized, WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED); +} + void wlr_foreign_toplevel_handle_v1_set_minimized( struct wlr_foreign_toplevel_handle_v1 *toplevel, bool minimized) { - if (minimized == !!(toplevel->state & - WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED)) { - return; - } - if (minimized) { - toplevel->state |= WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED; - } else { - toplevel->state &= ~WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED; - } - toplevel_send_state(toplevel); + set_state(toplevel, minimized, WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED); } void wlr_foreign_toplevel_handle_v1_set_activated( struct wlr_foreign_toplevel_handle_v1 *toplevel, bool activated) { - if (activated == !!(toplevel->state & - WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED)) { - return; - } - if (activated) { - toplevel->state |= WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED; - } else { - toplevel->state &= ~WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED; - } - toplevel_send_state(toplevel); + set_state(toplevel, activated, WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED); } void wlr_foreign_toplevel_handle_v1_set_fullscreen( struct wlr_foreign_toplevel_handle_v1 * toplevel, bool fullscreen) { - if (fullscreen == !!(toplevel->state & - WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN)) { - return; - } - if (fullscreen) { - toplevel->state |= WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN; - } else { - toplevel->state &= ~WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN; - } - toplevel_send_state(toplevel); + set_state(toplevel, fullscreen, WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN); } static void toplevel_resource_send_parent( From bd3724aa26d8246c44af7aef6900e70808f1a550 Mon Sep 17 00:00:00 2001 From: Consolatis <40171-Consolatis@users.noreply.gitlab.freedesktop.org> Date: Fri, 20 Dec 2024 21:36:25 +0100 Subject: [PATCH 221/519] wlr-foreign-toplevel: convert to stack backed states array --- types/wlr_foreign_toplevel_management_v1.c | 72 +++++++--------------- 1 file changed, 22 insertions(+), 50 deletions(-) diff --git a/types/wlr_foreign_toplevel_management_v1.c b/types/wlr_foreign_toplevel_management_v1.c index 7a4594a21..f5812ecd0 100644 --- a/types/wlr_foreign_toplevel_management_v1.c +++ b/types/wlr_foreign_toplevel_management_v1.c @@ -1,4 +1,3 @@ - #include #include #include @@ -10,6 +9,8 @@ #define FOREIGN_TOPLEVEL_MANAGEMENT_V1_VERSION 3 +#define FOREIGN_TOPLEVEL_HANDLE_V1_STATE_COUNT 32 + static const struct zwlr_foreign_toplevel_handle_v1_interface toplevel_handle_impl; static struct wlr_foreign_toplevel_handle_v1 *toplevel_handle_from_resource( @@ -338,60 +339,38 @@ void wlr_foreign_toplevel_handle_v1_output_leave( } } -static bool fill_array_from_toplevel_state(struct wl_array *array, - uint32_t state) { +static void fill_array_from_toplevel_state(struct wl_array *states, + uint32_t data[], uint32_t state) { + size_t nstates = 0; if (state & WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED) { - uint32_t *index = wl_array_add(array, sizeof(uint32_t)); - if (index == NULL) { - return false; - } - *index = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED; + data[nstates++] = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED; } if (state & WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED) { - uint32_t *index = wl_array_add(array, sizeof(uint32_t)); - if (index == NULL) { - return false; - } - *index = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED; + data[nstates++] = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED; } if (state & WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED) { - uint32_t *index = wl_array_add(array, sizeof(uint32_t)); - if (index == NULL) { - return false; - } - *index = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED; + data[nstates++] = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED; } if (state & WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN) { - uint32_t *index = wl_array_add(array, sizeof(uint32_t)); - if (index == NULL) { - return false; - } - *index = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN; + data[nstates++] = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN; } + assert(nstates <= FOREIGN_TOPLEVEL_HANDLE_V1_STATE_COUNT); - return true; + *states = (struct wl_array){ + .data = data, + .size = nstates * sizeof(data[0]) + }; } static void toplevel_send_state(struct wlr_foreign_toplevel_handle_v1 *toplevel) { - struct wl_array states; - wl_array_init(&states); - bool r = fill_array_from_toplevel_state(&states, toplevel->state); - if (!r) { - struct wl_resource *resource; - wl_resource_for_each(resource, &toplevel->resources) { - wl_resource_post_no_memory(resource); - } - - wl_array_release(&states); - return; - } + struct wl_array state_array; + uint32_t states[FOREIGN_TOPLEVEL_HANDLE_V1_STATE_COUNT]; struct wl_resource *resource; wl_resource_for_each(resource, &toplevel->resources) { - zwlr_foreign_toplevel_handle_v1_send_state(resource, &states); + fill_array_from_toplevel_state(&state_array, states, toplevel->state); + zwlr_foreign_toplevel_handle_v1_send_state(resource, &state_array); } - - wl_array_release(&states); toplevel_update_idle_source(toplevel); } @@ -600,17 +579,10 @@ static void toplevel_send_details_to_toplevel_resource( send_output_to_resource(resource, output->output, true); } - struct wl_array states; - wl_array_init(&states); - bool r = fill_array_from_toplevel_state(&states, toplevel->state); - if (!r) { - wl_resource_post_no_memory(resource); - wl_array_release(&states); - return; - } - - zwlr_foreign_toplevel_handle_v1_send_state(resource, &states); - wl_array_release(&states); + struct wl_array state_array; + uint32_t states[FOREIGN_TOPLEVEL_HANDLE_V1_STATE_COUNT]; + fill_array_from_toplevel_state(&state_array, states, toplevel->state); + zwlr_foreign_toplevel_handle_v1_send_state(resource, &state_array); toplevel_resource_send_parent(resource, toplevel->parent); From c6de47d4158b95a69f678b4131d6a0f8fd9032f0 Mon Sep 17 00:00:00 2001 From: Consolatis <40171-Consolatis@users.noreply.gitlab.freedesktop.org> Date: Sat, 14 Dec 2024 07:37:33 +0100 Subject: [PATCH 222/519] wlr-foreign-toplevel: version guard fullscreen state --- types/wlr_foreign_toplevel_management_v1.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/types/wlr_foreign_toplevel_management_v1.c b/types/wlr_foreign_toplevel_management_v1.c index f5812ecd0..db7e526fd 100644 --- a/types/wlr_foreign_toplevel_management_v1.c +++ b/types/wlr_foreign_toplevel_management_v1.c @@ -340,7 +340,7 @@ void wlr_foreign_toplevel_handle_v1_output_leave( } static void fill_array_from_toplevel_state(struct wl_array *states, - uint32_t data[], uint32_t state) { + uint32_t data[], uint32_t state, uint32_t version) { size_t nstates = 0; if (state & WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED) { data[nstates++] = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED; @@ -351,7 +351,8 @@ static void fill_array_from_toplevel_state(struct wl_array *states, if (state & WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED) { data[nstates++] = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED; } - if (state & WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN) { + if (version >= ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN_SINCE_VERSION + && (state & WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN)) { data[nstates++] = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN; } assert(nstates <= FOREIGN_TOPLEVEL_HANDLE_V1_STATE_COUNT); @@ -368,7 +369,8 @@ static void toplevel_send_state(struct wlr_foreign_toplevel_handle_v1 *toplevel) struct wl_resource *resource; wl_resource_for_each(resource, &toplevel->resources) { - fill_array_from_toplevel_state(&state_array, states, toplevel->state); + fill_array_from_toplevel_state(&state_array, states, + toplevel->state, wl_resource_get_version(resource)); zwlr_foreign_toplevel_handle_v1_send_state(resource, &state_array); } toplevel_update_idle_source(toplevel); @@ -581,7 +583,8 @@ static void toplevel_send_details_to_toplevel_resource( struct wl_array state_array; uint32_t states[FOREIGN_TOPLEVEL_HANDLE_V1_STATE_COUNT]; - fill_array_from_toplevel_state(&state_array, states, toplevel->state); + fill_array_from_toplevel_state(&state_array, states, + toplevel->state, wl_resource_get_version(resource)); zwlr_foreign_toplevel_handle_v1_send_state(resource, &state_array); toplevel_resource_send_parent(resource, toplevel->parent); From be555a9a99a4b4deb9ad72b4ed25f39ba5352d0d Mon Sep 17 00:00:00 2001 From: Consolatis <40171-Consolatis@users.noreply.gitlab.freedesktop.org> Date: Wed, 7 Aug 2024 19:37:23 +0200 Subject: [PATCH 223/519] backend/drm: make drm_lease implementation follow docs This ensures compositors get a output_destroy signal when the lease is granted and a new_output signal when the lease is revoked. --- backend/drm/drm.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 52bcae06b..bed6ba497 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -1762,6 +1762,10 @@ void scan_drm_connectors(struct wlr_drm_backend *drm, } } + if (wlr_conn && wlr_conn->lease) { + continue; + } + // If the hotplug event contains a connector ID, ignore any other // connector. if (event != NULL && event->connector_id != 0 && @@ -2150,6 +2154,7 @@ struct wlr_drm_lease *wlr_drm_create_lease(struct wlr_output **outputs, get_drm_connector_from_output(outputs[i]); conn->lease = lease; conn->crtc->lease = lease; + disconnect_drm_connector(conn); } return lease; @@ -2186,4 +2191,5 @@ void drm_lease_destroy(struct wlr_drm_lease *lease) { } free(lease); + scan_drm_connectors(drm, NULL); } From 90530d43fe43984062741179c36af7294ccb202c Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Mon, 19 Aug 2024 14:19:38 +0300 Subject: [PATCH 224/519] xdg-dialog-v1: add implementation --- include/wlr/types/wlr_xdg_dialog_v1.h | 57 ++++++++ protocol/meson.build | 1 + types/meson.build | 1 + types/wlr_xdg_dialog_v1.c | 192 ++++++++++++++++++++++++++ 4 files changed, 251 insertions(+) create mode 100644 include/wlr/types/wlr_xdg_dialog_v1.h create mode 100644 types/wlr_xdg_dialog_v1.c diff --git a/include/wlr/types/wlr_xdg_dialog_v1.h b/include/wlr/types/wlr_xdg_dialog_v1.h new file mode 100644 index 000000000..061c3252b --- /dev/null +++ b/include/wlr/types/wlr_xdg_dialog_v1.h @@ -0,0 +1,57 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_TYPES_WLR_XDG_DIALOG_V1_H +#define WLR_TYPES_WLR_XDG_DIALOG_V1_H + +#include +#include + +struct wlr_xdg_dialog_v1 { + struct wl_resource *resource; + struct wlr_xdg_toplevel *xdg_toplevel; + + bool modal; + + struct { + struct wl_signal destroy; + // Corresponds to both xdg_dialog_v1.set_modal and xdg_dialog_v1.unset_modal + struct wl_signal set_modal; + } events; + + struct { + struct wlr_addon surface_addon; + + struct wl_listener xdg_toplevel_destroy; + } WLR_PRIVATE; +}; + +struct wlr_xdg_wm_dialog_v1 { + struct wl_global *global; + + struct { + struct wl_signal new_dialog; // struct wlr_xdg_dialog_v1 + } events; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; +}; + +struct wlr_xdg_wm_dialog_v1 *wlr_xdg_wm_dialog_v1_create(struct wl_display *display, + uint32_t version); + +/** + * Get a struct wlr_xdg_dialog_v1 from a struct wlr_xdg_toplevel. + * + * Returns NULL if there's no xdg_dialog_v1 associated with the xdg toplevel. + */ +struct wlr_xdg_dialog_v1 *wlr_xdg_dialog_v1_try_from_wlr_xdg_toplevel( + struct wlr_xdg_toplevel *xdg_toplevel); + +#endif diff --git a/protocol/meson.build b/protocol/meson.build index 2889fb033..2b0e49977 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -35,6 +35,7 @@ protocols = { 'security-context-v1': wl_protocol_dir / 'staging/security-context/security-context-v1.xml', 'single-pixel-buffer-v1': wl_protocol_dir / 'staging/single-pixel-buffer/single-pixel-buffer-v1.xml', 'xdg-activation-v1': wl_protocol_dir / 'staging/xdg-activation/xdg-activation-v1.xml', + 'xdg-dialog-v1': wl_protocol_dir / 'staging/xdg-dialog/xdg-dialog-v1.xml', 'xdg-system-bell-v1': wl_protocol_dir / 'staging/xdg-system-bell/xdg-system-bell-v1.xml', 'xwayland-shell-v1': wl_protocol_dir / 'staging/xwayland-shell/xwayland-shell-v1.xml', 'tearing-control-v1': wl_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml', diff --git a/types/meson.build b/types/meson.build index 8753b8a0c..237d41f53 100644 --- a/types/meson.build +++ b/types/meson.build @@ -91,6 +91,7 @@ wlr_files += files( 'wlr_xcursor_manager.c', 'wlr_xdg_activation_v1.c', 'wlr_xdg_decoration_v1.c', + 'wlr_xdg_dialog_v1.c', 'wlr_xdg_foreign_v1.c', 'wlr_xdg_foreign_v2.c', 'wlr_xdg_foreign_registry.c', diff --git a/types/wlr_xdg_dialog_v1.c b/types/wlr_xdg_dialog_v1.c new file mode 100644 index 000000000..f9e73c7e4 --- /dev/null +++ b/types/wlr_xdg_dialog_v1.c @@ -0,0 +1,192 @@ +#include +#include + +#include +#include + +#include "xdg-dialog-v1-protocol.h" + +// NOTE: xdg_dialog_v1 becomes inert when the corresponding xdg_toplevel is destroyed + +#define XDG_WM_DIALOG_V1_VERSION 1 + +static const struct xdg_dialog_v1_interface dialog_impl; + +static struct wlr_xdg_dialog_v1 *dialog_from_resource(struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &xdg_dialog_v1_interface, &dialog_impl)); + return wl_resource_get_user_data(resource); +} + +static const struct xdg_wm_dialog_v1_interface wm_impl; + +static struct wlr_xdg_wm_dialog_v1 *wm_from_resource(struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &xdg_wm_dialog_v1_interface, &wm_impl)); + return wl_resource_get_user_data(resource); +} + +static void resource_destroy(struct wl_client *client, struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static void surface_addon_destroy(struct wlr_addon *addon) { + // As wlr_xdg_toplevel is always destroyed before the surface, this should + // never be reached. + abort(); +} + +static const struct wlr_addon_interface surface_addon_impl = { + .name = "xdg_wm_dialog_v1", + .destroy = surface_addon_destroy, +}; + +static void set_modal(struct wlr_xdg_dialog_v1 *dialog, bool modal) { + if (dialog->modal == modal) { + return; + } + dialog->modal = modal; + wl_signal_emit_mutable(&dialog->events.set_modal, NULL); +} + +static void dialog_handle_set_modal(struct wl_client *client, struct wl_resource *resource) { + struct wlr_xdg_dialog_v1 *dialog = dialog_from_resource(resource); + if (dialog == NULL) { + return; + } + set_modal(dialog, true); +} + +static void dialog_handle_unset_modal(struct wl_client *client, struct wl_resource *resource) { + struct wlr_xdg_dialog_v1 *dialog = dialog_from_resource(resource); + if (dialog == NULL) { + return; + } + set_modal(dialog, false); +}; + +static const struct xdg_dialog_v1_interface dialog_impl = { + .destroy = resource_destroy, + .set_modal = dialog_handle_set_modal, + .unset_modal = dialog_handle_unset_modal, +}; + +static void dialog_destroy(struct wlr_xdg_dialog_v1 *dialog) { + if (dialog == NULL) { + return; + } + + wl_signal_emit_mutable(&dialog->events.destroy, NULL); + + wlr_addon_finish(&dialog->surface_addon); + wl_list_remove(&dialog->xdg_toplevel_destroy.link); + + wl_resource_set_user_data(dialog->resource, NULL); + free(dialog); +} + +static void handle_xdg_toplevel_destroy(struct wl_listener *listener, void *data) { + struct wlr_xdg_dialog_v1 *dialog = wl_container_of(listener, dialog, xdg_toplevel_destroy); + dialog_destroy(dialog); +} + +static void handle_resource_destroy(struct wl_resource *resource) { + struct wlr_xdg_dialog_v1 *dialog = dialog_from_resource(resource); + dialog_destroy(dialog); +} + +static void wm_get_xdg_dialog(struct wl_client *client, struct wl_resource *wm_resource, + uint32_t id, struct wl_resource *toplevel_resource) { + struct wlr_xdg_wm_dialog_v1 *wm = wm_from_resource(wm_resource); + struct wlr_xdg_toplevel *xdg_toplevel = wlr_xdg_toplevel_from_resource(toplevel_resource); + + struct wlr_addon_set *addon_set = &xdg_toplevel->base->surface->addons; + + if (wlr_addon_find(addon_set, NULL, &surface_addon_impl) != NULL) { + wl_resource_post_error(wm_resource, XDG_WM_DIALOG_V1_ERROR_ALREADY_USED, + "the xdg_toplevel object has already been used to create a xdg_dialog_v1"); + return; + } + + struct wlr_xdg_dialog_v1 *dialog = calloc(1, sizeof(*dialog)); + if (dialog == NULL) { + wl_resource_post_no_memory(wm_resource); + return; + } + + dialog->resource = wl_resource_create(client, &xdg_dialog_v1_interface, + wl_resource_get_version(wm_resource), id); + if (dialog->resource == NULL) { + free(dialog); + wl_resource_post_no_memory(wm_resource); + return; + } + wl_resource_set_implementation(dialog->resource, &dialog_impl, + dialog, handle_resource_destroy); + + dialog->xdg_toplevel = xdg_toplevel; + wlr_addon_init(&dialog->surface_addon, addon_set, NULL, &surface_addon_impl); + + dialog->xdg_toplevel_destroy.notify = handle_xdg_toplevel_destroy; + wl_signal_add(&xdg_toplevel->events.destroy, &dialog->xdg_toplevel_destroy); + + wl_signal_init(&dialog->events.destroy); + wl_signal_init(&dialog->events.set_modal); + + wl_signal_emit_mutable(&wm->events.new_dialog, dialog); +} + +static const struct xdg_wm_dialog_v1_interface wm_impl = { + .destroy = resource_destroy, + .get_xdg_dialog = wm_get_xdg_dialog, +}; + +static void wm_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { + struct wlr_xdg_wm_dialog_v1 *wm = data; + struct wl_resource *resource = + wl_resource_create(client, &xdg_wm_dialog_v1_interface, version, id); + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &wm_impl, wm, NULL); +} + +static void xdg_wm_dialog_handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_xdg_wm_dialog_v1 *wm = wl_container_of(listener, wm, display_destroy); + wl_list_remove(&wm->display_destroy.link); + wl_global_destroy(wm->global); + free(wm); +} + +struct wlr_xdg_dialog_v1 *wlr_xdg_dialog_v1_try_from_wlr_xdg_toplevel( + struct wlr_xdg_toplevel *xdg_toplevel) { + struct wlr_addon *addon = + wlr_addon_find(&xdg_toplevel->base->surface->addons, NULL, &surface_addon_impl); + if (addon == NULL) { + return NULL; + } + struct wlr_xdg_dialog_v1 *dialog = wl_container_of(addon, dialog, surface_addon); + return dialog; +} + +struct wlr_xdg_wm_dialog_v1 *wlr_xdg_wm_dialog_v1_create(struct wl_display *display, + uint32_t version) { + assert(version <= XDG_WM_DIALOG_V1_VERSION); + + struct wlr_xdg_wm_dialog_v1 *wm = calloc(1, sizeof(*wm)); + if (wm == NULL) { + return NULL; + } + + wm->global = wl_global_create(display, &xdg_wm_dialog_v1_interface, version, wm, wm_bind); + if (wm->global == NULL) { + free(wm); + return NULL; + } + + wm->display_destroy.notify = xdg_wm_dialog_handle_display_destroy; + wl_display_add_destroy_listener(display, &wm->display_destroy); + + wl_signal_init(&wm->events.new_dialog); + + return wm; +} From 428279a319289839079d459ab220bb9fa502aa0f Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 26 Nov 2024 19:08:16 +0100 Subject: [PATCH 225/519] build: add Meson wrap manifests for our dependencies Add Meson wrap manifests for all of our dependencies which can fallback to a subproject. This makes it easier to build wlroots on platforms where system packages are outdated. Users can now opt-in via `meson configure --wrap-mode=default` and Meson will download and build any missing dependency. Don't download by default because this can be quite surprising and undesirable for some users (e.g. if they are just missing some -dev package). --- .gitignore | 1 + meson.build | 1 + subprojects/libdisplay-info.wrap | 3 +++ subprojects/libdrm.wrap | 3 +++ subprojects/libliftoff.wrap | 3 +++ subprojects/libxkbcommon.wrap | 3 +++ subprojects/pixman.wrap | 3 +++ subprojects/seatd.wrap | 3 +++ subprojects/wayland-protocols.wrap | 3 +++ subprojects/wayland.wrap | 3 +++ 10 files changed, 26 insertions(+) create mode 100644 subprojects/libdisplay-info.wrap create mode 100644 subprojects/libdrm.wrap create mode 100644 subprojects/libliftoff.wrap create mode 100644 subprojects/libxkbcommon.wrap create mode 100644 subprojects/pixman.wrap create mode 100644 subprojects/seatd.wrap create mode 100644 subprojects/wayland-protocols.wrap create mode 100644 subprojects/wayland.wrap diff --git a/.gitignore b/.gitignore index d73983bfb..623384e78 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /subprojects/ +!/subprojects/*.wrap diff --git a/meson.build b/meson.build index 690b77b35..606a8d808 100644 --- a/meson.build +++ b/meson.build @@ -8,6 +8,7 @@ project( 'c_std=' + (meson.version().version_compare('>=1.4.0') ? 'c23,c11' : 'c11'), 'warning_level=2', 'werror=true', + 'wrap_mode=nodownload', ], ) diff --git a/subprojects/libdisplay-info.wrap b/subprojects/libdisplay-info.wrap new file mode 100644 index 000000000..f92d3041f --- /dev/null +++ b/subprojects/libdisplay-info.wrap @@ -0,0 +1,3 @@ +[wrap-git] +url = https://gitlab.freedesktop.org/emersion/libdisplay-info.git +revision = HEAD diff --git a/subprojects/libdrm.wrap b/subprojects/libdrm.wrap new file mode 100644 index 000000000..2b5ca29c8 --- /dev/null +++ b/subprojects/libdrm.wrap @@ -0,0 +1,3 @@ +[wrap-git] +url = https://gitlab.freedesktop.org/mesa/drm.git +revision = HEAD diff --git a/subprojects/libliftoff.wrap b/subprojects/libliftoff.wrap new file mode 100644 index 000000000..57e6bb231 --- /dev/null +++ b/subprojects/libliftoff.wrap @@ -0,0 +1,3 @@ +[wrap-git] +url = https://gitlab.freedesktop.org/emersion/libliftoff.git +revision = HEAD diff --git a/subprojects/libxkbcommon.wrap b/subprojects/libxkbcommon.wrap new file mode 100644 index 000000000..102b84496 --- /dev/null +++ b/subprojects/libxkbcommon.wrap @@ -0,0 +1,3 @@ +[wrap-git] +url = https://github.com/xkbcommon/libxkbcommon.git +revision = HEAD diff --git a/subprojects/pixman.wrap b/subprojects/pixman.wrap new file mode 100644 index 000000000..e34794a92 --- /dev/null +++ b/subprojects/pixman.wrap @@ -0,0 +1,3 @@ +[wrap-git] +url = https://gitlab.freedesktop.org/pixman/pixman.git +revision = HEAD diff --git a/subprojects/seatd.wrap b/subprojects/seatd.wrap new file mode 100644 index 000000000..2116366b8 --- /dev/null +++ b/subprojects/seatd.wrap @@ -0,0 +1,3 @@ +[wrap-git] +url = https://git.sr.ht/~kennylevinsen/seatd +revision = HEAD diff --git a/subprojects/wayland-protocols.wrap b/subprojects/wayland-protocols.wrap new file mode 100644 index 000000000..7b093efce --- /dev/null +++ b/subprojects/wayland-protocols.wrap @@ -0,0 +1,3 @@ +[wrap-git] +url = https://gitlab.freedesktop.org/wayland/wayland-protocols.git +revision = HEAD diff --git a/subprojects/wayland.wrap b/subprojects/wayland.wrap new file mode 100644 index 000000000..18d265671 --- /dev/null +++ b/subprojects/wayland.wrap @@ -0,0 +1,3 @@ +[wrap-git] +url = https://gitlab.freedesktop.org/wayland/wayland.git +revision = HEAD From 1c604207c67f4ddd65d66d543f99c98325531e89 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Mon, 18 Nov 2024 18:15:01 +0300 Subject: [PATCH 226/519] seat: don't generate serial 0 0 is reserved to mean "no event has been sent to the client". --- types/seat/wlr_seat.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/types/seat/wlr_seat.c b/types/seat/wlr_seat.c index 2e8e4a844..5ca69aac8 100644 --- a/types/seat/wlr_seat.c +++ b/types/seat/wlr_seat.c @@ -443,9 +443,13 @@ struct wlr_seat_client *wlr_seat_client_from_resource( } uint32_t wlr_seat_client_next_serial(struct wlr_seat_client *client) { - uint32_t serial = wl_display_next_serial(wl_client_get_display(client->client)); - struct wlr_serial_ringset *set = &client->serials; + struct wl_display *display = wl_client_get_display(client->client); + uint32_t serial = wl_display_next_serial(display); + if (serial == 0) { + serial = wl_display_next_serial(display); + } + struct wlr_serial_ringset *set = &client->serials; if (set->count == 0) { set->data[0].min_incl = serial; set->data[0].max_incl = serial; From 7cf8e80ffe82522628b24b065e7c58a2928aa042 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 3 Oct 2024 12:31:52 +0200 Subject: [PATCH 227/519] render/allocator: add udmabuf allocator udmabuf can create a DMA-BUF backed by a memfd. This is useful when running with a software implementation of GL/Vulkan: the memfd can be passed to the parent compositor via wl_shm and the DMA-BUF can be imported via the usual APIs into GL/Vulkan. --- .builds/freebsd.yml | 2 +- include/render/allocator/udmabuf.h | 23 ++++ include/wlr/config.h.in | 1 + meson.build | 1 + meson.options | 2 +- render/allocator/meson.build | 21 +++- render/allocator/udmabuf.c | 162 +++++++++++++++++++++++++++++ 7 files changed, 209 insertions(+), 3 deletions(-) create mode 100644 include/render/allocator/udmabuf.h create mode 100644 render/allocator/udmabuf.c diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index 83cea7495..a99e9c911 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -29,7 +29,7 @@ sources: tasks: - wlroots: | cd wlroots - meson setup build --fatal-meson-warnings -Dauto_features=enabled + meson setup build --fatal-meson-warnings -Dauto_features=enabled -Dallocators=gbm ninja -C build sudo ninja -C build install - tinywl: | diff --git a/include/render/allocator/udmabuf.h b/include/render/allocator/udmabuf.h new file mode 100644 index 000000000..b38b5ab0d --- /dev/null +++ b/include/render/allocator/udmabuf.h @@ -0,0 +1,23 @@ +#ifndef RENDER_ALLOCATOR_UDMABUF_H +#define RENDER_ALLOCATOR_UDMABUF_H + +#include +#include + +struct wlr_udmabuf_buffer { + struct wlr_buffer base; + + size_t size; + struct wlr_shm_attributes shm; + struct wlr_dmabuf_attributes dmabuf; +}; + +struct wlr_udmabuf_allocator { + struct wlr_allocator base; + + int fd; +}; + +struct wlr_allocator *wlr_udmabuf_allocator_create(void); + +#endif diff --git a/include/wlr/config.h.in b/include/wlr/config.h.in index e03049da6..e687bfeec 100644 --- a/include/wlr/config.h.in +++ b/include/wlr/config.h.in @@ -9,6 +9,7 @@ #mesondefine WLR_HAS_VULKAN_RENDERER #mesondefine WLR_HAS_GBM_ALLOCATOR +#mesondefine WLR_HAS_UDMABUF_ALLOCATOR #mesondefine WLR_HAS_XWAYLAND diff --git a/meson.build b/meson.build index 606a8d808..5a1146692 100644 --- a/meson.build +++ b/meson.build @@ -74,6 +74,7 @@ features = { 'gles2-renderer': false, 'vulkan-renderer': false, 'gbm-allocator': false, + 'udmabuf-allocator': false, 'session': false, 'color-management': false, } diff --git a/meson.options b/meson.options index 35961b10e..5863764aa 100644 --- a/meson.options +++ b/meson.options @@ -4,7 +4,7 @@ option('examples', type: 'boolean', value: true, description: 'Build example app option('icon_directory', description: 'Location used to look for cursors (default: ${datadir}/icons)', type: 'string', value: '') option('renderers', type: 'array', choices: ['auto', 'gles2', 'vulkan'], value: ['auto'], description: 'Select built-in renderers') option('backends', type: 'array', choices: ['auto', 'drm', 'libinput', 'x11'], value: ['auto'], description: 'Select built-in backends') -option('allocators', type: 'array', choices: ['auto', 'gbm'], value: ['auto'], +option('allocators', type: 'array', choices: ['auto', 'gbm', 'udmabuf'], value: ['auto'], description: 'Select built-in allocators') option('session', type: 'feature', value: 'auto', description: 'Enable session support') option('color-management', type: 'feature', value: 'auto', description: 'Enable support for color management') diff --git a/render/allocator/meson.build b/render/allocator/meson.build index 730a2a41b..c7559b967 100644 --- a/render/allocator/meson.build +++ b/render/allocator/meson.build @@ -1,6 +1,6 @@ allocators = get_option('allocators') if 'auto' in allocators and get_option('auto_features').enabled() - allocators = ['gbm'] + allocators = ['gbm', 'udmabuf'] elif 'auto' in allocators and get_option('auto_features').disabled() allocators = [] endif @@ -23,3 +23,22 @@ if gbm.found() has = cc.has_function('gbm_bo_get_fd_for_plane', dependencies: [gbm]) internal_config.set10('HAVE_GBM_BO_GET_FD_FOR_PLANE', has) endif + +udmabuf = false +if 'udmabuf' in allocators or 'auto' in allocators + args = ['-D_GNU_SOURCE'] + prefix = [ + '#include ', + '#include ', + ] + udmabuf = (cc.has_function('memfd_create', args: args, prefix: prefix) and + cc.has_define('F_ADD_SEALS', args: args, prefix: prefix) and + cc.has_header('linux/udmabuf.h')) +endif +if 'udmabuf' in allocators and not udmabuf + error('memfd_create(), F_ADD_SEALS and are required for the udmabuf allocator') +endif +if udmabuf + wlr_files += files('udmabuf.c') + features += { 'udmabuf-allocator': true } +endif diff --git a/render/allocator/udmabuf.c b/render/allocator/udmabuf.c new file mode 100644 index 000000000..e0b01b70a --- /dev/null +++ b/render/allocator/udmabuf.c @@ -0,0 +1,162 @@ +#undef _POSIX_C_SOURCE +#define _GNU_SOURCE // for memfd_create() and F_ADD_SEALS + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "render/allocator/udmabuf.h" +#include "render/pixel_format.h" + +static bool buffer_get_shm(struct wlr_buffer *wlr_buffer, struct wlr_shm_attributes *shm) { + struct wlr_udmabuf_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); + *shm = buffer->shm; + return true; +} + +static bool buffer_get_dmabuf(struct wlr_buffer *wlr_buffer, struct wlr_dmabuf_attributes *dmabuf) { + struct wlr_udmabuf_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); + *dmabuf = buffer->dmabuf; + return true; +} + +static void buffer_destroy(struct wlr_buffer *wlr_buffer) { + struct wlr_udmabuf_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); + wlr_dmabuf_attributes_finish(&buffer->dmabuf); + close(buffer->shm.fd); + free(buffer); +} + +static const struct wlr_buffer_impl buffer_impl = { + .destroy = buffer_destroy, + .get_shm = buffer_get_shm, + .get_dmabuf = buffer_get_dmabuf, +}; + +static struct wlr_buffer *allocator_create_buffer( + struct wlr_allocator *wlr_allocator, int width, int height, + const struct wlr_drm_format *format) { + struct wlr_udmabuf_allocator *allocator = wl_container_of(wlr_allocator, allocator, base); + + const struct wlr_pixel_format_info *info = + drm_get_pixel_format_info(format->format); + if (info == NULL) { + wlr_log(WLR_ERROR, "Unsupported pixel format 0x%"PRIX32, format->format); + return NULL; + } + + long page_size = sysconf(_SC_PAGE_SIZE); + if (page_size == -1) { + wlr_log_errno(WLR_ERROR, "Failed to query page size"); + return NULL; + } + + struct wlr_udmabuf_buffer *buffer = calloc(1, sizeof(*buffer)); + if (buffer == NULL) { + return NULL; + } + wlr_buffer_init(&buffer->base, &buffer_impl, width, height); + + // TODO: consider using a single file for multiple buffers + int stride = pixel_format_info_min_stride(info, width); // TODO: align? + size_t size = stride * height; + if (size % page_size != 0) { + size += page_size - (size % page_size); + } + + int memfd = memfd_create("wlroots", MFD_CLOEXEC | MFD_ALLOW_SEALING); + if (memfd < 0) { + wlr_log_errno(WLR_ERROR, "memfd_create() failed"); + goto err_buffer; + } + + if (ftruncate(memfd, size) < 0) { + wlr_log_errno(WLR_ERROR, "ftruncate() failed"); + goto err_memfd; + } + + if (fcntl(memfd, F_ADD_SEALS, F_SEAL_SEAL | F_SEAL_SHRINK) < 0) { + wlr_log_errno(WLR_ERROR, "fcntl(F_ADD_SEALS) failed"); + goto err_memfd; + } + + struct udmabuf_create udmabuf_create = { + .memfd = memfd, + .flags = UDMABUF_FLAGS_CLOEXEC, + .offset = 0, + .size = size, + }; + int dmabuf_fd = ioctl(allocator->fd, UDMABUF_CREATE, &udmabuf_create); + if (dmabuf_fd < 0) { + wlr_log_errno(WLR_ERROR, "ioctl(UDMABUF_CREATE) failed"); + goto err_memfd; + } + + buffer->size = size; + buffer->shm = (struct wlr_shm_attributes){ + .width = width, + .height = height, + .format = format->format, + .offset = 0, + .stride = stride, + .fd = memfd, + }; + buffer->dmabuf = (struct wlr_dmabuf_attributes){ + .width = width, + .height = height, + .format = format->format, + .modifier = DRM_FORMAT_MOD_LINEAR, + .n_planes = 1, + .offset[0] = 0, + .stride[0] = stride, + .fd[0] = dmabuf_fd, + }; + + return &buffer->base; + +err_memfd: + close(memfd); +err_buffer: + free(buffer); + return NULL; +} + +static void allocator_destroy(struct wlr_allocator *wlr_allocator) { + struct wlr_udmabuf_allocator *allocator = wl_container_of(wlr_allocator, allocator, base); + close(allocator->fd); + free(allocator); +} + +static const struct wlr_allocator_interface allocator_impl = { + .destroy = allocator_destroy, + .create_buffer = allocator_create_buffer, +}; + +struct wlr_allocator *wlr_udmabuf_allocator_create(void) { + int fd = open("/dev/udmabuf", O_RDWR | O_CLOEXEC); + if (fd < 0) { + wlr_log_errno(WLR_ERROR, "Failed to open /dev/udmabuf"); + return NULL; + } + + struct wlr_udmabuf_allocator *allocator = calloc(1, sizeof(*allocator)); + if (allocator == NULL) { + close(fd); + return NULL; + } + wlr_allocator_init(&allocator->base, &allocator_impl, + WLR_BUFFER_CAP_SHM | WLR_BUFFER_CAP_DMABUF); + + allocator->fd = fd; + + return &allocator->base; +} From b4ce0d8b39a478258672894913c45311cf386955 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 3 Oct 2024 18:52:27 +0200 Subject: [PATCH 228/519] render/egl: accept negative DRM FD to select software rendering --- render/egl.c | 72 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 24 deletions(-) diff --git a/render/egl.c b/render/egl.c index d38f394e6..d6ca34575 100644 --- a/render/egl.c +++ b/render/egl.c @@ -312,17 +312,6 @@ static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display) { return false; } - if (check_egl_ext(device_exts_str, "EGL_MESA_device_software")) { - if (env_parse_bool("WLR_RENDERER_ALLOW_SOFTWARE")) { - wlr_log(WLR_INFO, "Using software rendering"); - } else { - wlr_log(WLR_ERROR, "Software rendering detected, please use " - "the WLR_RENDERER_ALLOW_SOFTWARE environment variable " - "to proceed"); - return false; - } - } - #ifdef EGL_DRIVER_NAME_EXT if (check_egl_ext(device_exts_str, "EGL_EXT_device_persistent_id")) { driver_name = egl->procs.eglQueryDeviceStringEXT(egl->device, @@ -334,6 +323,20 @@ static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display) { check_egl_ext(device_exts_str, "EGL_EXT_device_drm"); egl->exts.EXT_device_drm_render_node = check_egl_ext(device_exts_str, "EGL_EXT_device_drm_render_node"); + + // The only way a non-DRM device is selected is when the user + // explicitly picks software rendering + if (check_egl_ext(device_exts_str, "EGL_MESA_device_software") && + egl->exts.EXT_device_drm) { + if (env_parse_bool("WLR_RENDERER_ALLOW_SOFTWARE")) { + wlr_log(WLR_INFO, "Using software rendering"); + } else { + wlr_log(WLR_ERROR, "Software rendering detected, please use " + "the WLR_RENDERER_ALLOW_SOFTWARE environment variable " + "to proceed"); + return false; + } + } } if (!check_egl_ext(display_exts_str, "EGL_KHR_no_config_context") && @@ -480,30 +483,51 @@ static EGLDeviceEXT get_egl_device_from_drm_fd(struct wlr_egl *egl, return EGL_NO_DEVICE_EXT; } - drmDevice *device = NULL; - int ret = drmGetDevice(drm_fd, &device); - if (ret < 0) { - wlr_log(WLR_ERROR, "Failed to get DRM device: %s", strerror(-ret)); - free(devices); - return EGL_NO_DEVICE_EXT; + drmDevice *selected_drm_device = NULL; + if (drm_fd >= 0) { + int ret = drmGetDevice(drm_fd, &selected_drm_device); + if (ret < 0) { + wlr_log(WLR_ERROR, "Failed to get DRM device: %s", strerror(-ret)); + free(devices); + return EGL_NO_DEVICE_EXT; + } } EGLDeviceEXT egl_device = NULL; for (int i = 0; i < nb_devices; i++) { - const char *egl_device_name = egl->procs.eglQueryDeviceStringEXT( - devices[i], EGL_DRM_DEVICE_FILE_EXT); - if (egl_device_name == NULL) { + const char *device_exts_str = egl->procs.eglQueryDeviceStringEXT(devices[i], EGL_EXTENSIONS); + if (device_exts_str == NULL) { + wlr_log(WLR_ERROR, "eglQueryDeviceStringEXT(EGL_EXTENSIONS) failed"); continue; } - if (device_has_name(device, egl_device_name)) { - wlr_log(WLR_DEBUG, "Using EGL device %s", egl_device_name); + const char *egl_device_name = NULL; + if (check_egl_ext(device_exts_str, "EGL_EXT_device_drm")) { + egl_device_name = egl->procs.eglQueryDeviceStringEXT(devices[i], EGL_DRM_DEVICE_FILE_EXT); + if (egl_device_name == NULL) { + wlr_log(WLR_ERROR, "eglQueryDeviceStringEXT(EGL_DRM_DEVICE_FILE_EXT) failed"); + continue; + } + } + + bool is_software = check_egl_ext(device_exts_str, "EGL_MESA_device_software"); + + bool found; + if (selected_drm_device != NULL) { + found = egl_device_name != NULL && device_has_name(selected_drm_device, egl_device_name); + } else { + found = is_software; + } + if (found) { + if (egl_device_name != NULL) { + wlr_log(WLR_DEBUG, "Using EGL device %s", egl_device_name); + } egl_device = devices[i]; break; } } - drmFreeDevice(&device); + drmFreeDevice(&selected_drm_device); free(devices); return egl_device; @@ -556,7 +580,7 @@ struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) { wlr_log(WLR_DEBUG, "EXT_platform_device not supported"); } - if (egl->exts.KHR_platform_gbm) { + if (egl->exts.KHR_platform_gbm && drm_fd >= 0) { int gbm_fd = open_render_node(drm_fd); if (gbm_fd < 0) { wlr_log(WLR_ERROR, "Failed to open DRM render node"); From 24232e8e9859217c5535f6a0b1d25a8ebf4acfbd Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 3 Oct 2024 19:03:43 +0200 Subject: [PATCH 229/519] render/vulkan: accept negative DRM FD to select software rendering --- render/vulkan/renderer.c | 5 ----- render/vulkan/vulkan.c | 28 +++++++++++++++++----------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index 337ded3a2..21805ea09 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -2513,11 +2513,6 @@ struct wlr_renderer *wlr_vk_renderer_create_with_drm_fd(int drm_fd) { // Do not use the drm_fd that was passed in: we should prefer the render // node even if a primary node was provided dev->drm_fd = vulkan_open_phdev_drm_fd(phdev); - if (dev->drm_fd < 0) { - vulkan_device_destroy(dev); - vulkan_instance_destroy(ini); - return NULL; - } return vulkan_renderer_create_for_device(dev); } diff --git a/render/vulkan/vulkan.c b/render/vulkan/vulkan.c index 00e5b4519..9454ad801 100644 --- a/render/vulkan/vulkan.c +++ b/render/vulkan/vulkan.c @@ -274,7 +274,7 @@ VkPhysicalDevice vulkan_find_drm_phdev(struct wlr_vk_instance *ini, int drm_fd) } struct stat drm_stat = {0}; - if (fstat(drm_fd, &drm_stat) != 0) { + if (drm_fd >= 0 && fstat(drm_fd, &drm_stat) != 0) { wlr_log_errno(WLR_ERROR, "fstat failed"); return VK_NULL_HANDLE; } @@ -344,17 +344,23 @@ VkPhysicalDevice vulkan_find_drm_phdev(struct wlr_vk_instance *ini, int drm_fd) wlr_log(WLR_INFO, " Driver name: %s (%s)", driver_props.driverName, driver_props.driverInfo); } - if (!has_drm_props) { - wlr_log(WLR_DEBUG, " Ignoring physical device \"%s\": " - "VK_EXT_physical_device_drm not supported", - phdev_props.deviceName); - continue; + bool found; + if (drm_fd >= 0) { + if (!has_drm_props) { + wlr_log(WLR_DEBUG, " Ignoring physical device \"%s\": " + "VK_EXT_physical_device_drm not supported", + phdev_props.deviceName); + continue; + } + + dev_t primary_devid = makedev(drm_props.primaryMajor, drm_props.primaryMinor); + dev_t render_devid = makedev(drm_props.renderMajor, drm_props.renderMinor); + found = primary_devid == drm_stat.st_rdev || render_devid == drm_stat.st_rdev; + } else { + found = phdev_props.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU; } - dev_t primary_devid = makedev(drm_props.primaryMajor, drm_props.primaryMinor); - dev_t render_devid = makedev(drm_props.renderMajor, drm_props.renderMinor); - if (primary_devid == drm_stat.st_rdev || - render_devid == drm_stat.st_rdev) { + if (found) { wlr_log(WLR_INFO, "Found matching Vulkan physical device: %s", phdev_props.deviceName); return phdev; @@ -382,7 +388,7 @@ int vulkan_open_phdev_drm_fd(VkPhysicalDevice phdev) { } else if (drm_props.hasPrimary) { devid = makedev(drm_props.primaryMajor, drm_props.primaryMinor); } else { - wlr_log(WLR_ERROR, "Physical device is missing both render and primary nodes"); + wlr_log(WLR_INFO, "Physical device is missing both render and primary nodes"); return -1; } From cfcf06b8b0ea0e26ae5e7d62c93b6e4098b810f5 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 3 Oct 2024 19:03:27 +0200 Subject: [PATCH 230/519] render: add WLR_RENDERER_FORCE_SOFTWARE env var Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3693 --- docs/env_vars.md | 2 ++ render/wlr_renderer.c | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/docs/env_vars.md b/docs/env_vars.md index 06e47ac59..59c8f078f 100644 --- a/docs/env_vars.md +++ b/docs/env_vars.md @@ -14,6 +14,8 @@ wlroots reads these environment variables hardware-accelerated renderers. * *WLR_RENDER_NO_EXPLICIT_SYNC*: set to 1 to disable explicit synchronization support in renderers. +* *WLR_RENDERER_FORCE_SOFTWARE*: set to 1 to force software rendering for GLES2 + and Vulkan * *WLR_EGL_NO_MODIFIERS*: set to 1 to disable format modifiers in EGL, this can be used to understand and work around driver bugs. diff --git a/render/wlr_renderer.c b/render/wlr_renderer.c index bb7bbbeb9..a4cea9384 100644 --- a/render/wlr_renderer.c +++ b/render/wlr_renderer.c @@ -143,6 +143,12 @@ static bool open_preferred_drm_fd(struct wlr_backend *backend, int *drm_fd_ptr, return true; } + if (env_parse_bool("WLR_RENDERER_FORCE_SOFTWARE")) { + *drm_fd_ptr = -1; + *own_drm_fd = false; + return true; + } + // Allow the user to override the render node const char *render_name = getenv("WLR_RENDER_DRM_DEVICE"); if (render_name != NULL) { From 9fdffba170c84f73795eb13ea9f0841aee285f6a Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 3 Oct 2024 12:33:38 +0200 Subject: [PATCH 231/519] render/allocator: use udmabuf allocator automatically It makes sense to use udmabuf when the backend wants shm, the renderer wants DMA-BUFs, and we don't have a DRM FD because we're running with a software renderer (e.g. llvmpipe or lavapipe). --- render/allocator/allocator.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/render/allocator/allocator.c b/render/allocator/allocator.c index 5e426c3c8..29dc9e72a 100644 --- a/render/allocator/allocator.c +++ b/render/allocator/allocator.c @@ -17,6 +17,10 @@ #include "render/allocator/gbm.h" #endif +#if WLR_HAS_UDMABUF_ALLOCATOR +#include "render/allocator/udmabuf.h" +#endif + void wlr_allocator_init(struct wlr_allocator *alloc, const struct wlr_allocator_interface *impl, uint32_t buffer_caps) { assert(impl && impl->destroy && impl->create_buffer); @@ -146,6 +150,20 @@ struct wlr_allocator *wlr_allocator_autocreate(struct wlr_backend *backend, wlr_log(WLR_DEBUG, "Failed to create drm dumb allocator"); } + uint32_t udmabuf_caps = WLR_BUFFER_CAP_DMABUF | WLR_BUFFER_CAP_SHM; + if ((backend_caps & udmabuf_caps) && (renderer_caps & udmabuf_caps) && + drm_fd < 0) { +#if WLR_HAS_UDMABUF_ALLOCATOR + wlr_log(WLR_DEBUG, "Trying udmabuf allocator"); + if ((alloc = wlr_udmabuf_allocator_create()) != NULL) { + return alloc; + } + wlr_log(WLR_DEBUG, "Failed to create udmabuf allocator"); +#else + wlr_log(WLR_DEBUG, "Skipping udmabuf allocator: disabled at compile-time"); +#endif + } + wlr_log(WLR_ERROR, "Failed to create allocator"); return NULL; } From b908d865b17a29234162f071b40897c15b033d06 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 29 Oct 2024 19:18:18 +0100 Subject: [PATCH 232/519] backend: replace get_buffer_caps hook with a struct field Do the same as wlr_renderer: the supported buffer capabilities are static for the lifetime of the backend. --- backend/backend.c | 9 -------- backend/drm/backend.c | 7 ++---- backend/drm/renderer.c | 1 - backend/headless/backend.c | 10 +++------ backend/multi/backend.c | 40 +++++++++++++-------------------- backend/wayland/backend.c | 14 ++++++------ backend/x11/backend.c | 14 ++++++------ include/backend/backend.h | 13 ----------- include/wlr/backend.h | 3 +++ include/wlr/backend/interface.h | 1 - render/allocator/allocator.c | 4 ++-- render/wlr_renderer.c | 5 ++--- types/output/output.c | 2 +- types/output/render.c | 8 ++----- 14 files changed, 44 insertions(+), 87 deletions(-) delete mode 100644 include/backend/backend.h diff --git a/backend/backend.c b/backend/backend.c index 2be6a5c5e..fdba79c55 100644 --- a/backend/backend.c +++ b/backend/backend.c @@ -12,7 +12,6 @@ #include #include #include -#include "backend/backend.h" #include "backend/multi.h" #include "types/wlr_output.h" #include "util/env.h" @@ -120,14 +119,6 @@ int wlr_backend_get_drm_fd(struct wlr_backend *backend) { return backend->impl->get_drm_fd(backend); } -uint32_t backend_get_buffer_caps(struct wlr_backend *backend) { - if (!backend->impl->get_buffer_caps) { - return 0; - } - - return backend->impl->get_buffer_caps(backend); -} - static size_t parse_outputs_env(const char *name) { const char *outputs_str = getenv(name); if (outputs_str == NULL) { diff --git a/backend/drm/backend.c b/backend/drm/backend.c index ba0e8651c..fa6dd2625 100644 --- a/backend/drm/backend.c +++ b/backend/drm/backend.c @@ -76,10 +76,6 @@ static int backend_get_drm_fd(struct wlr_backend *backend) { return drm->fd; } -static uint32_t backend_get_buffer_caps(struct wlr_backend *backend) { - return WLR_BUFFER_CAP_DMABUF; -} - static bool backend_test(struct wlr_backend *backend, const struct wlr_backend_output_state *states, size_t states_len) { struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend); @@ -96,7 +92,6 @@ static const struct wlr_backend_impl backend_impl = { .start = backend_start, .destroy = backend_destroy, .get_drm_fd = backend_get_drm_fd, - .get_buffer_caps = backend_get_buffer_caps, .test = backend_test, .commit = backend_commit, }; @@ -238,6 +233,8 @@ struct wlr_backend *wlr_drm_backend_create(struct wlr_session *session, } wlr_backend_init(&drm->backend, &backend_impl); + drm->backend.buffer_caps = WLR_BUFFER_CAP_DMABUF; + drm->session = session; wl_list_init(&drm->fbs); wl_list_init(&drm->connectors); diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c index 1253d22bd..721e64bf2 100644 --- a/backend/drm/renderer.c +++ b/backend/drm/renderer.c @@ -8,7 +8,6 @@ #include "backend/drm/drm.h" #include "backend/drm/fb.h" #include "backend/drm/renderer.h" -#include "backend/backend.h" #include "render/drm_format_set.h" #include "render/pixel_format.h" #include "render/wlr_renderer.h" diff --git a/backend/headless/backend.c b/backend/headless/backend.c index c43c03971..d03f520b8 100644 --- a/backend/headless/backend.c +++ b/backend/headless/backend.c @@ -45,16 +45,9 @@ static void backend_destroy(struct wlr_backend *wlr_backend) { free(backend); } -static uint32_t get_buffer_caps(struct wlr_backend *wlr_backend) { - return WLR_BUFFER_CAP_DATA_PTR - | WLR_BUFFER_CAP_DMABUF - | WLR_BUFFER_CAP_SHM; -} - static const struct wlr_backend_impl backend_impl = { .start = backend_start, .destroy = backend_destroy, - .get_buffer_caps = get_buffer_caps, }; static void handle_event_loop_destroy(struct wl_listener *listener, void *data) { @@ -74,6 +67,9 @@ struct wlr_backend *wlr_headless_backend_create(struct wl_event_loop *loop) { wlr_backend_init(&backend->backend, &backend_impl); + backend->backend.buffer_caps = + WLR_BUFFER_CAP_DATA_PTR | WLR_BUFFER_CAP_DMABUF | WLR_BUFFER_CAP_SHM; + backend->event_loop = loop; wl_list_init(&backend->outputs); diff --git a/backend/multi/backend.c b/backend/multi/backend.c index bcb8a4f0c..41ef31ccb 100644 --- a/backend/multi/backend.c +++ b/backend/multi/backend.c @@ -6,7 +6,6 @@ #include #include #include -#include "backend/backend.h" #include "backend/multi.h" struct subbackend_state { @@ -76,28 +75,6 @@ static int multi_backend_get_drm_fd(struct wlr_backend *backend) { return -1; } -static uint32_t multi_backend_get_buffer_caps(struct wlr_backend *backend) { - struct wlr_multi_backend *multi = multi_backend_from_backend(backend); - - if (wl_list_empty(&multi->backends)) { - return 0; - } - - uint32_t caps = WLR_BUFFER_CAP_DATA_PTR | WLR_BUFFER_CAP_DMABUF - | WLR_BUFFER_CAP_SHM; - - struct subbackend_state *sub; - wl_list_for_each(sub, &multi->backends, link) { - uint32_t backend_caps = backend_get_buffer_caps(sub->backend); - if (backend_caps != 0) { - // only count backend capable of presenting a buffer - caps = caps & backend_caps; - } - } - - return caps; -} - static int compare_output_state_backend(const void *data_a, const void *data_b) { const struct wlr_backend_output_state *a = data_a; const struct wlr_backend_output_state *b = data_b; @@ -164,7 +141,6 @@ static const struct wlr_backend_impl backend_impl = { .start = multi_backend_start, .destroy = multi_backend_destroy, .get_drm_fd = multi_backend_get_drm_fd, - .get_buffer_caps = multi_backend_get_buffer_caps, .test = multi_backend_test, .commit = multi_backend_commit, }; @@ -228,16 +204,30 @@ static struct subbackend_state *multi_backend_get_subbackend(struct wlr_multi_ba } static void multi_backend_refresh_features(struct wlr_multi_backend *multi) { + multi->backend.buffer_caps = 0; multi->backend.features.timeline = true; + bool has_buffer_cap = false; + uint32_t buffer_caps_intersection = + WLR_BUFFER_CAP_DATA_PTR | WLR_BUFFER_CAP_DMABUF | WLR_BUFFER_CAP_SHM; struct subbackend_state *sub = NULL; wl_list_for_each(sub, &multi->backends, link) { + // Only take into account backends capable of presenting a buffer + if (sub->backend->buffer_caps != 0) { + has_buffer_cap = true; + buffer_caps_intersection &= sub->backend->buffer_caps; + } + // timeline is only applicable to backends that support DMABUFs - if (backend_get_buffer_caps(sub->backend) & WLR_BUFFER_CAP_DMABUF) { + if (sub->backend->buffer_caps & WLR_BUFFER_CAP_DMABUF) { multi->backend.features.timeline = multi->backend.features.timeline && sub->backend->features.timeline; } } + + if (has_buffer_cap) { + multi->backend.buffer_caps = buffer_caps_intersection; + } } bool wlr_multi_backend_add(struct wlr_backend *_multi, diff --git a/backend/wayland/backend.c b/backend/wayland/backend.c index 475d44222..978209995 100644 --- a/backend/wayland/backend.c +++ b/backend/wayland/backend.c @@ -566,17 +566,10 @@ static int backend_get_drm_fd(struct wlr_backend *backend) { return wl->drm_fd; } -static uint32_t get_buffer_caps(struct wlr_backend *backend) { - struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend); - return (wl->zwp_linux_dmabuf_v1 ? WLR_BUFFER_CAP_DMABUF : 0) - | (wl->shm ? WLR_BUFFER_CAP_SHM : 0); -} - static const struct wlr_backend_impl backend_impl = { .start = backend_start, .destroy = backend_destroy, .get_drm_fd = backend_get_drm_fd, - .get_buffer_caps = get_buffer_caps, }; bool wlr_backend_is_wl(struct wlr_backend *b) { @@ -672,6 +665,13 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_event_loop *loop, zwp_linux_dmabuf_feedback_v1_destroy(linux_dmabuf_feedback_v1); } + if (wl->zwp_linux_dmabuf_v1) { + wl->backend.buffer_caps |= WLR_BUFFER_CAP_DMABUF; + } + if (wl->shm) { + wl->backend.buffer_caps |= WLR_BUFFER_CAP_SHM; + } + int fd = wl_display_get_fd(wl->remote_display); wl->remote_display_src = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE, dispatch_events, wl); diff --git a/backend/x11/backend.c b/backend/x11/backend.c index a265c2a65..0983bad0a 100644 --- a/backend/x11/backend.c +++ b/backend/x11/backend.c @@ -212,17 +212,10 @@ static int backend_get_drm_fd(struct wlr_backend *backend) { return x11->drm_fd; } -static uint32_t get_buffer_caps(struct wlr_backend *backend) { - struct wlr_x11_backend *x11 = get_x11_backend_from_backend(backend); - return (x11->have_dri3 ? WLR_BUFFER_CAP_DMABUF : 0) - | (x11->have_shm ? WLR_BUFFER_CAP_SHM : 0); -} - static const struct wlr_backend_impl backend_impl = { .start = backend_start, .destroy = backend_destroy, .get_drm_fd = backend_get_drm_fd, - .get_buffer_caps = get_buffer_caps, }; bool wlr_backend_is_x11(struct wlr_backend *backend) { @@ -573,6 +566,13 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_event_loop *loop, } free(xi_reply); + if (x11->have_dri3) { + x11->backend.buffer_caps |= WLR_BUFFER_CAP_DMABUF; + } + if (x11->have_shm) { + x11->backend.buffer_caps |= WLR_BUFFER_CAP_SHM; + } + int fd = xcb_get_file_descriptor(x11->xcb); uint32_t events = WL_EVENT_READABLE | WL_EVENT_ERROR | WL_EVENT_HANGUP; x11->event_source = wl_event_loop_add_fd(loop, fd, events, x11_event, x11); diff --git a/include/backend/backend.h b/include/backend/backend.h deleted file mode 100644 index 8c7440c3d..000000000 --- a/include/backend/backend.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef BACKEND_WLR_BACKEND_H -#define BACKEND_WLR_BACKEND_H - -#include - -/** - * Get the supported buffer capabilities. - * - * This functions returns a bitfield of supported wlr_buffer_cap. - */ -uint32_t backend_get_buffer_caps(struct wlr_backend *backend); - -#endif diff --git a/include/wlr/backend.h b/include/wlr/backend.h index 59feb644e..fdcc4ad5c 100644 --- a/include/wlr/backend.h +++ b/include/wlr/backend.h @@ -29,6 +29,9 @@ struct wlr_backend_output_state { struct wlr_backend { const struct wlr_backend_impl *impl; + // Bitfield of supported buffer capabilities (see enum wlr_buffer_cap) + uint32_t buffer_caps; + struct { // Whether wait/signal timelines are supported in output commits bool timeline; diff --git a/include/wlr/backend/interface.h b/include/wlr/backend/interface.h index 938ca73c9..648fa66e8 100644 --- a/include/wlr/backend/interface.h +++ b/include/wlr/backend/interface.h @@ -18,7 +18,6 @@ struct wlr_backend_impl { bool (*start)(struct wlr_backend *backend); void (*destroy)(struct wlr_backend *backend); int (*get_drm_fd)(struct wlr_backend *backend); - uint32_t (*get_buffer_caps)(struct wlr_backend *backend); bool (*test)(struct wlr_backend *backend, const struct wlr_backend_output_state *states, size_t states_len); bool (*commit)(struct wlr_backend *backend, diff --git a/render/allocator/allocator.c b/render/allocator/allocator.c index 29dc9e72a..4fd5211a8 100644 --- a/render/allocator/allocator.c +++ b/render/allocator/allocator.c @@ -2,13 +2,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include "backend/backend.h" #include "render/allocator/drm_dumb.h" #include "render/allocator/shm.h" #include "render/wlr_renderer.h" @@ -96,7 +96,7 @@ static int reopen_drm_node(int drm_fd, bool allow_render_node) { struct wlr_allocator *wlr_allocator_autocreate(struct wlr_backend *backend, struct wlr_renderer *renderer) { - uint32_t backend_caps = backend_get_buffer_caps(backend); + uint32_t backend_caps = backend->buffer_caps; uint32_t renderer_caps = renderer->render_buffer_caps; // Note, drm_fd may be negative if unavailable diff --git a/render/wlr_renderer.c b/render/wlr_renderer.c index a4cea9384..ae324afb9 100644 --- a/render/wlr_renderer.c +++ b/render/wlr_renderer.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -24,7 +25,6 @@ #include #endif // WLR_HAS_VULKAN_RENDERER -#include "backend/backend.h" #include "render/pixel_format.h" #include "render/wlr_renderer.h" #include "util/env.h" @@ -180,8 +180,7 @@ static bool open_preferred_drm_fd(struct wlr_backend *backend, int *drm_fd_ptr, // If the backend hasn't picked a DRM FD, but accepts DMA-BUFs, pick an // arbitrary render node - uint32_t backend_caps = backend_get_buffer_caps(backend); - if (backend_caps & WLR_BUFFER_CAP_DMABUF) { + if (backend->buffer_caps & WLR_BUFFER_CAP_DMABUF) { int drm_fd = open_drm_render_node(); if (drm_fd < 0) { return false; diff --git a/types/output/output.c b/types/output/output.c index c8a174959..4cded1ab2 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -1,7 +1,7 @@ #include -#include #include #include +#include #include #include #include diff --git a/types/output/render.c b/types/output/render.c index 3fb2b22ec..155da5cf8 100644 --- a/types/output/render.c +++ b/types/output/render.c @@ -7,7 +7,6 @@ #include #include #include -#include "backend/backend.h" #include "render/drm_format_set.h" #include "render/wlr_renderer.h" #include "render/pixel_format.h" @@ -17,14 +16,11 @@ bool wlr_output_init_render(struct wlr_output *output, struct wlr_allocator *allocator, struct wlr_renderer *renderer) { assert(allocator != NULL && renderer != NULL); - uint32_t backend_caps = backend_get_buffer_caps(output->backend); - uint32_t renderer_caps = renderer->render_buffer_caps; - - if (!(backend_caps & allocator->buffer_caps)) { + if (!(output->backend->buffer_caps & allocator->buffer_caps)) { wlr_log(WLR_ERROR, "output backend and allocator buffer capabilities " "don't match"); return false; - } else if (!(renderer_caps & allocator->buffer_caps)) { + } else if (!(renderer->render_buffer_caps & allocator->buffer_caps)) { wlr_log(WLR_ERROR, "renderer and allocator buffer capabilities " "don't match"); return false; From 6f6268988be93c4564e7b5be8cd46e62cd18e107 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 25 Dec 2024 18:44:17 +0100 Subject: [PATCH 233/519] backend: document that buffer_caps/features are mutable --- include/wlr/backend.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/wlr/backend.h b/include/wlr/backend.h index fdcc4ad5c..ad8070c23 100644 --- a/include/wlr/backend.h +++ b/include/wlr/backend.h @@ -25,6 +25,9 @@ struct wlr_backend_output_state { /** * A backend provides a set of input and output devices. + * + * Buffer capabilities and features can change over the lifetime of a backend, + * for instance when a child backend is added to a multi-backend. */ struct wlr_backend { const struct wlr_backend_impl *impl; From fa4d8bbad7600a12a32788601b6d7fb1eb323e78 Mon Sep 17 00:00:00 2001 From: liupeng Date: Mon, 30 Dec 2024 17:22:40 +0800 Subject: [PATCH 234/519] render/egl: attribs len could be equal to size Exts.KHR_display_reference makes assert fail. --- render/egl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/render/egl.c b/render/egl.c index d6ca34575..07d3a1425 100644 --- a/render/egl.c +++ b/render/egl.c @@ -392,7 +392,7 @@ static bool egl_init(struct wlr_egl *egl, EGLenum platform, } display_attribs[display_attribs_len++] = EGL_NONE; - assert(display_attribs_len < sizeof(display_attribs) / sizeof(display_attribs[0])); + assert(display_attribs_len <= sizeof(display_attribs) / sizeof(display_attribs[0])); EGLDisplay display = egl->procs.eglGetPlatformDisplayEXT(platform, remote_display, display_attribs); @@ -808,7 +808,7 @@ EGLImageKHR wlr_egl_create_image_from_dmabuf(struct wlr_egl *egl, attribs[atti++] = EGL_TRUE; attribs[atti++] = EGL_NONE; - assert(atti < sizeof(attribs)/sizeof(attribs[0])); + assert(atti <= sizeof(attribs)/sizeof(attribs[0])); EGLImageKHR image = egl->procs.eglCreateImageKHR(egl->display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL, attribs); From 38923826c3bd2c8d5752b63570063282e658e2b0 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Mon, 30 Dec 2024 12:47:30 +0300 Subject: [PATCH 235/519] xwayland,render/vulkan: fix some size assertions --- render/vulkan/vulkan.c | 2 +- xwayland/server.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/render/vulkan/vulkan.c b/render/vulkan/vulkan.c index 9454ad801..78bc25941 100644 --- a/render/vulkan/vulkan.c +++ b/render/vulkan/vulkan.c @@ -604,7 +604,7 @@ struct wlr_vk_device *vulkan_device_create(struct wlr_vk_instance *ini, .ppEnabledExtensionNames = extensions, }; - assert(extensions_len < sizeof(extensions) / sizeof(extensions[0])); + assert(extensions_len <= sizeof(extensions) / sizeof(extensions[0])); res = vkCreateDevice(phdev, &dev_info, NULL, &dev->dev); diff --git a/xwayland/server.c b/xwayland/server.c index 0f3956fcb..417bbe54b 100644 --- a/xwayland/server.c +++ b/xwayland/server.c @@ -98,7 +98,7 @@ noreturn static void exec_xwayland(struct wlr_xwayland_server *server, argv[i++] = NULL; - assert(i < sizeof(argv) / sizeof(argv[0])); + assert(i <= sizeof(argv) / sizeof(argv[0])); char wayland_socket_str[16]; snprintf(wayland_socket_str, sizeof(wayland_socket_str), "%d", server->wl_fd[1]); From 5eed5d622d5afd885e70f269ce8a7e494a4f7cd2 Mon Sep 17 00:00:00 2001 From: sunzhguy Date: Fri, 3 Jan 2025 16:58:36 +0800 Subject: [PATCH 236/519] session: skip adding duplicate DRM device Signed-off-by: sunzhguy --- backend/session/session.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/backend/session/session.c b/backend/session/session.c index 6254a734a..9c77bef5b 100644 --- a/backend/session/session.c +++ b/backend/session/session.c @@ -191,14 +191,22 @@ static int handle_udev_event(int fd, uint32_t mask, void *data) { goto out; } + dev_t devnum = udev_device_get_devnum(udev_dev); if (strcmp(action, "add") == 0) { + struct wlr_device *dev; + wl_list_for_each(dev, &session->devices, link) { + if (dev->dev == devnum) { + wlr_log(WLR_DEBUG, "Skipping duplicate device %s", sysname); + goto out; + } + } + wlr_log(WLR_DEBUG, "DRM device %s added", sysname); struct wlr_session_add_event event = { .path = devnode, }; wl_signal_emit_mutable(&session->events.add_drm_card, &event); } else if (strcmp(action, "change") == 0 || strcmp(action, "remove") == 0) { - dev_t devnum = udev_device_get_devnum(udev_dev); struct wlr_device *dev; wl_list_for_each(dev, &session->devices, link) { if (dev->dev != devnum) { From 7963ba6a0deb5b696050d914ac395bca9c4c06b2 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Fri, 22 Nov 2024 23:18:55 +0300 Subject: [PATCH 237/519] buffer: introduce wlr_buffer_finish() --- examples/cairo-buffer.c | 1 + include/wlr/interfaces/wlr_buffer.h | 5 +++++ render/allocator/drm_dumb.c | 2 ++ render/allocator/gbm.c | 6 ++++-- types/buffer/buffer.c | 8 +++++--- types/buffer/client.c | 3 +++ types/buffer/dmabuf.c | 1 + types/buffer/readonly_data.c | 1 + types/wlr_drm.c | 5 ++++- types/wlr_linux_dmabuf_v1.c | 5 ++++- types/wlr_shm.c | 5 ++++- types/wlr_single_pixel_buffer_v1.c | 5 ++++- 12 files changed, 38 insertions(+), 9 deletions(-) diff --git a/examples/cairo-buffer.c b/examples/cairo-buffer.c index 7de4a053b..6392b2ac5 100644 --- a/examples/cairo-buffer.c +++ b/examples/cairo-buffer.c @@ -24,6 +24,7 @@ struct cairo_buffer { static void cairo_buffer_destroy(struct wlr_buffer *wlr_buffer) { struct cairo_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); + wlr_buffer_finish(wlr_buffer); cairo_surface_destroy(buffer->surface); free(buffer); } diff --git a/include/wlr/interfaces/wlr_buffer.h b/include/wlr/interfaces/wlr_buffer.h index 820300f65..4244a00d5 100644 --- a/include/wlr/interfaces/wlr_buffer.h +++ b/include/wlr/interfaces/wlr_buffer.h @@ -36,6 +36,11 @@ struct wlr_buffer_resource_interface { void wlr_buffer_init(struct wlr_buffer *buffer, const struct wlr_buffer_impl *impl, int width, int height); +/** + * Emit the destroy event and clean up common buffer state. + */ +void wlr_buffer_finish(struct wlr_buffer *buffer); + /** * Allows the registration of a struct wl_resource implementation. * diff --git a/render/allocator/drm_dumb.c b/render/allocator/drm_dumb.c index eb4ce99fe..ea2757b0b 100644 --- a/render/allocator/drm_dumb.c +++ b/render/allocator/drm_dumb.c @@ -135,6 +135,8 @@ static bool buffer_get_dmabuf(struct wlr_buffer *wlr_buffer, static void buffer_destroy(struct wlr_buffer *wlr_buffer) { struct wlr_drm_dumb_buffer *buf = drm_dumb_buffer_from_buffer(wlr_buffer); + wlr_buffer_finish(wlr_buffer); + if (buf->data) { munmap(buf->data, buf->size); } diff --git a/render/allocator/gbm.c b/render/allocator/gbm.c index da035c668..3e157bb69 100644 --- a/render/allocator/gbm.c +++ b/render/allocator/gbm.c @@ -155,8 +155,10 @@ static struct wlr_gbm_buffer *create_buffer(struct wlr_gbm_allocator *alloc, } static void buffer_destroy(struct wlr_buffer *wlr_buffer) { - struct wlr_gbm_buffer *buffer = - get_gbm_buffer_from_buffer(wlr_buffer); + struct wlr_gbm_buffer *buffer = get_gbm_buffer_from_buffer(wlr_buffer); + + wlr_buffer_finish(wlr_buffer); + wlr_dmabuf_attributes_finish(&buffer->dmabuf); if (buffer->gbm_bo != NULL) { gbm_bo_destroy(buffer->gbm_bo); diff --git a/types/buffer/buffer.c b/types/buffer/buffer.c index 953207a2c..1438b27b2 100644 --- a/types/buffer/buffer.c +++ b/types/buffer/buffer.c @@ -22,6 +22,11 @@ void wlr_buffer_init(struct wlr_buffer *buffer, wlr_addon_set_init(&buffer->addons); } +void wlr_buffer_finish(struct wlr_buffer *buffer) { + wl_signal_emit_mutable(&buffer->events.destroy, NULL); + wlr_addon_set_finish(&buffer->addons); +} + static void buffer_consider_destroy(struct wlr_buffer *buffer) { if (!buffer->dropped || buffer->n_locks > 0) { return; @@ -29,9 +34,6 @@ static void buffer_consider_destroy(struct wlr_buffer *buffer) { assert(!buffer->accessing_data_ptr); - wl_signal_emit_mutable(&buffer->events.destroy, NULL); - wlr_addon_set_finish(&buffer->addons); - buffer->impl->destroy(buffer); } diff --git a/types/buffer/client.c b/types/buffer/client.c index d96603a45..6aa55386d 100644 --- a/types/buffer/client.c +++ b/types/buffer/client.c @@ -24,6 +24,9 @@ static struct wlr_client_buffer *client_buffer_from_buffer( static void client_buffer_destroy(struct wlr_buffer *buffer) { struct wlr_client_buffer *client_buffer = client_buffer_from_buffer(buffer); + + wlr_buffer_finish(buffer); + wl_list_remove(&client_buffer->source_destroy.link); wl_list_remove(&client_buffer->renderer_destroy.link); wlr_texture_destroy(client_buffer->texture); diff --git a/types/buffer/dmabuf.c b/types/buffer/dmabuf.c index af0c8c5c8..078821e43 100644 --- a/types/buffer/dmabuf.c +++ b/types/buffer/dmabuf.c @@ -15,6 +15,7 @@ static struct wlr_dmabuf_buffer *dmabuf_buffer_from_buffer( static void dmabuf_buffer_destroy(struct wlr_buffer *wlr_buffer) { struct wlr_dmabuf_buffer *buffer = dmabuf_buffer_from_buffer(wlr_buffer); + wlr_buffer_finish(wlr_buffer); if (buffer->saved) { wlr_dmabuf_attributes_finish(&buffer->dmabuf); } diff --git a/types/buffer/readonly_data.c b/types/buffer/readonly_data.c index f934ddc77..1c8fcf126 100644 --- a/types/buffer/readonly_data.c +++ b/types/buffer/readonly_data.c @@ -17,6 +17,7 @@ static struct wlr_readonly_data_buffer *readonly_data_buffer_from_buffer( static void readonly_data_buffer_destroy(struct wlr_buffer *wlr_buffer) { struct wlr_readonly_data_buffer *buffer = readonly_data_buffer_from_buffer(wlr_buffer); + wlr_buffer_finish(wlr_buffer); free(buffer->saved_data); free(buffer); } diff --git a/types/wlr_drm.c b/types/wlr_drm.c index c58cdb4ec..e7e57c3c6 100644 --- a/types/wlr_drm.c +++ b/types/wlr_drm.c @@ -34,11 +34,14 @@ static struct wlr_drm_buffer *drm_buffer_from_buffer( static void buffer_destroy(struct wlr_buffer *wlr_buffer) { struct wlr_drm_buffer *buffer = drm_buffer_from_buffer(wlr_buffer); + wl_list_remove(&buffer->release.link); + + wlr_buffer_finish(wlr_buffer); + if (buffer->resource != NULL) { wl_resource_set_user_data(buffer->resource, NULL); } wlr_dmabuf_attributes_finish(&buffer->dmabuf); - wl_list_remove(&buffer->release.link); free(buffer); } diff --git a/types/wlr_linux_dmabuf_v1.c b/types/wlr_linux_dmabuf_v1.c index 7809a29d8..8ccca906a 100644 --- a/types/wlr_linux_dmabuf_v1.c +++ b/types/wlr_linux_dmabuf_v1.c @@ -100,11 +100,14 @@ static struct wlr_dmabuf_v1_buffer *dmabuf_v1_buffer_from_buffer( static void buffer_destroy(struct wlr_buffer *wlr_buffer) { struct wlr_dmabuf_v1_buffer *buffer = dmabuf_v1_buffer_from_buffer(wlr_buffer); + wl_list_remove(&buffer->release.link); + + wlr_buffer_finish(wlr_buffer); + if (buffer->resource != NULL) { wl_resource_set_user_data(buffer->resource, NULL); } wlr_dmabuf_attributes_finish(&buffer->attributes); - wl_list_remove(&buffer->release.link); free(buffer); } diff --git a/types/wlr_shm.c b/types/wlr_shm.c index 1f45c88a3..171724471 100644 --- a/types/wlr_shm.c +++ b/types/wlr_shm.c @@ -155,8 +155,11 @@ static void pool_consider_destroy(struct wlr_shm_pool *pool); static void buffer_destroy(struct wlr_buffer *wlr_buffer) { struct wlr_shm_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); - assert(buffer->resource == NULL); wl_list_remove(&buffer->release.link); + + wlr_buffer_finish(wlr_buffer); + + assert(buffer->resource == NULL); wl_list_remove(&buffer->link); pool_consider_destroy(buffer->pool); free(buffer); diff --git a/types/wlr_single_pixel_buffer_v1.c b/types/wlr_single_pixel_buffer_v1.c index 2dab6c53c..949588ce4 100644 --- a/types/wlr_single_pixel_buffer_v1.c +++ b/types/wlr_single_pixel_buffer_v1.c @@ -53,10 +53,13 @@ static const struct wlr_buffer_resource_interface buffer_resource_interface = { static void buffer_destroy(struct wlr_buffer *wlr_buffer) { struct wlr_single_pixel_buffer_v1 *buffer = wl_container_of(wlr_buffer, buffer, base); + wl_list_remove(&buffer->release.link); + + wlr_buffer_finish(wlr_buffer); + if (buffer->resource != NULL) { wl_resource_set_user_data(buffer->resource, NULL); } - wl_list_remove(&buffer->release.link); free(buffer); } From d5d650f9f62e29324e5cd20dc9fd998ca73a5489 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Fri, 22 Nov 2024 23:21:08 +0300 Subject: [PATCH 238/519] output: introduce wlr_output_finish() --- backend/drm/drm.c | 2 ++ backend/headless/output.c | 6 ++++-- backend/wayland/output.c | 2 ++ backend/x11/output.c | 2 ++ include/wlr/interfaces/wlr_output.h | 4 ++++ types/output/output.c | 13 ++++++++----- 6 files changed, 22 insertions(+), 7 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index bed6ba497..727832c6b 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -1215,6 +1215,8 @@ static void dealloc_crtc(struct wlr_drm_connector *conn); static void drm_connector_destroy_output(struct wlr_output *output) { struct wlr_drm_connector *conn = get_drm_connector_from_output(output); + wlr_output_finish(output); + dealloc_crtc(conn); conn->status = DRM_MODE_DISCONNECTED; diff --git a/backend/headless/output.c b/backend/headless/output.c index 5a00e2e91..4d983a72a 100644 --- a/backend/headless/output.c +++ b/backend/headless/output.c @@ -80,8 +80,10 @@ static bool output_commit(struct wlr_output *wlr_output, } static void output_destroy(struct wlr_output *wlr_output) { - struct wlr_headless_output *output = - headless_output_from_output(wlr_output); + struct wlr_headless_output *output = headless_output_from_output(wlr_output); + + wlr_output_finish(wlr_output); + wl_list_remove(&output->link); wl_event_source_remove(output->frame_timer); free(output); diff --git a/backend/wayland/output.c b/backend/wayland/output.c index 5bc05f947..c3e4866da 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -933,6 +933,8 @@ static void output_destroy(struct wlr_output *wlr_output) { return; } + wlr_output_finish(wlr_output); + wl_list_remove(&output->link); if (output->cursor.surface) { diff --git a/backend/x11/output.c b/backend/x11/output.c index 67f1dae95..0b63a7088 100644 --- a/backend/x11/output.c +++ b/backend/x11/output.c @@ -94,6 +94,8 @@ static void output_destroy(struct wlr_output *wlr_output) { struct wlr_x11_output *output = get_x11_output_from_output(wlr_output); struct wlr_x11_backend *x11 = output->x11; + wlr_output_finish(wlr_output); + pixman_region32_fini(&output->exposed); wlr_pointer_finish(&output->pointer); diff --git a/include/wlr/interfaces/wlr_output.h b/include/wlr/interfaces/wlr_output.h index 2a40a0fe3..6da2c2c54 100644 --- a/include/wlr/interfaces/wlr_output.h +++ b/include/wlr/interfaces/wlr_output.h @@ -108,6 +108,10 @@ struct wlr_output_impl { void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend, const struct wlr_output_impl *impl, struct wl_event_loop *event_loop, const struct wlr_output_state *state); +/** + * Emit the destroy event and clean up common output state. + */ +void wlr_output_finish(struct wlr_output *output); /** * Notify compositors that they need to submit a new frame in order to apply * output changes. diff --git a/types/output/output.c b/types/output/output.c index 4cded1ab2..7bfdf9c77 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -375,11 +375,7 @@ void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend, } } -void wlr_output_destroy(struct wlr_output *output) { - if (!output) { - return; - } - +void wlr_output_finish(struct wlr_output *output) { wl_signal_emit_mutable(&output->events.destroy, output); wlr_output_destroy_global(output); @@ -418,10 +414,17 @@ void wlr_output_destroy(struct wlr_output *output) { free(output->make); free(output->model); free(output->serial); +} + +void wlr_output_destroy(struct wlr_output *output) { + if (!output) { + return; + } if (output->impl && output->impl->destroy) { output->impl->destroy(output); } else { + wlr_output_finish(output); free(output); } } From 89000b7df0de7073ad894240c06aa84021034da1 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Wed, 8 Jan 2025 20:31:47 +0300 Subject: [PATCH 239/519] backend/session: simplify udev event action matching This avoids extra strcmp() calls and is easier to read. --- backend/session/session.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/backend/session/session.c b/backend/session/session.c index 9c77bef5b..b0b2894b4 100644 --- a/backend/session/session.c +++ b/backend/session/session.c @@ -206,25 +206,25 @@ static int handle_udev_event(int fd, uint32_t mask, void *data) { .path = devnode, }; wl_signal_emit_mutable(&session->events.add_drm_card, &event); - } else if (strcmp(action, "change") == 0 || strcmp(action, "remove") == 0) { + } else if (strcmp(action, "change") == 0) { struct wlr_device *dev; wl_list_for_each(dev, &session->devices, link) { - if (dev->dev != devnum) { - continue; - } - - if (strcmp(action, "change") == 0) { + if (dev->dev == devnum) { wlr_log(WLR_DEBUG, "DRM device %s changed", sysname); struct wlr_device_change_event event = {0}; read_udev_change_event(&event, udev_dev); wl_signal_emit_mutable(&dev->events.change, &event); - } else if (strcmp(action, "remove") == 0) { + break; + } + } + } else if (strcmp(action, "remove") == 0) { + struct wlr_device *dev; + wl_list_for_each(dev, &session->devices, link) { + if (dev->dev == devnum) { wlr_log(WLR_DEBUG, "DRM device %s removed", sysname); wl_signal_emit_mutable(&dev->events.remove, NULL); - } else { - assert(0); + break; } - break; } } From 62ecec6d5341e8a39df04eeffa069c1beaa0b208 Mon Sep 17 00:00:00 2001 From: llyyr Date: Sat, 11 Jan 2025 20:20:10 +0530 Subject: [PATCH 240/519] wlr_drag: cleanup icon_destroy listener when destroying icon --- types/data_device/wlr_drag.c | 1 + 1 file changed, 1 insertion(+) diff --git a/types/data_device/wlr_drag.c b/types/data_device/wlr_drag.c index 6338a4388..b1d348949 100644 --- a/types/data_device/wlr_drag.c +++ b/types/data_device/wlr_drag.c @@ -362,6 +362,7 @@ static const struct wlr_keyboard_grab_interface static void drag_handle_icon_destroy(struct wl_listener *listener, void *data) { struct wlr_drag *drag = wl_container_of(listener, drag, icon_destroy); drag->icon = NULL; + wl_list_remove(&drag->icon_destroy.link); } static void drag_handle_drag_source_destroy(struct wl_listener *listener, From 4a67628cb03e0999385eb3f98b65879a449790b8 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Sat, 11 Jan 2025 22:42:18 +0300 Subject: [PATCH 241/519] session: drop unused wlr_session.vtnr --- include/wlr/backend/session.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/include/wlr/backend/session.h b/include/wlr/backend/session.h index bd218ce8b..a1ff0c317 100644 --- a/include/wlr/backend/session.h +++ b/include/wlr/backend/session.h @@ -43,11 +43,6 @@ struct wlr_session { */ bool active; - /* - * 0 if virtual terminals are not supported - * i.e. seat != "seat0" - */ - unsigned vtnr; char seat[256]; struct udev *udev; From a231bf7f62c52f7cc833b2e34a00e976b432fff8 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Sat, 11 Jan 2025 22:40:21 +0300 Subject: [PATCH 242/519] backend: drop wl_display refs from docs --- include/wlr/backend.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/wlr/backend.h b/include/wlr/backend.h index ad8070c23..31588ca9c 100644 --- a/include/wlr/backend.h +++ b/include/wlr/backend.h @@ -65,13 +65,12 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_event_loop *loop, struct wlr_session **session_ptr); /** * Start the backend. This may signal new_input or new_output immediately, but - * may also wait until the display's event loop begins. Returns false on - * failure. + * may also wait until the event loop is started. Returns false on failure. */ bool wlr_backend_start(struct wlr_backend *backend); /** * Destroy the backend and clean up all of its resources. Normally called - * automatically when the struct wl_display is destroyed. + * automatically when the event loop is destroyed. */ void wlr_backend_destroy(struct wlr_backend *backend); /** From eb85831284b6f46301e41205c5d4e08fc5b92c27 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Fri, 10 Jan 2025 23:35:42 +0300 Subject: [PATCH 243/519] text-input: fix wlr_text_input_manager_v3.text_inputs comment --- include/wlr/types/wlr_text_input_v3.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/wlr/types/wlr_text_input_v3.h b/include/wlr/types/wlr_text_input_v3.h index 20396a91f..77f7a672e 100644 --- a/include/wlr/types/wlr_text_input_v3.h +++ b/include/wlr/types/wlr_text_input_v3.h @@ -71,7 +71,7 @@ struct wlr_text_input_v3 { struct wlr_text_input_manager_v3 { struct wl_global *global; - struct wl_list text_inputs; // struct wlr_text_input_v3.resource.link + struct wl_list text_inputs; // wlr_text_input_v3.link struct { struct wl_signal text_input; // struct wlr_text_input_v3 From dadcbf65e6587ba2f1320447298f37b19d96bf84 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 12 Feb 2024 20:14:56 +0100 Subject: [PATCH 244/519] ext-image-capture-source-v1: add base interface --- .../wlr_ext_image_capture_source_v1.h | 30 ++++++++ .../types/wlr_ext_image_capture_source_v1.h | 60 ++++++++++++++++ protocol/meson.build | 1 + types/ext_image_capture_source_v1/base.c | 71 +++++++++++++++++++ types/meson.build | 1 + 5 files changed, 163 insertions(+) create mode 100644 include/wlr/interfaces/wlr_ext_image_capture_source_v1.h create mode 100644 include/wlr/types/wlr_ext_image_capture_source_v1.h create mode 100644 types/ext_image_capture_source_v1/base.c diff --git a/include/wlr/interfaces/wlr_ext_image_capture_source_v1.h b/include/wlr/interfaces/wlr_ext_image_capture_source_v1.h new file mode 100644 index 000000000..239e515cf --- /dev/null +++ b/include/wlr/interfaces/wlr_ext_image_capture_source_v1.h @@ -0,0 +1,30 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_INTERFACES_WLR_EXT_IMAGE_CAPTURE_SOURCE_V1_H +#define WLR_INTERFACES_WLR_EXT_IMAGE_CAPTURE_SOURCE_V1_H + +#include +#include + +struct wlr_ext_image_copy_capture_frame_v1; + +struct wlr_ext_image_capture_source_v1_interface { + void (*schedule_frame)(struct wlr_ext_image_capture_source_v1 *source); + void (*copy_frame)(struct wlr_ext_image_capture_source_v1 *source, + struct wlr_ext_image_copy_capture_frame_v1 *dst_frame, + struct wlr_ext_image_capture_source_v1_frame_event *frame_event); +}; + +void wlr_ext_image_capture_source_v1_init(struct wlr_ext_image_capture_source_v1 *source, + const struct wlr_ext_image_capture_source_v1_interface *impl); +void wlr_ext_image_capture_source_v1_finish(struct wlr_ext_image_capture_source_v1 *source); +bool wlr_ext_image_capture_source_v1_create_resource(struct wlr_ext_image_capture_source_v1 *source, + struct wl_client *client, uint32_t new_id); + +#endif diff --git a/include/wlr/types/wlr_ext_image_capture_source_v1.h b/include/wlr/types/wlr_ext_image_capture_source_v1.h new file mode 100644 index 000000000..5405bf01e --- /dev/null +++ b/include/wlr/types/wlr_ext_image_capture_source_v1.h @@ -0,0 +1,60 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_TYPES_WLR_EXT_IMAGE_CAPTURE_SOURCE_V1_H +#define WLR_TYPES_WLR_EXT_IMAGE_CAPTURE_SOURCE_V1_H + +#include +#include +#include + +/** + * A screen capture source. + * + * When the size, device or formats change, the constraints_update event is + * emitted. + * + * The device and formats advertised are suitable for copying into a + * struct wlr_buffer. + */ +struct wlr_ext_image_capture_source_v1 { + const struct wlr_ext_image_capture_source_v1_interface *impl; + struct wl_list resources; // wl_resource_get_link() + + uint32_t width, height; + + uint32_t *shm_formats; + size_t shm_formats_len; + + dev_t dmabuf_device; + struct wlr_drm_format_set dmabuf_formats; + + struct { + struct wl_signal constraints_update; + struct wl_signal frame; // struct wlr_ext_image_capture_source_v1_frame_event + struct wl_signal destroy; + } events; +}; + +/** + * Event indicating that the source has produced a new frame. + */ +struct wlr_ext_image_capture_source_v1_frame_event { + const pixman_region32_t *damage; +}; + +/** + * Obtain a struct wlr_ext_image_capture_source_v1 from an ext_image_capture_source_v1 + * resource. + * + * Asserts that the resource has the correct type. Returns NULL if the resource + * is inert. + */ +struct wlr_ext_image_capture_source_v1 *wlr_ext_image_capture_source_v1_from_resource(struct wl_resource *resource); + +#endif diff --git a/protocol/meson.build b/protocol/meson.build index 2b0e49977..8afa4057a 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -29,6 +29,7 @@ protocols = { 'drm-lease-v1': wl_protocol_dir / 'staging/drm-lease/drm-lease-v1.xml', 'ext-foreign-toplevel-list-v1': wl_protocol_dir / 'staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml', 'ext-idle-notify-v1': wl_protocol_dir / 'staging/ext-idle-notify/ext-idle-notify-v1.xml', + 'ext-image-capture-source-v1': wl_protocol_dir / 'staging/ext-image-capture-source/ext-image-capture-source-v1.xml', 'ext-session-lock-v1': wl_protocol_dir / 'staging/ext-session-lock/ext-session-lock-v1.xml', 'fractional-scale-v1': wl_protocol_dir / 'staging/fractional-scale/fractional-scale-v1.xml', 'linux-drm-syncobj-v1': wl_protocol_dir / 'staging/linux-drm-syncobj/linux-drm-syncobj-v1.xml', diff --git a/types/ext_image_capture_source_v1/base.c b/types/ext_image_capture_source_v1/base.c new file mode 100644 index 000000000..be89beb88 --- /dev/null +++ b/types/ext_image_capture_source_v1/base.c @@ -0,0 +1,71 @@ +#include +#include +#include +#include +#include "ext-image-capture-source-v1-protocol.h" + +static void source_handle_destroy(struct wl_client *client, + struct wl_resource *source_resource) { + wl_resource_destroy(source_resource); +} + +static const struct ext_image_capture_source_v1_interface source_impl = { + .destroy = source_handle_destroy, +}; + +struct wlr_ext_image_capture_source_v1 *wlr_ext_image_capture_source_v1_from_resource(struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &ext_image_capture_source_v1_interface, &source_impl)); + return wl_resource_get_user_data(resource); +} + +static void source_handle_resource_destroy(struct wl_resource *resource) { + wl_list_remove(wl_resource_get_link(resource)); +} + +void wlr_ext_image_capture_source_v1_init(struct wlr_ext_image_capture_source_v1 *source, + const struct wlr_ext_image_capture_source_v1_interface *impl) { + *source = (struct wlr_ext_image_capture_source_v1){ + .impl = impl, + }; + wl_list_init(&source->resources); + wl_signal_init(&source->events.destroy); + wl_signal_init(&source->events.constraints_update); + wl_signal_init(&source->events.frame); +} + +void wlr_ext_image_capture_source_v1_finish(struct wlr_ext_image_capture_source_v1 *source) { + wl_signal_emit_mutable(&source->events.destroy, NULL); + + assert(wl_list_empty(&source->events.destroy.listener_list)); + assert(wl_list_empty(&source->events.constraints_update.listener_list)); + assert(wl_list_empty(&source->events.frame.listener_list)); + + struct wl_resource *resource, *resource_tmp; + wl_resource_for_each_safe(resource, resource_tmp, &source->resources) { + wl_resource_set_user_data(resource, NULL); + wl_list_remove(wl_resource_get_link(resource)); + wl_list_init(wl_resource_get_link(resource)); + } + + free(source->shm_formats); + wlr_drm_format_set_finish(&source->dmabuf_formats); +} + +bool wlr_ext_image_capture_source_v1_create_resource(struct wlr_ext_image_capture_source_v1 *source, + struct wl_client *client, uint32_t new_id) { + struct wl_resource *resource = wl_resource_create(client, + &ext_image_capture_source_v1_interface, 1, new_id); + if (resource == NULL) { + wl_client_post_no_memory(client); + return false; + } + wl_resource_set_implementation(resource, &source_impl, source, + source_handle_resource_destroy); + if (source != NULL) { + wl_list_insert(&source->resources, wl_resource_get_link(resource)); + } else { + wl_list_init(wl_resource_get_link(resource)); + } + return true; +} diff --git a/types/meson.build b/types/meson.build index 237d41f53..39a2ac793 100644 --- a/types/meson.build +++ b/types/meson.build @@ -3,6 +3,7 @@ wlr_files += files( 'data_device/wlr_data_offer.c', 'data_device/wlr_data_source.c', 'data_device/wlr_drag.c', + 'ext_image_capture_source_v1/base.c', 'output/cursor.c', 'output/output.c', 'output/render.c', From 6712e774d4aa4b055a66c49226ae2096ad61978d Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 8 Apr 2024 19:59:44 +0200 Subject: [PATCH 245/519] ext-image-capture-source-v1: add source pointer cursors --- .../wlr_ext_image_capture_source_v1.h | 7 +++++++ .../types/wlr_ext_image_capture_source_v1.h | 20 +++++++++++++++++++ types/ext_image_capture_source_v1/base.c | 11 ++++++++++ 3 files changed, 38 insertions(+) diff --git a/include/wlr/interfaces/wlr_ext_image_capture_source_v1.h b/include/wlr/interfaces/wlr_ext_image_capture_source_v1.h index 239e515cf..49737ac3b 100644 --- a/include/wlr/interfaces/wlr_ext_image_capture_source_v1.h +++ b/include/wlr/interfaces/wlr_ext_image_capture_source_v1.h @@ -13,12 +13,15 @@ #include struct wlr_ext_image_copy_capture_frame_v1; +struct wlr_seat; struct wlr_ext_image_capture_source_v1_interface { void (*schedule_frame)(struct wlr_ext_image_capture_source_v1 *source); void (*copy_frame)(struct wlr_ext_image_capture_source_v1 *source, struct wlr_ext_image_copy_capture_frame_v1 *dst_frame, struct wlr_ext_image_capture_source_v1_frame_event *frame_event); + struct wlr_ext_image_capture_source_v1_cursor *(*get_pointer_cursor)( + struct wlr_ext_image_capture_source_v1 *source, struct wlr_seat *seat); }; void wlr_ext_image_capture_source_v1_init(struct wlr_ext_image_capture_source_v1 *source, @@ -27,4 +30,8 @@ void wlr_ext_image_capture_source_v1_finish(struct wlr_ext_image_capture_source_ bool wlr_ext_image_capture_source_v1_create_resource(struct wlr_ext_image_capture_source_v1 *source, struct wl_client *client, uint32_t new_id); +void wlr_ext_image_capture_source_v1_cursor_init(struct wlr_ext_image_capture_source_v1_cursor *source_cursor, + const struct wlr_ext_image_capture_source_v1_interface *impl); +void wlr_ext_image_capture_source_v1_cursor_finish(struct wlr_ext_image_capture_source_v1_cursor *source_cursor); + #endif diff --git a/include/wlr/types/wlr_ext_image_capture_source_v1.h b/include/wlr/types/wlr_ext_image_capture_source_v1.h index 5405bf01e..4247dd29a 100644 --- a/include/wlr/types/wlr_ext_image_capture_source_v1.h +++ b/include/wlr/types/wlr_ext_image_capture_source_v1.h @@ -48,6 +48,26 @@ struct wlr_ext_image_capture_source_v1_frame_event { const pixman_region32_t *damage; }; +/** + * A cursor capture source. + * + * Provides additional cursor-specific functionality on top of + * struct wlr_ext_image_capture_source_v1. + */ +struct wlr_ext_image_capture_source_v1_cursor { + struct wlr_ext_image_capture_source_v1 base; + + bool entered; + int32_t x, y; + struct { + int32_t x, y; + } hotspot; + + struct { + struct wl_signal update; + } events; +}; + /** * Obtain a struct wlr_ext_image_capture_source_v1 from an ext_image_capture_source_v1 * resource. diff --git a/types/ext_image_capture_source_v1/base.c b/types/ext_image_capture_source_v1/base.c index be89beb88..af68beac5 100644 --- a/types/ext_image_capture_source_v1/base.c +++ b/types/ext_image_capture_source_v1/base.c @@ -69,3 +69,14 @@ bool wlr_ext_image_capture_source_v1_create_resource(struct wlr_ext_image_captur } return true; } + +void wlr_ext_image_capture_source_v1_cursor_init(struct wlr_ext_image_capture_source_v1_cursor *source_cursor, + const struct wlr_ext_image_capture_source_v1_interface *impl) { + *source_cursor = (struct wlr_ext_image_capture_source_v1_cursor){0}; + wlr_ext_image_capture_source_v1_init(&source_cursor->base, impl); + wl_signal_init(&source_cursor->events.update); +} + +void wlr_ext_image_capture_source_v1_cursor_finish(struct wlr_ext_image_capture_source_v1_cursor *source_cursor) { + wlr_ext_image_capture_source_v1_finish(&source_cursor->base); +} From 4e4155ccbee6a36fe81ae1cdf7b5508b5445df4a Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 18 Apr 2024 18:19:53 +0200 Subject: [PATCH 246/519] ext-image-copy-capture-v1: new protocol implementation Co-authored-by: Andri Yngvason --- .../wlr/types/wlr_ext_image_copy_capture_v1.h | 57 ++ protocol/meson.build | 1 + types/meson.build | 1 + types/wlr_ext_image_copy_capture_v1.c | 591 ++++++++++++++++++ 4 files changed, 650 insertions(+) create mode 100644 include/wlr/types/wlr_ext_image_copy_capture_v1.h create mode 100644 types/wlr_ext_image_copy_capture_v1.c diff --git a/include/wlr/types/wlr_ext_image_copy_capture_v1.h b/include/wlr/types/wlr_ext_image_copy_capture_v1.h new file mode 100644 index 000000000..d53405de9 --- /dev/null +++ b/include/wlr/types/wlr_ext_image_copy_capture_v1.h @@ -0,0 +1,57 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_TYPES_WLR_EXT_IMAGE_COPY_CAPTURE_V1_H +#define WLR_TYPES_WLR_EXT_IMAGE_COPY_CAPTURE_V1_H + +#include +#include +#include "ext-image-copy-capture-v1-protocol.h" + +struct wlr_ext_image_copy_capture_manager_v1 { + struct wl_global *global; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; +}; + +struct wlr_ext_image_copy_capture_frame_v1 { + struct wl_resource *resource; + bool capturing; + struct wlr_buffer *buffer; + pixman_region32_t buffer_damage; + + struct { + struct wl_signal destroy; + } events; + + struct { + struct wlr_ext_image_copy_capture_session_v1 *session; + } WLR_PRIVATE; +}; + +struct wlr_ext_image_copy_capture_manager_v1 *wlr_ext_image_copy_capture_manager_v1_create( + struct wl_display *display, uint32_t version); + +/** + * Notify the client that the frame is ready. + * + * This function destroys the frame. + */ +void wlr_ext_image_copy_capture_frame_v1_ready(struct wlr_ext_image_copy_capture_frame_v1 *frame, + enum wl_output_transform transform, const struct timespec *presentation_time); +/** + * Notify the client that the frame has failed. + * + * This function destroys the frame. + */ +void wlr_ext_image_copy_capture_frame_v1_fail(struct wlr_ext_image_copy_capture_frame_v1 *frame, + enum ext_image_copy_capture_frame_v1_failure_reason reason); + +#endif diff --git a/protocol/meson.build b/protocol/meson.build index 8afa4057a..e987d4ea3 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -30,6 +30,7 @@ protocols = { 'ext-foreign-toplevel-list-v1': wl_protocol_dir / 'staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml', 'ext-idle-notify-v1': wl_protocol_dir / 'staging/ext-idle-notify/ext-idle-notify-v1.xml', 'ext-image-capture-source-v1': wl_protocol_dir / 'staging/ext-image-capture-source/ext-image-capture-source-v1.xml', + 'ext-image-copy-capture-v1': wl_protocol_dir / 'staging/ext-image-copy-capture/ext-image-copy-capture-v1.xml', 'ext-session-lock-v1': wl_protocol_dir / 'staging/ext-session-lock/ext-session-lock-v1.xml', 'fractional-scale-v1': wl_protocol_dir / 'staging/fractional-scale/fractional-scale-v1.xml', 'linux-drm-syncobj-v1': wl_protocol_dir / 'staging/linux-drm-syncobj/linux-drm-syncobj-v1.xml', diff --git a/types/meson.build b/types/meson.build index 39a2ac793..4858c3135 100644 --- a/types/meson.build +++ b/types/meson.build @@ -44,6 +44,7 @@ wlr_files += files( 'wlr_drm.c', 'wlr_export_dmabuf_v1.c', 'wlr_foreign_toplevel_management_v1.c', + 'wlr_ext_image_copy_capture_v1.c', 'wlr_ext_foreign_toplevel_list_v1.c', 'wlr_fractional_scale_v1.c', 'wlr_fullscreen_shell_v1.c', diff --git a/types/wlr_ext_image_copy_capture_v1.c b/types/wlr_ext_image_copy_capture_v1.c new file mode 100644 index 000000000..4032aee76 --- /dev/null +++ b/types/wlr_ext_image_copy_capture_v1.c @@ -0,0 +1,591 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "render/pixel_format.h" + +#define IMAGE_COPY_CAPTURE_MANAGER_V1_VERSION 1 + +struct wlr_ext_image_copy_capture_session_v1 { + struct wl_resource *resource; + struct wlr_ext_image_capture_source_v1 *source; + struct wlr_ext_image_copy_capture_frame_v1 *frame; + + struct wl_listener source_destroy; + struct wl_listener source_constraints_update; + struct wl_listener source_frame; + + pixman_region32_t damage; +}; + +struct wlr_ext_image_copy_capture_cursor_session_v1 { + struct wl_resource *resource; + struct wlr_ext_image_capture_source_v1_cursor *source; + bool capture_session_created; + + struct { + bool entered; + int32_t x, y; + struct { + int32_t x, y; + } hotspot; + } prev; + + struct wl_listener source_destroy; + struct wl_listener source_update; +}; + +static const struct ext_image_copy_capture_frame_v1_interface frame_impl; +static const struct ext_image_copy_capture_session_v1_interface session_impl; +static const struct ext_image_copy_capture_cursor_session_v1_interface cursor_session_impl; + +static struct wlr_ext_image_copy_capture_frame_v1 *frame_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &ext_image_copy_capture_frame_v1_interface, &frame_impl)); + return wl_resource_get_user_data(resource); +} + +static struct wlr_ext_image_copy_capture_session_v1 *session_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &ext_image_copy_capture_session_v1_interface, &session_impl)); + return wl_resource_get_user_data(resource); +} + +static struct wlr_ext_image_copy_capture_cursor_session_v1 *cursor_session_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &ext_image_copy_capture_cursor_session_v1_interface, &cursor_session_impl)); + return wl_resource_get_user_data(resource); +} + +static void frame_destroy(struct wlr_ext_image_copy_capture_frame_v1 *frame) { + if (frame == NULL) { + return; + } + wl_signal_emit_mutable(&frame->events.destroy, NULL); + assert(wl_list_empty(&frame->events.destroy.listener_list)); + wl_resource_set_user_data(frame->resource, NULL); + wlr_buffer_unlock(frame->buffer); + pixman_region32_fini(&frame->buffer_damage); + if (frame->session->frame == frame) { + frame->session->frame = NULL; + } + free(frame); +} + +static void frame_handle_resource_destroy(struct wl_resource *resource) { + frame_destroy(wl_resource_get_user_data(resource)); +} + +void wlr_ext_image_copy_capture_frame_v1_ready(struct wlr_ext_image_copy_capture_frame_v1 *frame, + enum wl_output_transform transform, + const struct timespec *presentation_time) { + assert(frame->capturing); + + int rects_len = 0; + const pixman_box32_t *rects = + pixman_region32_rectangles(&frame->session->damage, &rects_len); + for (int i = 0; i < rects_len; i++) { + const pixman_box32_t *rect = &rects[i]; + ext_image_copy_capture_frame_v1_send_damage(frame->resource, + rect->x1, rect->y1, rect->x2 - rect->x1, rect->y2 - rect->y1); + } + + pixman_region32_clear(&frame->session->damage); + + uint64_t pres_time_sec = (uint64_t)presentation_time->tv_sec; + ext_image_copy_capture_frame_v1_send_transform(frame->resource, transform); + ext_image_copy_capture_frame_v1_send_presentation_time(frame->resource, + pres_time_sec >> 32, (uint32_t)pres_time_sec, presentation_time->tv_nsec); + ext_image_copy_capture_frame_v1_send_ready(frame->resource); + frame_destroy(frame); +} + +void wlr_ext_image_copy_capture_frame_v1_fail(struct wlr_ext_image_copy_capture_frame_v1 *frame, + enum ext_image_copy_capture_frame_v1_failure_reason reason) { + ext_image_copy_capture_frame_v1_send_failed(frame->resource, reason); + frame_destroy(frame); +} + +static void frame_handle_destroy(struct wl_client *client, + struct wl_resource *frame_resource) { + wl_resource_destroy(frame_resource); +} + +static void frame_handle_attach_buffer(struct wl_client *client, + struct wl_resource *frame_resource, struct wl_resource *buffer_resource) { + struct wlr_ext_image_copy_capture_frame_v1 *frame = frame_from_resource(frame_resource); + if (frame == NULL) { + return; + } + + if (frame->capturing) { + wl_resource_post_error(frame->resource, + EXT_IMAGE_COPY_CAPTURE_FRAME_V1_ERROR_ALREADY_CAPTURED, + "attach_buffer sent after capture"); + return; + } + + struct wlr_buffer *buffer = wlr_buffer_try_from_resource(buffer_resource); + if (buffer == NULL) { + wl_resource_post_no_memory(frame_resource); + return; + } + + wlr_buffer_unlock(frame->buffer); + frame->buffer = buffer; +} + +static void frame_handle_damage_buffer(struct wl_client *client, + struct wl_resource *frame_resource, int32_t x, int32_t y, + int32_t width, int32_t height) { + struct wlr_ext_image_copy_capture_frame_v1 *frame = frame_from_resource(frame_resource); + if (frame == NULL) { + return; + } + + if (frame->capturing) { + wl_resource_post_error(frame->resource, + EXT_IMAGE_COPY_CAPTURE_FRAME_V1_ERROR_ALREADY_CAPTURED, + "damage_buffer sent after capture"); + return; + } + + if (x < 0 || y < 0 || width <= 0 || height <= 0) { + wl_resource_post_error(frame->resource, + EXT_IMAGE_COPY_CAPTURE_FRAME_V1_ERROR_INVALID_BUFFER_DAMAGE, + "Invalid buffer damage coordinates"); + return; + } + + pixman_region32_union_rect(&frame->buffer_damage, &frame->buffer_damage, + x, y, width, height); +} + +static void frame_handle_capture(struct wl_client *client, + struct wl_resource *frame_resource) { + struct wlr_ext_image_copy_capture_frame_v1 *frame = frame_from_resource(frame_resource); + if (frame == NULL) { + return; + } + + if (frame->capturing) { + wl_resource_post_error(frame->resource, + EXT_IMAGE_COPY_CAPTURE_FRAME_V1_ERROR_ALREADY_CAPTURED, + "capture sent twice"); + return; + } + + if (frame->buffer == NULL) { + wl_resource_post_error(frame->resource, + EXT_IMAGE_COPY_CAPTURE_FRAME_V1_ERROR_NO_BUFFER, + "No buffer attached"); + return; + } + + frame->capturing = true; + + bool need_frame = pixman_region32_not_empty(&frame->session->damage); + struct wlr_ext_image_capture_source_v1 *source = frame->session->source; + if (need_frame && source->impl->schedule_frame) { + source->impl->schedule_frame(source); + } +} + +static const struct ext_image_copy_capture_frame_v1_interface frame_impl = { + .destroy = frame_handle_destroy, + .attach_buffer = frame_handle_attach_buffer, + .damage_buffer = frame_handle_damage_buffer, + .capture = frame_handle_capture, +}; + +static void session_handle_destroy(struct wl_client *client, + struct wl_resource *session_resource) { + wl_resource_destroy(session_resource); +} + +static void session_handle_create_frame(struct wl_client *client, + struct wl_resource *session_resource, uint32_t new_id) { + struct wlr_ext_image_copy_capture_session_v1 *session = session_from_resource(session_resource); + + if (session != NULL && session->frame != NULL) { + wl_resource_post_error(session_resource, + EXT_IMAGE_COPY_CAPTURE_SESSION_V1_ERROR_DUPLICATE_FRAME, + "session already has a frame object"); + return; + } + + uint32_t version = wl_resource_get_version(session_resource); + struct wl_resource *frame_resource = wl_resource_create(client, + &ext_image_copy_capture_frame_v1_interface, version, new_id); + if (frame_resource == NULL) { + wl_resource_post_no_memory(frame_resource); + return; + } + wl_resource_set_implementation(frame_resource, &frame_impl, NULL, + frame_handle_resource_destroy); + + if (session == NULL) { + ext_image_copy_capture_frame_v1_send_failed(frame_resource, + EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_STOPPED); + return; + } + + struct wlr_ext_image_copy_capture_frame_v1 *frame = calloc(1, sizeof(*frame)); + if (frame == NULL) { + wl_resource_post_no_memory(session_resource); + return; + } + + frame->resource = frame_resource; + frame->session = session; + pixman_region32_init(&frame->buffer_damage); + wl_signal_init(&frame->events.destroy); + + wl_resource_set_user_data(frame_resource, frame); + + session->frame = frame; +} + +static const struct ext_image_copy_capture_session_v1_interface session_impl = { + .destroy = session_handle_destroy, + .create_frame = session_handle_create_frame, +}; + +static void session_send_constraints(struct wlr_ext_image_copy_capture_session_v1 *session) { + struct wlr_ext_image_capture_source_v1 *source = session->source; + + ext_image_copy_capture_session_v1_send_buffer_size(session->resource, + source->width, source->height); + + for (size_t i = 0; i < source->shm_formats_len; i++) { + ext_image_copy_capture_session_v1_send_shm_format(session->resource, + convert_drm_format_to_wl_shm(source->shm_formats[i])); + } + + if (source->dmabuf_formats.len > 0) { + struct wl_array dev_id_array = { + .data = &source->dmabuf_device, + .size = sizeof(source->dmabuf_device), + }; + ext_image_copy_capture_session_v1_send_dmabuf_device(session->resource, + &dev_id_array); + } + for (size_t i = 0; i < source->dmabuf_formats.len; i++) { + struct wlr_drm_format *fmt = &source->dmabuf_formats.formats[i]; + struct wl_array modifiers_array = { + .data = fmt->modifiers, + .size = fmt->len * sizeof(fmt->modifiers[0]), + }; + ext_image_copy_capture_session_v1_send_dmabuf_format(session->resource, + fmt->format, &modifiers_array); + } + + ext_image_copy_capture_session_v1_send_done(session->resource); +} + +static void session_destroy(struct wlr_ext_image_copy_capture_session_v1 *session) { + if (session == NULL) { + return; + } + + if (session->frame != NULL) { + wlr_ext_image_copy_capture_frame_v1_fail(session->frame, + EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_STOPPED); + } + + ext_image_copy_capture_session_v1_send_stopped(session->resource); + wl_resource_set_user_data(session->resource, NULL); + + pixman_region32_fini(&session->damage); + wl_list_remove(&session->source_destroy.link); + wl_list_remove(&session->source_constraints_update.link); + wl_list_remove(&session->source_frame.link); + free(session); +} + +static void session_handle_source_destroy(struct wl_listener *listener, void *data) { + struct wlr_ext_image_copy_capture_session_v1 *session = wl_container_of(listener, session, source_destroy); + session_destroy(session); +} + +static void session_handle_source_constraints_update(struct wl_listener *listener, + void *data) { + struct wlr_ext_image_copy_capture_session_v1 *session = + wl_container_of(listener, session, source_constraints_update); + session_send_constraints(session); +} + +static void session_handle_source_frame(struct wl_listener *listener, void *data) { + struct wlr_ext_image_copy_capture_session_v1 *session = wl_container_of(listener, session, source_frame); + struct wlr_ext_image_capture_source_v1_frame_event *event = data; + + pixman_region32_union(&session->damage, &session->damage, event->damage); + + struct wlr_ext_image_copy_capture_frame_v1 *frame = session->frame; + if (frame != NULL && frame->capturing && + pixman_region32_not_empty(&session->damage)) { + pixman_region32_union(&frame->buffer_damage, + &frame->buffer_damage, &session->damage); + + struct wlr_ext_image_capture_source_v1 *source = frame->session->source; + source->impl->copy_frame(source, frame, event); + } +} + +static void session_handle_resource_destroy(struct wl_resource *resource) { + session_destroy(session_from_resource(resource)); +} + +static void session_create(struct wl_resource *parent_resource, uint32_t new_id, + struct wlr_ext_image_capture_source_v1 *source, uint32_t options) { + struct wl_client *client = wl_resource_get_client(parent_resource); + uint32_t version = wl_resource_get_version(parent_resource); + struct wl_resource *session_resource = wl_resource_create(client, + &ext_image_copy_capture_session_v1_interface, version, new_id); + if (session_resource == NULL) { + wl_resource_post_no_memory(parent_resource); + return; + } + wl_resource_set_implementation(session_resource, &session_impl, NULL, + session_handle_resource_destroy); + + if (source == NULL) { + ext_image_copy_capture_session_v1_send_stopped(session_resource); + return; + } + + struct wlr_ext_image_copy_capture_session_v1 *session = calloc(1, sizeof(*session)); + if (session == NULL) { + wl_resource_post_no_memory(parent_resource); + return; + } + + session->resource = session_resource; + session->source = source; + pixman_region32_init_rect(&session->damage, 0, 0, source->width, + source->height); + + session->source_destroy.notify = session_handle_source_destroy; + wl_signal_add(&source->events.destroy, &session->source_destroy); + + session->source_constraints_update.notify = session_handle_source_constraints_update; + wl_signal_add(&source->events.constraints_update, &session->source_constraints_update); + + session->source_frame.notify = session_handle_source_frame; + wl_signal_add(&source->events.frame, &session->source_frame); + + wl_resource_set_user_data(session_resource, session); + session_send_constraints(session); +} + +static void cursor_session_destroy(struct wlr_ext_image_copy_capture_cursor_session_v1 *cursor_session) { + if (cursor_session == NULL) { + return; + } + if (cursor_session->source->entered) { + ext_image_copy_capture_cursor_session_v1_send_leave(cursor_session->resource); + } + wl_resource_set_user_data(cursor_session->resource, NULL); + wl_list_remove(&cursor_session->source_destroy.link); + wl_list_remove(&cursor_session->source_update.link); + free(cursor_session); +} + +static void cursor_session_handle_destroy(struct wl_client *client, + struct wl_resource *cursor_session_resource) { + wl_resource_destroy(cursor_session_resource); +} + +static void cursor_session_handle_get_capture_session(struct wl_client *client, + struct wl_resource *cursor_session_resource, uint32_t new_id) { + struct wlr_ext_image_copy_capture_cursor_session_v1 *cursor_session = + cursor_session_from_resource(cursor_session_resource); + + if (cursor_session != NULL && cursor_session->capture_session_created) { + wl_resource_post_error(cursor_session_resource, + EXT_IMAGE_COPY_CAPTURE_CURSOR_SESSION_V1_ERROR_DUPLICATE_SESSION, + "get_capture_session sent twice"); + return; + } + + cursor_session->capture_session_created = true; + + struct wlr_ext_image_capture_source_v1 *source = NULL; + if (cursor_session != NULL) { + source = &cursor_session->source->base; + } + + session_create(cursor_session_resource, new_id, source, 0); +} + +static const struct ext_image_copy_capture_cursor_session_v1_interface cursor_session_impl = { + .destroy = cursor_session_handle_destroy, + .get_capture_session = cursor_session_handle_get_capture_session, +}; + +static void cursor_session_handle_resource_destroy(struct wl_resource *resource) { + struct wlr_ext_image_copy_capture_cursor_session_v1 *cursor_session = + cursor_session_from_resource(resource); + cursor_session_destroy(cursor_session); +} + +static void cursor_session_handle_source_destroy(struct wl_listener *listener, + void *data) { + struct wlr_ext_image_copy_capture_cursor_session_v1 *cursor_session = + wl_container_of(listener, cursor_session, source_destroy); + cursor_session_destroy(cursor_session); +} + +static void cursor_session_update( + struct wlr_ext_image_copy_capture_cursor_session_v1 *cursor_session) { + struct wlr_ext_image_capture_source_v1_cursor *cursor_source = cursor_session->source; + + if (cursor_source->entered && !cursor_session->prev.entered) { + ext_image_copy_capture_cursor_session_v1_send_enter(cursor_session->resource); + } + if (!cursor_source->entered && cursor_session->prev.entered) { + ext_image_copy_capture_cursor_session_v1_send_leave(cursor_session->resource); + } + + if (cursor_source->x != cursor_session->prev.x || + cursor_source->y != cursor_session->prev.y) { + ext_image_copy_capture_cursor_session_v1_send_position(cursor_session->resource, + cursor_source->x, cursor_source->y); + } + + if (cursor_source->hotspot.x != cursor_session->prev.hotspot.x || + cursor_source->hotspot.y != cursor_session->prev.hotspot.y) { + ext_image_copy_capture_cursor_session_v1_send_hotspot(cursor_session->resource, + cursor_source->hotspot.x, cursor_source->hotspot.y); + } + + cursor_session->prev.entered = cursor_source->entered; + cursor_session->prev.x = cursor_source->x; + cursor_session->prev.y = cursor_source->y; + cursor_session->prev.hotspot.y = cursor_source->hotspot.y; + cursor_session->prev.hotspot.y = cursor_source->hotspot.y; +} + +static void cursor_session_handle_source_update(struct wl_listener *listener, + void *data) { + struct wlr_ext_image_copy_capture_cursor_session_v1 *cursor_session = + wl_container_of(listener, cursor_session, source_update); + cursor_session_update(cursor_session); +} + +static void manager_handle_create_session(struct wl_client *client, + struct wl_resource *manager_resource, uint32_t new_id, + struct wl_resource *source_resource, uint32_t options) { + struct wlr_ext_image_capture_source_v1 *source = + wlr_ext_image_capture_source_v1_from_resource(source_resource); + session_create(manager_resource, new_id, source, options); +} + +static void manager_handle_create_pointer_cursor_session(struct wl_client *client, + struct wl_resource *manager_resource, uint32_t new_id, + struct wl_resource *source_resource, struct wl_resource *pointer_resource) { + struct wlr_ext_image_capture_source_v1 *source = wlr_ext_image_capture_source_v1_from_resource(source_resource); + struct wlr_seat_client *seat_client = wlr_seat_client_from_pointer_resource(pointer_resource); + + struct wlr_seat *seat = NULL; + if (seat_client != NULL) { + seat = seat_client->seat; + } + + struct wlr_ext_image_capture_source_v1_cursor *source_cursor = NULL; + if (source != NULL && seat != NULL && source->impl->get_pointer_cursor) { + source_cursor = source->impl->get_pointer_cursor(source, seat); + } + + uint32_t version = wl_resource_get_version(manager_resource); + struct wl_resource *cursor_session_resource = wl_resource_create(client, + &ext_image_copy_capture_cursor_session_v1_interface, version, new_id); + if (cursor_session_resource == NULL) { + wl_resource_post_no_memory(manager_resource); + return; + } + wl_resource_set_implementation(cursor_session_resource, &cursor_session_impl, NULL, + cursor_session_handle_resource_destroy); + + if (source_cursor == NULL) { + return; // leave inert + } + + struct wlr_ext_image_copy_capture_cursor_session_v1 *cursor_session = calloc(1, sizeof(*cursor_session)); + if (cursor_session == NULL) { + wl_resource_post_no_memory(manager_resource); + return; + } + + cursor_session->resource = cursor_session_resource; + cursor_session->source = source_cursor; + + cursor_session->source_destroy.notify = cursor_session_handle_source_destroy; + wl_signal_add(&source_cursor->base.events.destroy, &cursor_session->source_destroy); + + cursor_session->source_update.notify = cursor_session_handle_source_update; + wl_signal_add(&source_cursor->events.update, &cursor_session->source_update); + + wl_resource_set_user_data(cursor_session_resource, cursor_session); + + cursor_session_update(cursor_session); +} + +static void manager_handle_destroy(struct wl_client *client, + struct wl_resource *manager_resource) { + wl_resource_destroy(manager_resource); +} + +static const struct ext_image_copy_capture_manager_v1_interface manager_impl = { + .create_session = manager_handle_create_session, + .create_pointer_cursor_session = manager_handle_create_pointer_cursor_session, + .destroy = manager_handle_destroy, +}; + +static void manager_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id) { + struct wl_resource *resource = wl_resource_create(client, + &ext_image_copy_capture_manager_v1_interface, version, id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &manager_impl, NULL, NULL); +} + +static void manager_handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_ext_image_copy_capture_manager_v1 *manager = + wl_container_of(listener, manager, display_destroy); + wl_list_remove(&manager->display_destroy.link); + wl_global_destroy(manager->global); + free(manager); +} + +struct wlr_ext_image_copy_capture_manager_v1 *wlr_ext_image_copy_capture_manager_v1_create( + struct wl_display *display, uint32_t version) { + assert(version <= IMAGE_COPY_CAPTURE_MANAGER_V1_VERSION); + + struct wlr_ext_image_copy_capture_manager_v1 *manager = calloc(1, sizeof(*manager)); + if (manager == NULL) { + return NULL; + } + + manager->global = wl_global_create(display, + &ext_image_copy_capture_manager_v1_interface, version, manager, manager_bind); + if (manager->global == NULL) { + free(manager); + return NULL; + } + + manager->display_destroy.notify = manager_handle_display_destroy; + wl_display_add_destroy_listener(display, &manager->display_destroy); + + return manager; +} From c24efad6dfb49d18baeb5d9e6c08092d4ca9e636 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 18 Apr 2024 18:20:29 +0200 Subject: [PATCH 247/519] ext-image-copy-capture-v1: add wlr_ext_image_copy_capture_frame_v1_copy_buffer() --- .../wlr/types/wlr_ext_image_copy_capture_v1.h | 5 ++ types/wlr_ext_image_copy_capture_v1.c | 89 +++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/include/wlr/types/wlr_ext_image_copy_capture_v1.h b/include/wlr/types/wlr_ext_image_copy_capture_v1.h index d53405de9..897005710 100644 --- a/include/wlr/types/wlr_ext_image_copy_capture_v1.h +++ b/include/wlr/types/wlr_ext_image_copy_capture_v1.h @@ -53,5 +53,10 @@ void wlr_ext_image_copy_capture_frame_v1_ready(struct wlr_ext_image_copy_capture */ void wlr_ext_image_copy_capture_frame_v1_fail(struct wlr_ext_image_copy_capture_frame_v1 *frame, enum ext_image_copy_capture_frame_v1_failure_reason reason); +/** + * Copy a struct wlr_buffer into the client-provided buffer for the frame. + */ +bool wlr_ext_image_copy_capture_frame_v1_copy_buffer(struct wlr_ext_image_copy_capture_frame_v1 *frame, + struct wlr_buffer *src, struct wlr_renderer *renderer); #endif diff --git a/types/wlr_ext_image_copy_capture_v1.c b/types/wlr_ext_image_copy_capture_v1.c index 4032aee76..a9d78a10d 100644 --- a/types/wlr_ext_image_copy_capture_v1.c +++ b/types/wlr_ext_image_copy_capture_v1.c @@ -107,6 +107,95 @@ void wlr_ext_image_copy_capture_frame_v1_ready(struct wlr_ext_image_copy_capture frame_destroy(frame); } +static bool copy_dmabuf(struct wlr_buffer *dst, + struct wlr_buffer *src, struct wlr_renderer *renderer, + const pixman_region32_t *clip) { + struct wlr_texture *texture = wlr_texture_from_buffer(renderer, src); + if (texture == NULL) { + return false; + } + + bool ok = false; + struct wlr_render_pass *pass = wlr_renderer_begin_buffer_pass(renderer, dst, NULL); + if (!pass) { + goto out; + } + + wlr_render_pass_add_texture(pass, &(struct wlr_render_texture_options) { + .texture = texture, + .clip = clip, + .blend_mode = WLR_RENDER_BLEND_MODE_NONE, + }); + + ok = wlr_render_pass_submit(pass); + +out: + wlr_texture_destroy(texture); + return ok; +} + +static bool copy_shm(void *data, uint32_t format, size_t stride, + struct wlr_buffer *src, struct wlr_renderer *renderer) { + // TODO: bypass renderer if source buffer supports data ptr access + struct wlr_texture *texture = wlr_texture_from_buffer(renderer, src); + if (!texture) { + return false; + } + + // TODO: only copy damaged region + bool ok = wlr_texture_read_pixels(texture, &(struct wlr_texture_read_pixels_options){ + .data = data, + .format = format, + .stride = stride, + }); + + wlr_texture_destroy(texture); + + return ok; +} + +bool wlr_ext_image_copy_capture_frame_v1_copy_buffer(struct wlr_ext_image_copy_capture_frame_v1 *frame, + struct wlr_buffer *src, struct wlr_renderer *renderer) { + struct wlr_buffer *dst = frame->buffer; + + if (src->width != dst->width || src->height != dst->height) { + wlr_ext_image_copy_capture_frame_v1_fail(frame, + EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_BUFFER_CONSTRAINTS); + return false; + } + + bool ok = false; + enum ext_image_copy_capture_frame_v1_failure_reason failure_reason = + EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_UNKNOWN; + struct wlr_dmabuf_attributes dmabuf; + void *data; + uint32_t format; + size_t stride; + if (wlr_buffer_get_dmabuf(dst, &dmabuf)) { + if (frame->session->source->dmabuf_formats.len == 0) { + ok = false; + failure_reason = EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_BUFFER_CONSTRAINTS; + } else { + ok = copy_dmabuf(dst, src, renderer, &frame->buffer_damage); + } + } else if (wlr_buffer_begin_data_ptr_access(dst, + WLR_BUFFER_DATA_PTR_ACCESS_WRITE, &data, &format, &stride)) { + if (frame->session->source->shm_formats_len == 0) { + ok = false; + failure_reason = EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_BUFFER_CONSTRAINTS; + } else { + ok = copy_shm(data, format, stride, src, renderer); + } + wlr_buffer_end_data_ptr_access(dst); + } + if (!ok) { + wlr_ext_image_copy_capture_frame_v1_fail(frame, failure_reason); + return false; + } + + return true; +} + void wlr_ext_image_copy_capture_frame_v1_fail(struct wlr_ext_image_copy_capture_frame_v1 *frame, enum ext_image_copy_capture_frame_v1_failure_reason reason) { ext_image_copy_capture_frame_v1_send_failed(frame->resource, reason); From 855b3fd607bef53e0681a9f48b5ebf6a53f80dd1 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 18 Apr 2024 18:39:28 +0200 Subject: [PATCH 248/519] ext-image-capture-source-v1: add wlr_ext_image_capture_source_v1_set_constraints_from_swapchain() --- .../wlr_ext_image_capture_source_v1.h | 5 ++ types/ext_image_capture_source_v1/base.c | 70 +++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/include/wlr/interfaces/wlr_ext_image_capture_source_v1.h b/include/wlr/interfaces/wlr_ext_image_capture_source_v1.h index 49737ac3b..c9b7857a7 100644 --- a/include/wlr/interfaces/wlr_ext_image_capture_source_v1.h +++ b/include/wlr/interfaces/wlr_ext_image_capture_source_v1.h @@ -13,6 +13,8 @@ #include struct wlr_ext_image_copy_capture_frame_v1; +struct wlr_swapchain; +struct wlr_renderer; struct wlr_seat; struct wlr_ext_image_capture_source_v1_interface { @@ -29,6 +31,9 @@ void wlr_ext_image_capture_source_v1_init(struct wlr_ext_image_capture_source_v1 void wlr_ext_image_capture_source_v1_finish(struct wlr_ext_image_capture_source_v1 *source); bool wlr_ext_image_capture_source_v1_create_resource(struct wlr_ext_image_capture_source_v1 *source, struct wl_client *client, uint32_t new_id); +bool wlr_ext_image_capture_source_v1_set_constraints_from_swapchain( + struct wlr_ext_image_capture_source_v1 *source, + struct wlr_swapchain *swapchain, struct wlr_renderer *renderer); void wlr_ext_image_capture_source_v1_cursor_init(struct wlr_ext_image_capture_source_v1_cursor *source_cursor, const struct wlr_ext_image_capture_source_v1_interface *impl); diff --git a/types/ext_image_capture_source_v1/base.c b/types/ext_image_capture_source_v1/base.c index af68beac5..0fc910864 100644 --- a/types/ext_image_capture_source_v1/base.c +++ b/types/ext_image_capture_source_v1/base.c @@ -1,7 +1,14 @@ #include +#include #include +#include #include +#include +#include +#include +#include #include +#include #include "ext-image-capture-source-v1-protocol.h" static void source_handle_destroy(struct wl_client *client, @@ -70,6 +77,69 @@ bool wlr_ext_image_capture_source_v1_create_resource(struct wlr_ext_image_captur return true; } +static uint32_t get_swapchain_shm_format(struct wlr_swapchain *swapchain, + struct wlr_renderer *renderer) { + struct wlr_buffer *buffer = wlr_swapchain_acquire(swapchain); + if (buffer == NULL) { + return DRM_FORMAT_INVALID; + } + + struct wlr_texture *texture = wlr_texture_from_buffer(renderer, buffer); + wlr_buffer_unlock(buffer); + if (texture == NULL) { + return DRM_FORMAT_INVALID; + } + + uint32_t format = wlr_texture_preferred_read_format(texture); + wlr_texture_destroy(texture); + + return format; +} + +bool wlr_ext_image_capture_source_v1_set_constraints_from_swapchain(struct wlr_ext_image_capture_source_v1 *source, + struct wlr_swapchain *swapchain, struct wlr_renderer *renderer) { + source->width = swapchain->width; + source->height = swapchain->height; + + uint32_t shm_format = get_swapchain_shm_format(swapchain, renderer); + if (shm_format != DRM_FORMAT_INVALID) { + uint32_t *shm_formats = calloc(1, sizeof(shm_formats[0])); + if (shm_formats == NULL) { + wlr_log(WLR_ERROR, "Allocation failed"); + return false; + } + shm_formats[0] = shm_format; + + source->shm_formats_len = 1; + free(source->shm_formats); + source->shm_formats = shm_formats; + } + + int drm_fd = wlr_renderer_get_drm_fd(renderer); + if (swapchain->allocator != NULL && + (swapchain->allocator->buffer_caps & WLR_BUFFER_CAP_DMABUF) && + drm_fd >= 0) { + struct stat dev_stat; + if (fstat(drm_fd, &dev_stat) != 0) { + wlr_log_errno(WLR_ERROR, "fstat() failed"); + return false; + } + + source->dmabuf_device = dev_stat.st_rdev; + + wlr_drm_format_set_finish(&source->dmabuf_formats); + source->dmabuf_formats = (struct wlr_drm_format_set){0}; + + for (size_t i = 0; i < swapchain->format.len; i++) { + wlr_drm_format_set_add(&source->dmabuf_formats, + swapchain->format.format, swapchain->format.modifiers[i]); + } + } + + wl_signal_emit_mutable(&source->events.constraints_update, NULL); + return true; +} + void wlr_ext_image_capture_source_v1_cursor_init(struct wlr_ext_image_capture_source_v1_cursor *source_cursor, const struct wlr_ext_image_capture_source_v1_interface *impl) { *source_cursor = (struct wlr_ext_image_capture_source_v1_cursor){0}; From 08e14deeca86fc529a5d58db11de7f62550e72b4 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 12 Feb 2024 20:16:28 +0100 Subject: [PATCH 249/519] ext-image-capture-source-v1: add output source --- .../types/wlr_ext_image_capture_source_v1.h | 14 ++ types/ext_image_capture_source_v1/output.c | 198 ++++++++++++++++++ types/meson.build | 1 + 3 files changed, 213 insertions(+) create mode 100644 types/ext_image_capture_source_v1/output.c diff --git a/include/wlr/types/wlr_ext_image_capture_source_v1.h b/include/wlr/types/wlr_ext_image_capture_source_v1.h index 4247dd29a..8d80a9e3c 100644 --- a/include/wlr/types/wlr_ext_image_capture_source_v1.h +++ b/include/wlr/types/wlr_ext_image_capture_source_v1.h @@ -68,6 +68,17 @@ struct wlr_ext_image_capture_source_v1_cursor { } events; }; +/** + * Interface exposing one screen capture source per output. + */ +struct wlr_ext_output_image_capture_source_manager_v1 { + struct wl_global *global; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; +}; + /** * Obtain a struct wlr_ext_image_capture_source_v1 from an ext_image_capture_source_v1 * resource. @@ -77,4 +88,7 @@ struct wlr_ext_image_capture_source_v1_cursor { */ struct wlr_ext_image_capture_source_v1 *wlr_ext_image_capture_source_v1_from_resource(struct wl_resource *resource); +struct wlr_ext_output_image_capture_source_manager_v1 *wlr_ext_output_image_capture_source_manager_v1_create( + struct wl_display *display, uint32_t version); + #endif diff --git a/types/ext_image_capture_source_v1/output.c b/types/ext_image_capture_source_v1/output.c new file mode 100644 index 000000000..f547ae31e --- /dev/null +++ b/types/ext_image_capture_source_v1/output.c @@ -0,0 +1,198 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "render/wlr_renderer.h" +#include "ext-image-capture-source-v1-protocol.h" + +#define OUTPUT_IMAGE_SOURCE_MANAGER_V1_VERSION 1 + +struct wlr_ext_output_image_capture_source_v1 { + struct wlr_ext_image_capture_source_v1 base; + struct wlr_addon addon; + + struct wlr_output *output; + + struct wl_listener output_commit; +}; + +struct wlr_ext_output_image_capture_source_v1_frame_event { + struct wlr_ext_image_capture_source_v1_frame_event base; + struct wlr_buffer *buffer; + struct timespec *when; +}; + +static void output_source_schedule_frame(struct wlr_ext_image_capture_source_v1 *base) { + struct wlr_ext_output_image_capture_source_v1 *source = wl_container_of(base, source, base); + wlr_output_update_needs_frame(source->output); +} + +static void output_source_copy_frame(struct wlr_ext_image_capture_source_v1 *base, + struct wlr_ext_image_copy_capture_frame_v1 *frame, + struct wlr_ext_image_capture_source_v1_frame_event *base_event) { + struct wlr_ext_output_image_capture_source_v1 *source = wl_container_of(base, source, base); + struct wlr_ext_output_image_capture_source_v1_frame_event *event = + wl_container_of(base_event, event, base); + + if (wlr_ext_image_copy_capture_frame_v1_copy_buffer(frame, + event->buffer, source->output->renderer)) { + wlr_ext_image_copy_capture_frame_v1_ready(frame, + source->output->transform, event->when); + } +} + +static const struct wlr_ext_image_capture_source_v1_interface output_source_impl = { + .schedule_frame = output_source_schedule_frame, + .copy_frame = output_source_copy_frame, +}; + +static void source_update_buffer_constraints(struct wlr_ext_output_image_capture_source_v1 *source) { + struct wlr_output *output = source->output; + + if (!wlr_output_configure_primary_swapchain(output, NULL, &output->swapchain)) { + return; + } + + wlr_ext_image_capture_source_v1_set_constraints_from_swapchain(&source->base, + output->swapchain, output->renderer); +} + +static void source_handle_output_commit(struct wl_listener *listener, + void *data) { + struct wlr_ext_output_image_capture_source_v1 *source = wl_container_of(listener, source, output_commit); + struct wlr_output_event_commit *event = data; + + if (event->state->committed & (WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_RENDER_FORMAT)) { + source_update_buffer_constraints(source); + } + + if (event->state->committed & WLR_OUTPUT_STATE_BUFFER) { + struct wlr_buffer *buffer = event->state->buffer; + + pixman_region32_t full_damage; + pixman_region32_init_rect(&full_damage, 0, 0, buffer->width, buffer->height); + + const pixman_region32_t *damage; + if (event->state->committed & WLR_OUTPUT_STATE_DAMAGE) { + damage = &event->state->damage; + } else { + damage = &full_damage; + } + + struct wlr_ext_output_image_capture_source_v1_frame_event frame_event = { + .base = { + .damage = damage, + }, + .buffer = buffer, + .when = event->when, // TODO: predict next presentation time instead + }; + wl_signal_emit_mutable(&source->base.events.frame, &frame_event); + + pixman_region32_fini(&full_damage); + } +} + +static void output_addon_destroy(struct wlr_addon *addon) { + struct wlr_ext_output_image_capture_source_v1 *source = wl_container_of(addon, source, addon); + wlr_ext_image_capture_source_v1_finish(&source->base); + wl_list_remove(&source->output_commit.link); + wlr_addon_finish(&source->addon); + free(source); +} + +static const struct wlr_addon_interface output_addon_impl = { + .name = "wlr_ext_output_image_capture_source_v1", + .destroy = output_addon_destroy, +}; + +static void output_manager_handle_create_source(struct wl_client *client, + struct wl_resource *manager_resource, uint32_t new_id, + struct wl_resource *output_resource) { + struct wlr_output *output = wlr_output_from_resource(output_resource); + if (output == NULL) { + wlr_ext_image_capture_source_v1_create_resource(NULL, client, new_id); + return; + } + + struct wlr_ext_output_image_capture_source_v1 *source; + struct wlr_addon *addon = wlr_addon_find(&output->addons, NULL, &output_addon_impl); + if (addon != NULL) { + source = wl_container_of(addon, source, addon); + } else { + source = calloc(1, sizeof(*source)); + if (source == NULL) { + wl_resource_post_no_memory(manager_resource); + return; + } + + wlr_ext_image_capture_source_v1_init(&source->base, &output_source_impl); + wlr_addon_init(&source->addon, &output->addons, NULL, &output_addon_impl); + source->output = output; + + source->output_commit.notify = source_handle_output_commit; + wl_signal_add(&output->events.commit, &source->output_commit); + + source_update_buffer_constraints(source); + } + + if (!wlr_ext_image_capture_source_v1_create_resource(&source->base, client, new_id)) { + return; + } +} + +static void output_manager_handle_destroy(struct wl_client *client, + struct wl_resource *manager_resource) { + wl_resource_destroy(manager_resource); +} + +static const struct ext_output_image_capture_source_manager_v1_interface output_manager_impl = { + .create_source = output_manager_handle_create_source, + .destroy = output_manager_handle_destroy, +}; + +static void output_manager_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id) { + struct wlr_ext_output_image_capture_source_manager_v1 *manager = data; + + struct wl_resource *resource = wl_resource_create(client, + &ext_output_image_capture_source_manager_v1_interface, version, id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &output_manager_impl, manager, NULL); +} + +static void output_manager_handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_ext_output_image_capture_source_manager_v1 *manager = + wl_container_of(listener, manager, display_destroy); + wl_list_remove(&manager->display_destroy.link); + wl_global_destroy(manager->global); + free(manager); +} + +struct wlr_ext_output_image_capture_source_manager_v1 *wlr_ext_output_image_capture_source_manager_v1_create( + struct wl_display *display, uint32_t version) { + assert(version <= OUTPUT_IMAGE_SOURCE_MANAGER_V1_VERSION); + + struct wlr_ext_output_image_capture_source_manager_v1 *manager = calloc(1, sizeof(*manager)); + if (manager == NULL) { + return NULL; + } + + manager->global = wl_global_create(display, + &ext_output_image_capture_source_manager_v1_interface, version, manager, output_manager_bind); + if (manager->global == NULL) { + free(manager); + return NULL; + } + + manager->display_destroy.notify = output_manager_handle_display_destroy; + wl_display_add_destroy_listener(display, &manager->display_destroy); + + return manager; +} diff --git a/types/meson.build b/types/meson.build index 4858c3135..ec516ae3d 100644 --- a/types/meson.build +++ b/types/meson.build @@ -4,6 +4,7 @@ wlr_files += files( 'data_device/wlr_data_source.c', 'data_device/wlr_drag.c', 'ext_image_capture_source_v1/base.c', + 'ext_image_capture_source_v1/output.c', 'output/cursor.c', 'output/output.c', 'output/render.c', From c0881bdc017b505a3d3e6a35b028d0a22caab0d7 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 26 Apr 2024 18:41:58 +0200 Subject: [PATCH 250/519] output: require commit after hardware cursor update Up until now only the DRM backend required an output commit after updating the cursor. Unify this for all backends, because: - Screen capture can now catch cursor updates listening for output commits - In the future we want to make the cursor a regular wlr_output_layer, which would need an output commit to be updated anyways --- backend/drm/drm.c | 2 -- types/output/cursor.c | 17 +++++++++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 727832c6b..237a0efe9 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -1162,7 +1162,6 @@ static bool drm_connector_set_cursor(struct wlr_output *output, conn->cursor_height = buffer->height; } - wlr_output_update_needs_frame(output); return true; } @@ -1192,7 +1191,6 @@ static bool drm_connector_move_cursor(struct wlr_output *output, conn->cursor_x = box.x; conn->cursor_y = box.y; - wlr_output_update_needs_frame(output); return true; } diff --git a/types/output/cursor.c b/types/output/cursor.c index dcb605fac..63341d275 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -23,6 +23,8 @@ static bool output_set_hardware_cursor(struct wlr_output *output, return false; } + wlr_output_update_needs_frame(output); + wlr_buffer_unlock(output->cursor_front_buffer); output->cursor_front_buffer = NULL; @@ -33,6 +35,15 @@ static bool output_set_hardware_cursor(struct wlr_output *output, return true; } +static bool output_move_hardware_cursor(struct wlr_output *output, int x, int y) { + assert(output->impl->move_cursor); + if (!output->impl->move_cursor(output, x, y)) { + return false; + } + wlr_output_update_needs_frame(output); + return true; +} + static void output_cursor_damage_whole(struct wlr_output_cursor *cursor); static void output_disable_hardware_cursor(struct wlr_output *output) { @@ -299,8 +310,7 @@ static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) { // If the cursor was hidden or was a software cursor, the hardware // cursor position is outdated - output->impl->move_cursor(cursor->output, - (int)cursor->x, (int)cursor->y); + output_move_hardware_cursor(cursor->output, (int)cursor->x, (int)cursor->y); struct wlr_buffer *buffer = NULL; if (texture != NULL) { @@ -452,8 +462,7 @@ bool wlr_output_cursor_move(struct wlr_output_cursor *cursor, return true; } - assert(cursor->output->impl->move_cursor); - return cursor->output->impl->move_cursor(cursor->output, (int)x, (int)y); + return output_move_hardware_cursor(cursor->output, (int)x, (int)y); } struct wlr_output_cursor *wlr_output_cursor_create(struct wlr_output *output) { From 248e837cb325070bf466e2ebed1d520d34df7827 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 18 Apr 2024 17:56:50 +0200 Subject: [PATCH 251/519] ext-image-capture-source-v1: add output cursor source --- types/ext_image_capture_source_v1/output.c | 158 +++++++++++++++++++++ 1 file changed, 158 insertions(+) diff --git a/types/ext_image_capture_source_v1/output.c b/types/ext_image_capture_source_v1/output.c index f547ae31e..bcfb36b9a 100644 --- a/types/ext_image_capture_source_v1/output.c +++ b/types/ext_image_capture_source_v1/output.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -11,6 +12,18 @@ #define OUTPUT_IMAGE_SOURCE_MANAGER_V1_VERSION 1 +struct output_cursor_source { + struct wlr_ext_image_capture_source_v1_cursor base; + + struct wlr_output *output; + struct wlr_buffer *prev_buffer; + bool initialized; + bool needs_frame; + + struct wl_listener output_commit; + struct wl_listener prev_buffer_release; +}; + struct wlr_ext_output_image_capture_source_v1 { struct wlr_ext_image_capture_source_v1 base; struct wlr_addon addon; @@ -18,6 +31,8 @@ struct wlr_ext_output_image_capture_source_v1 { struct wlr_output *output; struct wl_listener output_commit; + + struct output_cursor_source cursor; }; struct wlr_ext_output_image_capture_source_v1_frame_event { @@ -45,9 +60,17 @@ static void output_source_copy_frame(struct wlr_ext_image_capture_source_v1 *bas } } +static struct wlr_ext_image_capture_source_v1_cursor *output_source_get_pointer_cursor( + struct wlr_ext_image_capture_source_v1 *base, struct wlr_seat *seat) { + // TODO: handle seat + struct wlr_ext_output_image_capture_source_v1 *source = wl_container_of(base, source, base); + return &source->cursor.base; +} + static const struct wlr_ext_image_capture_source_v1_interface output_source_impl = { .schedule_frame = output_source_schedule_frame, .copy_frame = output_source_copy_frame, + .get_pointer_cursor = output_source_get_pointer_cursor, }; static void source_update_buffer_constraints(struct wlr_ext_output_image_capture_source_v1 *source) { @@ -96,9 +119,14 @@ static void source_handle_output_commit(struct wl_listener *listener, } } +static void output_cursor_source_init(struct output_cursor_source *cursor_source, + struct wlr_output *output); +static void output_cursor_source_finish(struct output_cursor_source *cursor_source); + static void output_addon_destroy(struct wlr_addon *addon) { struct wlr_ext_output_image_capture_source_v1 *source = wl_container_of(addon, source, addon); wlr_ext_image_capture_source_v1_finish(&source->base); + output_cursor_source_finish(&source->cursor); wl_list_remove(&source->output_commit.link); wlr_addon_finish(&source->addon); free(source); @@ -137,6 +165,8 @@ static void output_manager_handle_create_source(struct wl_client *client, wl_signal_add(&output->events.commit, &source->output_commit); source_update_buffer_constraints(source); + + output_cursor_source_init(&source->cursor, output); } if (!wlr_ext_image_capture_source_v1_create_resource(&source->base, client, new_id)) { @@ -196,3 +226,131 @@ struct wlr_ext_output_image_capture_source_manager_v1 *wlr_ext_output_image_capt return manager; } + +static void output_cursor_source_schedule_frame(struct wlr_ext_image_capture_source_v1 *base) { + struct output_cursor_source *cursor_source = wl_container_of(base, cursor_source, base); + wlr_output_update_needs_frame(cursor_source->output); + cursor_source->needs_frame = true; +} + +static void output_cursor_source_copy_frame(struct wlr_ext_image_capture_source_v1 *base, + struct wlr_ext_image_copy_capture_frame_v1 *frame, + struct wlr_ext_image_capture_source_v1_frame_event *base_event) { + struct output_cursor_source *cursor_source = wl_container_of(base, cursor_source, base); + + struct wlr_buffer *src_buffer = cursor_source->output->cursor_front_buffer; + if (src_buffer == NULL) { + wlr_ext_image_copy_capture_frame_v1_fail(frame, EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_STOPPED); + return; + } + + if (!wlr_ext_image_copy_capture_frame_v1_copy_buffer(frame, + src_buffer, cursor_source->output->renderer)) { + return; + } + + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + wlr_ext_image_copy_capture_frame_v1_ready(frame, WL_OUTPUT_TRANSFORM_NORMAL, &now); +} + +static const struct wlr_ext_image_capture_source_v1_interface output_cursor_source_impl = { + .schedule_frame = output_cursor_source_schedule_frame, + .copy_frame = output_cursor_source_copy_frame, +}; + +static void output_cursor_source_update(struct output_cursor_source *cursor_source) { + struct wlr_output *output = cursor_source->output; + + if (output->cursor_swapchain != NULL && !cursor_source->initialized) { + wlr_ext_image_capture_source_v1_set_constraints_from_swapchain(&cursor_source->base.base, + output->cursor_swapchain, output->renderer); + cursor_source->initialized = true; + } + + struct wlr_output_cursor *output_cursor = output->hardware_cursor; + if (output_cursor == NULL || !output_cursor->visible) { + cursor_source->base.entered = false; + wl_signal_emit_mutable(&cursor_source->base.events.update, NULL); + return; + } + + if (output->cursor_swapchain != NULL && + ((int)cursor_source->base.base.width != output->cursor_swapchain->width || + (int)cursor_source->base.base.height != output->cursor_swapchain->height)) { + cursor_source->base.base.width = output->cursor_swapchain->width; + cursor_source->base.base.height = output->cursor_swapchain->height; + wl_signal_emit_mutable(&cursor_source->base.base.events.constraints_update, NULL); + } + + cursor_source->base.entered = true; + cursor_source->base.x = round(output_cursor->x); + cursor_source->base.y = round(output_cursor->y); + cursor_source->base.hotspot.x = output_cursor->hotspot_x; + cursor_source->base.hotspot.y = output_cursor->hotspot_y; + wl_signal_emit_mutable(&cursor_source->base.events.update, NULL); +} + +static void output_cursor_source_handle_prev_buffer_release(struct wl_listener *listener, + void *data) { + struct output_cursor_source *cursor_source = wl_container_of(listener, cursor_source, prev_buffer_release); + wl_list_remove(&cursor_source->prev_buffer_release.link); + wl_list_init(&cursor_source->prev_buffer_release.link); + cursor_source->prev_buffer = NULL; +} + +static void output_cursor_source_handle_output_commit(struct wl_listener *listener, + void *data) { + struct output_cursor_source *cursor_source = wl_container_of(listener, cursor_source, output_commit); + struct wlr_output_event_commit *event = data; + + output_cursor_source_update(cursor_source); + + struct wlr_buffer *buffer = cursor_source->output->cursor_front_buffer; + if (buffer != NULL && (buffer != cursor_source->prev_buffer || cursor_source->needs_frame)) { + pixman_region32_t full_damage; + pixman_region32_init_rect(&full_damage, 0, 0, buffer->width, buffer->height); + + struct wlr_ext_output_image_capture_source_v1_frame_event frame_event = { + .base = { + .damage = &full_damage, + }, + .buffer = buffer, + .when = event->when, // TODO: predict next presentation time instead + }; + wl_signal_emit_mutable(&cursor_source->base.base.events.frame, &frame_event); + + pixman_region32_fini(&full_damage); + + assert(buffer->n_locks > 0); + cursor_source->prev_buffer = buffer; + wl_list_remove(&cursor_source->prev_buffer_release.link); + cursor_source->prev_buffer_release.notify = output_cursor_source_handle_prev_buffer_release; + wl_signal_add(&buffer->events.release, &cursor_source->prev_buffer_release); + } + + cursor_source->needs_frame = false; +} + +static void output_cursor_source_init(struct output_cursor_source *cursor_source, + struct wlr_output *output) { + wlr_ext_image_capture_source_v1_cursor_init(&cursor_source->base, &output_cursor_source_impl); + + // Caller is responsible for destroying the output cursor source when the + // output is destroyed + cursor_source->output = output; + + cursor_source->output_commit.notify = output_cursor_source_handle_output_commit; + wl_signal_add(&output->events.commit, &cursor_source->output_commit); + + wl_list_init(&cursor_source->prev_buffer_release.link); + + output_cursor_source_update(cursor_source); +} + +static void output_cursor_source_finish(struct output_cursor_source *cursor_source) { + wlr_ext_image_capture_source_v1_cursor_finish(&cursor_source->base); + wl_list_remove(&cursor_source->output_commit.link); + wl_list_remove(&cursor_source->prev_buffer_release.link); +} From 82f9cd53101b8391b3f58accdfcd6abb8e49a113 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 26 Apr 2024 18:48:19 +0200 Subject: [PATCH 252/519] backend/headless: accept hardware cursors When running headless, remoting programs (e.g. VNC servers) might want to capture outputs without the cursor, and send the cursor image separately. --- backend/headless/output.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/backend/headless/output.c b/backend/headless/output.c index 4d983a72a..a4cdb17c3 100644 --- a/backend/headless/output.c +++ b/backend/headless/output.c @@ -79,6 +79,15 @@ static bool output_commit(struct wlr_output *wlr_output, return true; } +static bool output_set_cursor(struct wlr_output *wlr_output, + struct wlr_buffer *buffer, int hotspot_x, int hotspot_y) { + return true; +} + +static bool output_move_cursor(struct wlr_output *wlr_output, int x, int y) { + return true; +} + static void output_destroy(struct wlr_output *wlr_output) { struct wlr_headless_output *output = headless_output_from_output(wlr_output); @@ -93,6 +102,8 @@ static const struct wlr_output_impl output_impl = { .destroy = output_destroy, .test = output_test, .commit = output_commit, + .set_cursor = output_set_cursor, + .move_cursor = output_move_cursor, }; bool wlr_output_is_headless(struct wlr_output *wlr_output) { From 6bb8bb1cb79edb4897dadf9071ced304dcd4ca05 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 7 Jul 2024 22:14:53 +0200 Subject: [PATCH 253/519] ext-image-capture-source-v1: add start/stop hooks This allows the source to change its behavior when actively captured. --- .../wlr_ext_image_capture_source_v1.h | 3 ++ types/ext_image_capture_source_v1/output.c | 32 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/include/wlr/interfaces/wlr_ext_image_capture_source_v1.h b/include/wlr/interfaces/wlr_ext_image_capture_source_v1.h index c9b7857a7..8678d3c67 100644 --- a/include/wlr/interfaces/wlr_ext_image_capture_source_v1.h +++ b/include/wlr/interfaces/wlr_ext_image_capture_source_v1.h @@ -18,6 +18,9 @@ struct wlr_renderer; struct wlr_seat; struct wlr_ext_image_capture_source_v1_interface { + // TODO: drop with_cursors flag + void (*start)(struct wlr_ext_image_capture_source_v1 *source, bool with_cursors); + void (*stop)(struct wlr_ext_image_capture_source_v1 *source); void (*schedule_frame)(struct wlr_ext_image_capture_source_v1 *source); void (*copy_frame)(struct wlr_ext_image_capture_source_v1 *source, struct wlr_ext_image_copy_capture_frame_v1 *dst_frame, diff --git a/types/ext_image_capture_source_v1/output.c b/types/ext_image_capture_source_v1/output.c index bcfb36b9a..1bb0dcc9e 100644 --- a/types/ext_image_capture_source_v1/output.c +++ b/types/ext_image_capture_source_v1/output.c @@ -33,6 +33,9 @@ struct wlr_ext_output_image_capture_source_v1 { struct wl_listener output_commit; struct output_cursor_source cursor; + + size_t num_started; + bool software_cursors_locked; }; struct wlr_ext_output_image_capture_source_v1_frame_event { @@ -41,6 +44,33 @@ struct wlr_ext_output_image_capture_source_v1_frame_event { struct timespec *when; }; +static void output_source_start(struct wlr_ext_image_capture_source_v1 *base, + bool with_cursors) { + struct wlr_ext_output_image_capture_source_v1 *source = wl_container_of(base, source, base); + source->num_started++; + if (source->num_started > 1) { + return; + } + wlr_output_lock_attach_render(source->output, true); + if (with_cursors) { + wlr_output_lock_software_cursors(source->output, true); + } + source->software_cursors_locked = with_cursors; +} + +static void output_source_stop(struct wlr_ext_image_capture_source_v1 *base) { + struct wlr_ext_output_image_capture_source_v1 *source = wl_container_of(base, source, base); + assert(source->num_started > 0); + source->num_started--; + if (source->num_started > 0) { + return; + } + wlr_output_lock_attach_render(source->output, false); + if (source->software_cursors_locked) { + wlr_output_lock_software_cursors(source->output, false); + } +} + static void output_source_schedule_frame(struct wlr_ext_image_capture_source_v1 *base) { struct wlr_ext_output_image_capture_source_v1 *source = wl_container_of(base, source, base); wlr_output_update_needs_frame(source->output); @@ -68,6 +98,8 @@ static struct wlr_ext_image_capture_source_v1_cursor *output_source_get_pointer_ } static const struct wlr_ext_image_capture_source_v1_interface output_source_impl = { + .start = output_source_start, + .stop = output_source_stop, .schedule_frame = output_source_schedule_frame, .copy_frame = output_source_copy_frame, .get_pointer_cursor = output_source_get_pointer_cursor, From 061aa1bd15679a24e17ac3bea0cf6cd606b5fd7e Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 7 Jul 2024 22:15:50 +0200 Subject: [PATCH 254/519] ext-image-copy-capture-v1: implement PAINT_CURSORS flag This is unreliable because this is first come, first served: the first capture stream decides whether or not cursors will be included. Moreover, if the output lacks hw cursor support, cursors will always be included. But it's the best we're going to get with automatic wlr_output sources (and has bug parity with wlr-screencopy-unstable-v1). --- types/wlr_ext_image_copy_capture_v1.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/types/wlr_ext_image_copy_capture_v1.c b/types/wlr_ext_image_copy_capture_v1.c index a9d78a10d..592e4e46c 100644 --- a/types/wlr_ext_image_copy_capture_v1.c +++ b/types/wlr_ext_image_copy_capture_v1.c @@ -389,6 +389,10 @@ static void session_destroy(struct wlr_ext_image_copy_capture_session_v1 *sessio EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_STOPPED); } + if (session->source->impl->stop) { + session->source->impl->stop(session->source); + } + ext_image_copy_capture_session_v1_send_stopped(session->resource); wl_resource_set_user_data(session->resource, NULL); @@ -456,6 +460,10 @@ static void session_create(struct wl_resource *parent_resource, uint32_t new_id, return; } + if (source->impl->start) { + source->impl->start(source, options & EXT_IMAGE_COPY_CAPTURE_MANAGER_V1_OPTIONS_PAINT_CURSORS); + } + session->resource = session_resource; session->source = source; pixman_region32_init_rect(&session->damage, 0, 0, source->width, From bcf8e467db76695982485f23fdafeedbec84e3fc Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Thu, 6 Jun 2024 13:46:50 +0300 Subject: [PATCH 255/519] xdg-toplevel-icon-v1: add implementation --- include/wlr/types/wlr_xdg_toplevel_icon_v1.h | 80 +++++ protocol/meson.build | 1 + types/meson.build | 1 + types/wlr_xdg_toplevel_icon_v1.c | 293 +++++++++++++++++++ 4 files changed, 375 insertions(+) create mode 100644 include/wlr/types/wlr_xdg_toplevel_icon_v1.h create mode 100644 types/wlr_xdg_toplevel_icon_v1.c diff --git a/include/wlr/types/wlr_xdg_toplevel_icon_v1.h b/include/wlr/types/wlr_xdg_toplevel_icon_v1.h new file mode 100644 index 000000000..a08a6a7f8 --- /dev/null +++ b/include/wlr/types/wlr_xdg_toplevel_icon_v1.h @@ -0,0 +1,80 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_TYPES_WLR_XDG_TOPLEVEL_ICON_V1_H +#define WLR_TYPES_WLR_XDG_TOPLEVEL_ICON_V1_H + +#include + +#include + +struct wlr_xdg_toplevel_icon_manager_v1 { + struct wl_global *global; + + struct wl_list resources; + + int *sizes; + size_t n_sizes; + + struct { + struct wl_signal set_icon; // struct wlr_xdg_toplevel_icon_manager_v1_set_icon_event + struct wl_signal destroy; + } events; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; +}; + +struct wlr_xdg_toplevel_icon_manager_v1_set_icon_event { + struct wlr_xdg_toplevel *toplevel; + + // Must be referenced to be used after the event is emitted + struct wlr_xdg_toplevel_icon_v1 *icon; // May be NULL +}; + +struct wlr_xdg_toplevel_icon_v1_buffer { + struct wlr_buffer *buffer; + int scale; + + struct wl_list link; // wlr_xdg_toplevel_icon_v1.buffers +}; + +struct wlr_xdg_toplevel_icon_v1 { + char *name; // May be NULL + struct wl_list buffers; // wlr_xdg_toplevel_icon_v1_buffer.link + + struct { + int n_refs; + bool immutable; + } WLR_PRIVATE; +}; + +struct wlr_xdg_toplevel_icon_manager_v1 *wlr_xdg_toplevel_icon_manager_v1_create( + struct wl_display *display, uint32_t version); + +/** + * Set icon size preferences. + * + * The list may be empty. + */ +void wlr_xdg_toplevel_icon_manager_v1_set_sizes(struct wlr_xdg_toplevel_icon_manager_v1 *manager, + int *sizes, size_t n_sizes); + +/** + * Reference an icon. + */ +struct wlr_xdg_toplevel_icon_v1 *wlr_xdg_toplevel_icon_v1_ref( + struct wlr_xdg_toplevel_icon_v1 *icon); + +/** + * Unreference an icon. When the icon reference count reaches 0, it is destroyed. + */ +void wlr_xdg_toplevel_icon_v1_unref(struct wlr_xdg_toplevel_icon_v1 *icon); + +#endif diff --git a/protocol/meson.build b/protocol/meson.build index e987d4ea3..931aad56f 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -39,6 +39,7 @@ protocols = { 'xdg-activation-v1': wl_protocol_dir / 'staging/xdg-activation/xdg-activation-v1.xml', 'xdg-dialog-v1': wl_protocol_dir / 'staging/xdg-dialog/xdg-dialog-v1.xml', 'xdg-system-bell-v1': wl_protocol_dir / 'staging/xdg-system-bell/xdg-system-bell-v1.xml', + 'xdg-toplevel-icon-v1': wl_protocol_dir / 'staging/xdg-toplevel-icon/xdg-toplevel-icon-v1.xml', 'xwayland-shell-v1': wl_protocol_dir / 'staging/xwayland-shell/xwayland-shell-v1.xml', 'tearing-control-v1': wl_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml', diff --git a/types/meson.build b/types/meson.build index ec516ae3d..466c1de63 100644 --- a/types/meson.build +++ b/types/meson.build @@ -99,6 +99,7 @@ wlr_files += files( 'wlr_xdg_foreign_v2.c', 'wlr_xdg_foreign_registry.c', 'wlr_xdg_output_v1.c', + 'wlr_xdg_toplevel_icon_v1.c', ) if features.get('drm-backend') diff --git a/types/wlr_xdg_toplevel_icon_v1.c b/types/wlr_xdg_toplevel_icon_v1.c new file mode 100644 index 000000000..0995e6d1c --- /dev/null +++ b/types/wlr_xdg_toplevel_icon_v1.c @@ -0,0 +1,293 @@ +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "xdg-toplevel-icon-v1-protocol.h" + +#define MANAGER_VERSION 1 + +static const struct xdg_toplevel_icon_v1_interface icon_impl; + +static struct wlr_xdg_toplevel_icon_v1 *icon_from_resource(struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &xdg_toplevel_icon_v1_interface, &icon_impl)); + return wl_resource_get_user_data(resource); +} + +static void icon_destroy(struct wlr_xdg_toplevel_icon_v1 *icon) { + struct wlr_xdg_toplevel_icon_v1_buffer *icon_buffer, *tmp; + wl_list_for_each_safe(icon_buffer, tmp, &icon->buffers, link) { + wlr_buffer_unlock(icon_buffer->buffer); + wl_list_remove(&icon_buffer->link); + free(icon_buffer); + } + + free(icon->name); + free(icon); +} + +static void icon_handle_destroy(struct wl_client *client, struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static void icon_handle_set_name(struct wl_client *client, struct wl_resource *resource, + const char *name) { + struct wlr_xdg_toplevel_icon_v1 *icon = icon_from_resource(resource); + if (icon->immutable) { + wl_resource_post_error(resource, XDG_TOPLEVEL_ICON_V1_ERROR_IMMUTABLE, + "the icon has already been assigned to a toplevel and must not be changed"); + return; + } + + char *dup = strdup(name); + if (dup == NULL) { + wl_resource_post_no_memory(resource); + return; + } + + free(icon->name); + icon->name = dup; +} + +static void icon_handle_add_buffer(struct wl_client *client, struct wl_resource *resource, + struct wl_resource *buffer_resource, int32_t scale) { + struct wlr_xdg_toplevel_icon_v1 *icon = icon_from_resource(resource); + if (icon->immutable) { + wl_resource_post_error(resource, XDG_TOPLEVEL_ICON_V1_ERROR_IMMUTABLE, + "the icon has already been assigned to a toplevel and must not be changed"); + return; + } + + struct wlr_buffer *buffer = wlr_buffer_try_from_resource(buffer_resource); + + const char *bad_buffer_msg = NULL; + + struct wlr_shm_attributes shm_attribs; + if (!wlr_buffer_get_shm(buffer, &shm_attribs)) { + bad_buffer_msg = "not backed by wl_shm"; + } else if (buffer->width != buffer->height) { + bad_buffer_msg = "not square"; + } + + if (bad_buffer_msg != NULL) { + wl_resource_post_error(resource, XDG_TOPLEVEL_ICON_V1_ERROR_INVALID_BUFFER, + "the provided buffer does not satisfy requirements: %s", bad_buffer_msg); + wlr_buffer_unlock(buffer); + return; + } + + struct wlr_xdg_toplevel_icon_v1_buffer *icon_buffer; + wl_list_for_each(icon_buffer, &icon->buffers, link) { + if (icon_buffer->buffer->width == buffer->width && icon_buffer->scale == scale) { + wlr_buffer_unlock(icon_buffer->buffer); + icon_buffer->buffer = buffer; + return; + } + } + + icon_buffer = calloc(1, sizeof(*icon_buffer)); + if (icon_buffer == NULL) { + wl_resource_post_no_memory(resource); + } + + icon_buffer->buffer = buffer; + icon_buffer->scale = scale; + + wl_list_insert(&icon->buffers, &icon_buffer->link); +} + +static const struct xdg_toplevel_icon_v1_interface icon_impl = { + .destroy = icon_handle_destroy, + .set_name = icon_handle_set_name, + .add_buffer = icon_handle_add_buffer, +}; + +static void icon_handle_resource_destroy(struct wl_resource *resource) { + wlr_xdg_toplevel_icon_v1_unref(icon_from_resource(resource)); +} + +struct wlr_xdg_toplevel_icon_v1 *wlr_xdg_toplevel_icon_v1_ref( + struct wlr_xdg_toplevel_icon_v1 *icon) { + ++icon->n_refs; + return icon; +} + +void wlr_xdg_toplevel_icon_v1_unref(struct wlr_xdg_toplevel_icon_v1 *icon) { + if (icon == NULL) { + return; + } + + assert(icon->n_refs > 0); + --icon->n_refs; + if (icon->n_refs == 0) { + icon_destroy(icon); + }; +} + +static const struct xdg_toplevel_icon_manager_v1_interface manager_impl; + +static struct wlr_xdg_toplevel_icon_manager_v1 *manager_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &xdg_toplevel_icon_manager_v1_interface, + &manager_impl)); + return wl_resource_get_user_data(resource); +} + +static void manager_handle_destroy(struct wl_client *client, struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static void manager_handle_create_icon(struct wl_client *client, struct wl_resource *resource, + uint32_t id) { + struct wlr_xdg_toplevel_icon_v1 *icon = calloc(1, sizeof(*icon)); + if (icon == NULL) { + wl_client_post_no_memory(client); + return; + } + + struct wl_resource *icon_resource = wl_resource_create(client, + &xdg_toplevel_icon_v1_interface, wl_resource_get_version(resource), id); + if (resource == NULL) { + wl_client_post_no_memory(client); + free(icon); + return; + } + + wl_list_init(&icon->buffers); + icon->n_refs = 1; + + wl_resource_set_implementation(icon_resource, &icon_impl, icon, icon_handle_resource_destroy); +} + +static void manager_handle_set_icon(struct wl_client *client, struct wl_resource *resource, + struct wl_resource *toplevel_resource, struct wl_resource *icon_resource) { + struct wlr_xdg_toplevel_icon_manager_v1 *manager = manager_from_resource(resource); + struct wlr_xdg_toplevel *toplevel = wlr_xdg_toplevel_from_resource(toplevel_resource); + + struct wlr_xdg_toplevel_icon_v1 *icon = NULL; + if (icon_resource != NULL) { + icon = icon_from_resource(icon_resource); + icon->immutable = true; + + if (icon->name == NULL && wl_list_empty(&icon->buffers)) { + // Same as supplying null icon + icon = NULL; + } + } + + struct wlr_xdg_toplevel_icon_manager_v1_set_icon_event event = { + .toplevel = toplevel, + .icon = icon, + }; + + wl_signal_emit_mutable(&manager->events.set_icon, &event); +} + +static const struct xdg_toplevel_icon_manager_v1_interface manager_impl = { + .destroy = manager_handle_destroy, + .create_icon = manager_handle_create_icon, + .set_icon = manager_handle_set_icon, +}; + +static void manager_send_sizes(struct wlr_xdg_toplevel_icon_manager_v1 *manager, + struct wl_resource *resource) { + for (size_t i = 0; i < manager->n_sizes; i++) { + xdg_toplevel_icon_manager_v1_send_icon_size(resource, manager->sizes[i]); + } + xdg_toplevel_icon_manager_v1_send_done(resource); +} + +static void manager_handle_resource_destroy(struct wl_resource *resource) { + wl_list_remove(wl_resource_get_link(resource)); +} + +static void manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { + struct wlr_xdg_toplevel_icon_manager_v1 *manager = data; + + struct wl_resource *resource = wl_resource_create(client, + &xdg_toplevel_icon_manager_v1_interface, version, id); + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &manager_impl, manager, manager_handle_resource_destroy); + + wl_list_insert(&manager->resources, wl_resource_get_link(resource)); + + manager_send_sizes(manager, resource); +} + +static void manager_handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_xdg_toplevel_icon_manager_v1 *manager = + wl_container_of(listener, manager, display_destroy); + + wl_signal_emit_mutable(&manager->events.destroy, NULL); + + wl_list_remove(&manager->display_destroy.link); + wl_global_destroy(manager->global); + + wl_list_remove(&manager->resources); + + free(manager->sizes); + free(manager); +} + +void wlr_xdg_toplevel_icon_manager_v1_set_sizes(struct wlr_xdg_toplevel_icon_manager_v1 *manager, + int *sizes, size_t n_sizes) { + if (n_sizes != manager->n_sizes) { + int *dup_sizes = NULL; + if (n_sizes > 0) { + dup_sizes = calloc(n_sizes, sizeof(*dup_sizes)); + if (dup_sizes == NULL) { + wlr_log(WLR_ERROR, "Allocation failed"); + return; + } + } + + free(manager->sizes); + manager->sizes = dup_sizes; + manager->n_sizes = n_sizes; + } + + for (size_t i = 0; i < n_sizes; i++) { + manager->sizes[i] = sizes[i]; + } + + struct wl_resource *resource; + wl_resource_for_each(resource, &manager->resources) { + manager_send_sizes(manager, resource); + } +} + +struct wlr_xdg_toplevel_icon_manager_v1 *wlr_xdg_toplevel_icon_manager_v1_create( + struct wl_display *display, uint32_t version) { + assert(version <= MANAGER_VERSION); + + struct wlr_xdg_toplevel_icon_manager_v1 *manager = calloc(1, sizeof(*manager)); + if (manager == NULL) { + return NULL; + } + + manager->global = wl_global_create(display, &xdg_toplevel_icon_manager_v1_interface, + version, manager, manager_bind); + if (manager->global == NULL) { + free(manager); + return NULL; + } + + wl_signal_init(&manager->events.set_icon); + wl_signal_init(&manager->events.destroy); + + wl_list_init(&manager->resources); + + manager->display_destroy.notify = manager_handle_display_destroy; + wl_display_add_destroy_listener(display, &manager->display_destroy); + + return manager; +} From b03b05d2b3f617bca684bec7e933cdbf209f0873 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Wed, 8 Jan 2025 19:29:19 +0300 Subject: [PATCH 256/519] xdg-dialog: add missing wm destroy signal --- include/wlr/types/wlr_xdg_dialog_v1.h | 1 + types/wlr_xdg_dialog_v1.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/include/wlr/types/wlr_xdg_dialog_v1.h b/include/wlr/types/wlr_xdg_dialog_v1.h index 061c3252b..0017015ad 100644 --- a/include/wlr/types/wlr_xdg_dialog_v1.h +++ b/include/wlr/types/wlr_xdg_dialog_v1.h @@ -35,6 +35,7 @@ struct wlr_xdg_wm_dialog_v1 { struct wl_global *global; struct { + struct wl_signal destroy; struct wl_signal new_dialog; // struct wlr_xdg_dialog_v1 } events; diff --git a/types/wlr_xdg_dialog_v1.c b/types/wlr_xdg_dialog_v1.c index f9e73c7e4..f1eac0207 100644 --- a/types/wlr_xdg_dialog_v1.c +++ b/types/wlr_xdg_dialog_v1.c @@ -152,6 +152,8 @@ static void wm_bind(struct wl_client *client, void *data, uint32_t version, uint static void xdg_wm_dialog_handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_xdg_wm_dialog_v1 *wm = wl_container_of(listener, wm, display_destroy); + wl_signal_emit_mutable(&wm->events.destroy, NULL); + wl_list_remove(&wm->display_destroy.link); wl_global_destroy(wm->global); free(wm); @@ -186,6 +188,7 @@ struct wlr_xdg_wm_dialog_v1 *wlr_xdg_wm_dialog_v1_create(struct wl_display *disp wm->display_destroy.notify = xdg_wm_dialog_handle_display_destroy; wl_display_add_destroy_listener(display, &wm->display_destroy); + wl_signal_init(&wm->events.destroy); wl_signal_init(&wm->events.new_dialog); return wm; From 8f56f7ca43257cc05c7c4eb57a0f541e05cf9a79 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Fri, 22 Nov 2024 20:32:32 +0300 Subject: [PATCH 257/519] Assert (almost all) signals have no attached listeners on destroy --- backend/backend.c | 4 +++ backend/drm/drm.c | 2 ++ backend/multi/backend.c | 3 ++ backend/session/session.c | 9 ++++++ render/allocator/allocator.c | 4 +++ render/drm_syncobj.c | 3 +- render/wlr_renderer.c | 3 ++ tinywl/tinywl.c | 17 +++++++++++ types/buffer/buffer.c | 5 ++++ types/data_device/wlr_data_device.c | 3 ++ types/data_device/wlr_data_source.c | 2 ++ types/data_device/wlr_drag.c | 8 +++++ types/ext_image_capture_source_v1/base.c | 1 + types/output/output.c | 15 ++++++++-- types/scene/wlr_scene.c | 1 + types/seat/wlr_seat.c | 28 +++++++++++++++++ types/seat/wlr_seat_touch.c | 2 ++ types/tablet_v2/wlr_tablet_v2.c | 4 +++ types/wlr_compositor.c | 15 +++++++++- types/wlr_cursor.c | 28 +++++++++++++++++ types/wlr_cursor_shape_v1.c | 2 ++ types/wlr_data_control_v1.c | 5 ++++ types/wlr_drm.c | 2 ++ types/wlr_drm_lease_v1.c | 3 ++ types/wlr_export_dmabuf_v1.c | 4 +++ types/wlr_ext_foreign_toplevel_list_v1.c | 6 ++++ types/wlr_foreign_toplevel_management_v1.c | 12 ++++++++ types/wlr_fullscreen_shell_v1.c | 4 +++ types/wlr_gamma_control_v1.c | 5 ++++ types/wlr_idle_inhibit_v1.c | 8 +++++ types/wlr_input_device.c | 4 +-- types/wlr_input_method_v2.c | 21 +++++++++++++ types/wlr_keyboard.c | 5 ++++ types/wlr_keyboard_group.c | 6 ++-- types/wlr_keyboard_shortcuts_inhibit_v1.c | 8 +++++ types/wlr_layer_shell_v1.c | 8 +++++ types/wlr_linux_dmabuf_v1.c | 3 ++ types/wlr_output_layer.c | 5 ++++ types/wlr_output_layout.c | 8 +++++ types/wlr_output_management_v1.c | 6 ++++ types/wlr_output_power_management_v1.c | 5 ++++ types/wlr_pointer.c | 14 +++++++++ types/wlr_pointer_constraints_v1.c | 7 +++++ types/wlr_pointer_gestures_v1.c | 3 ++ types/wlr_presentation_time.c | 3 ++ types/wlr_primary_selection.c | 3 ++ types/wlr_primary_selection_v1.c | 4 +++ types/wlr_relative_pointer_v1.c | 6 ++++ types/wlr_screencopy_v1.c | 3 ++ types/wlr_security_context_v1.c | 2 ++ types/wlr_server_decoration.c | 9 ++++++ types/wlr_subcompositor.c | 5 ++++ types/wlr_switch.c | 2 ++ types/wlr_tablet_pad.c | 5 ++++ types/wlr_tablet_tool.c | 6 ++++ types/wlr_tearing_control_v1.c | 6 ++++ types/wlr_text_input_v3.c | 11 +++++++ types/wlr_touch.c | 6 ++++ types/wlr_transient_seat_v1.c | 4 +++ types/wlr_viewporter.c | 3 ++ types/wlr_virtual_keyboard_v1.c | 4 +++ types/wlr_virtual_pointer_v1.c | 5 ++++ types/wlr_xdg_activation_v1.c | 8 +++++ types/wlr_xdg_decoration_v1.c | 10 +++++++ types/wlr_xdg_dialog_v1.c | 6 ++++ types/wlr_xdg_foreign_registry.c | 8 +++++ types/wlr_xdg_foreign_v1.c | 4 +++ types/wlr_xdg_foreign_v2.c | 4 +++ types/wlr_xdg_output_v1.c | 6 +++- types/wlr_xdg_system_bell_v1.c | 4 +++ types/wlr_xdg_toplevel_icon_v1.c | 3 ++ types/xdg_shell/wlr_xdg_popup.c | 3 ++ types/xdg_shell/wlr_xdg_shell.c | 6 ++++ types/xdg_shell/wlr_xdg_surface.c | 6 ++++ types/xdg_shell/wlr_xdg_toplevel.c | 11 +++++++ xwayland/server.c | 6 ++++ xwayland/shell.c | 4 +++ xwayland/xwayland.c | 5 ++++ xwayland/xwm.c | 35 ++++++++++++++++++++++ 79 files changed, 518 insertions(+), 9 deletions(-) diff --git a/backend/backend.c b/backend/backend.c index fdba79c55..b8a9d71cc 100644 --- a/backend/backend.c +++ b/backend/backend.c @@ -48,6 +48,10 @@ void wlr_backend_init(struct wlr_backend *backend, void wlr_backend_finish(struct wlr_backend *backend) { wl_signal_emit_mutable(&backend->events.destroy, backend); + + assert(wl_list_empty(&backend->events.destroy.listener_list)); + assert(wl_list_empty(&backend->events.new_input.listener_list)); + assert(wl_list_empty(&backend->events.new_output.listener_list)); } bool wlr_backend_start(struct wlr_backend *backend) { diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 237a0efe9..6ae9dcbf3 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -2177,6 +2177,8 @@ void drm_lease_destroy(struct wlr_drm_lease *lease) { wl_signal_emit_mutable(&lease->events.destroy, NULL); + assert(wl_list_empty(&lease->events.destroy.listener_list)); + struct wlr_drm_connector *conn; wl_list_for_each(conn, &drm->connectors, link) { if (conn->lease == lease) { diff --git a/backend/multi/backend.c b/backend/multi/backend.c index 41ef31ccb..3d8fb96f5 100644 --- a/backend/multi/backend.c +++ b/backend/multi/backend.c @@ -51,6 +51,9 @@ static void multi_backend_destroy(struct wlr_backend *wlr_backend) { wlr_backend_finish(wlr_backend); + assert(wl_list_empty(&backend->events.backend_add.listener_list)); + assert(wl_list_empty(&backend->events.backend_remove.listener_list)); + // Some backends may depend on other backends, ie. destroying a backend may // also destroy other backends while (!wl_list_empty(&backend->backends)) { diff --git a/backend/session/session.c b/backend/session/session.c index b0b2894b4..dcf07c708 100644 --- a/backend/session/session.c +++ b/backend/session/session.c @@ -303,6 +303,11 @@ void wlr_session_destroy(struct wlr_session *session) { } wl_signal_emit_mutable(&session->events.destroy, session); + + assert(wl_list_empty(&session->events.active.listener_list)); + assert(wl_list_empty(&session->events.add_drm_card.listener_list)); + assert(wl_list_empty(&session->events.destroy.listener_list)); + wl_list_remove(&session->event_loop_destroy.link); wl_event_source_remove(session->udev_event); @@ -360,6 +365,10 @@ void wlr_session_close_file(struct wlr_session *session, if (libseat_close_device(session->seat_handle, dev->device_id) == -1) { wlr_log_errno(WLR_ERROR, "Failed to close device %d", dev->device_id); } + + assert(wl_list_empty(&dev->events.change.listener_list)); + assert(wl_list_empty(&dev->events.remove.listener_list)); + close(dev->fd); wl_list_remove(&dev->link); free(dev); diff --git a/render/allocator/allocator.c b/render/allocator/allocator.c index 4fd5211a8..b7dbf3d65 100644 --- a/render/allocator/allocator.c +++ b/render/allocator/allocator.c @@ -28,6 +28,7 @@ void wlr_allocator_init(struct wlr_allocator *alloc, .impl = impl, .buffer_caps = buffer_caps, }; + wl_signal_init(&alloc->events.destroy); } @@ -173,6 +174,9 @@ void wlr_allocator_destroy(struct wlr_allocator *alloc) { return; } wl_signal_emit_mutable(&alloc->events.destroy, NULL); + + assert(wl_list_empty(&alloc->events.destroy.listener_list)); + alloc->impl->destroy(alloc); } diff --git a/render/drm_syncobj.c b/render/drm_syncobj.c index 9e5569fb4..7d52f4118 100644 --- a/render/drm_syncobj.c +++ b/render/drm_syncobj.c @@ -241,7 +241,8 @@ bool wlr_drm_syncobj_timeline_waiter_init(struct wlr_drm_syncobj_timeline_waiter } void wlr_drm_syncobj_timeline_waiter_finish(struct wlr_drm_syncobj_timeline_waiter *waiter) { - wl_list_remove(&waiter->events.ready.listener_list); + assert(wl_list_empty(&waiter->events.ready.listener_list)); + wl_event_source_remove(waiter->event_source); close(waiter->ev_fd); } diff --git a/render/wlr_renderer.c b/render/wlr_renderer.c index ae324afb9..eb6b9e97f 100644 --- a/render/wlr_renderer.c +++ b/render/wlr_renderer.c @@ -51,6 +51,9 @@ void wlr_renderer_destroy(struct wlr_renderer *r) { wl_signal_emit_mutable(&r->events.destroy, r); + assert(wl_list_empty(&r->events.destroy.listener_list)); + assert(wl_list_empty(&r->events.lost.listener_list)); + if (r->impl && r->impl->destroy) { r->impl->destroy(r); } else { diff --git a/tinywl/tinywl.c b/tinywl/tinywl.c index 915938485..89837b4fb 100644 --- a/tinywl/tinywl.c +++ b/tinywl/tinywl.c @@ -1053,9 +1053,26 @@ int main(int argc, char *argv[]) { socket); wl_display_run(server.wl_display); + /* Once wl_display_run returns, we destroy all clients then shut down the * server. */ wl_display_destroy_clients(server.wl_display); + + wl_list_remove(&server.new_xdg_toplevel.link); + wl_list_remove(&server.new_xdg_popup.link); + + wl_list_remove(&server.cursor_motion.link); + wl_list_remove(&server.cursor_motion_absolute.link); + wl_list_remove(&server.cursor_button.link); + wl_list_remove(&server.cursor_axis.link); + wl_list_remove(&server.cursor_frame.link); + + wl_list_remove(&server.new_input.link); + wl_list_remove(&server.request_cursor.link); + wl_list_remove(&server.request_set_selection.link); + + wl_list_remove(&server.new_output.link); + wlr_scene_node_destroy(&server.scene->tree.node); wlr_xcursor_manager_destroy(server.cursor_mgr); wlr_cursor_destroy(server.cursor); diff --git a/types/buffer/buffer.c b/types/buffer/buffer.c index 1438b27b2..2a7576c7e 100644 --- a/types/buffer/buffer.c +++ b/types/buffer/buffer.c @@ -17,14 +17,19 @@ void wlr_buffer_init(struct wlr_buffer *buffer, .width = width, .height = height, }; + wl_signal_init(&buffer->events.destroy); wl_signal_init(&buffer->events.release); + wlr_addon_set_init(&buffer->addons); } void wlr_buffer_finish(struct wlr_buffer *buffer) { wl_signal_emit_mutable(&buffer->events.destroy, NULL); wlr_addon_set_finish(&buffer->addons); + + assert(wl_list_empty(&buffer->events.destroy.listener_list)); + assert(wl_list_empty(&buffer->events.release.listener_list)); } static void buffer_consider_destroy(struct wlr_buffer *buffer) { diff --git a/types/data_device/wlr_data_device.c b/types/data_device/wlr_data_device.c index 3b5b8a668..ad7966d36 100644 --- a/types/data_device/wlr_data_device.c +++ b/types/data_device/wlr_data_device.c @@ -284,6 +284,9 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_data_device_manager *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); + + assert(wl_list_empty(&manager->events.destroy.listener_list)); + wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); diff --git a/types/data_device/wlr_data_source.c b/types/data_device/wlr_data_source.c index 15fb9929e..64b73c56e 100644 --- a/types/data_device/wlr_data_source.c +++ b/types/data_device/wlr_data_source.c @@ -41,6 +41,8 @@ void wlr_data_source_destroy(struct wlr_data_source *source) { wl_signal_emit_mutable(&source->events.destroy, source); + assert(wl_list_empty(&source->events.destroy.listener_list)); + char **p; wl_array_for_each(p, &source->mime_types) { free(*p); diff --git a/types/data_device/wlr_drag.c b/types/data_device/wlr_drag.c index b1d348949..b780eedac 100644 --- a/types/data_device/wlr_drag.c +++ b/types/data_device/wlr_drag.c @@ -123,6 +123,9 @@ static void drag_icon_destroy(struct wlr_drag_icon *icon) { icon->drag->icon = NULL; wl_list_remove(&icon->surface_destroy.link); wl_signal_emit_mutable(&icon->events.destroy, icon); + + assert(wl_list_empty(&icon->events.destroy.listener_list)); + free(icon); } @@ -160,6 +163,11 @@ static void drag_destroy(struct wlr_drag *drag) { // signal handler. wl_signal_emit_mutable(&drag->events.destroy, drag); + assert(wl_list_empty(&drag->events.focus.listener_list)); + assert(wl_list_empty(&drag->events.motion.listener_list)); + assert(wl_list_empty(&drag->events.drop.listener_list)); + assert(wl_list_empty(&drag->events.destroy.listener_list)); + if (drag->source) { wl_list_remove(&drag->source_destroy.link); } diff --git a/types/ext_image_capture_source_v1/base.c b/types/ext_image_capture_source_v1/base.c index 0fc910864..7b34030c8 100644 --- a/types/ext_image_capture_source_v1/base.c +++ b/types/ext_image_capture_source_v1/base.c @@ -149,4 +149,5 @@ void wlr_ext_image_capture_source_v1_cursor_init(struct wlr_ext_image_capture_so void wlr_ext_image_capture_source_v1_cursor_finish(struct wlr_ext_image_capture_source_v1_cursor *source_cursor) { wlr_ext_image_capture_source_v1_finish(&source_cursor->base); + assert(wl_list_empty(&source_cursor->events.update.listener_list)); } diff --git a/types/output/output.c b/types/output/output.c index 7bfdf9c77..a352a5e59 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -349,6 +349,7 @@ void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend, wl_list_init(&output->cursors); wl_list_init(&output->layers); wl_list_init(&output->resources); + wl_signal_init(&output->events.frame); wl_signal_init(&output->events.damage); wl_signal_init(&output->events.needs_frame); @@ -377,13 +378,23 @@ void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend, void wlr_output_finish(struct wlr_output *output) { wl_signal_emit_mutable(&output->events.destroy, output); + wlr_addon_set_finish(&output->addons); + + assert(wl_list_empty(&output->events.frame.listener_list)); + assert(wl_list_empty(&output->events.damage.listener_list)); + assert(wl_list_empty(&output->events.needs_frame.listener_list)); + assert(wl_list_empty(&output->events.precommit.listener_list)); + assert(wl_list_empty(&output->events.commit.listener_list)); + assert(wl_list_empty(&output->events.present.listener_list)); + assert(wl_list_empty(&output->events.bind.listener_list)); + assert(wl_list_empty(&output->events.description.listener_list)); + assert(wl_list_empty(&output->events.request_state.listener_list)); + assert(wl_list_empty(&output->events.destroy.listener_list)); wlr_output_destroy_global(output); wl_list_remove(&output->display_destroy.link); - wlr_addon_set_finish(&output->addons); - // The backend is responsible for free-ing the list of modes struct wlr_output_cursor *cursor, *tmp_cursor; diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 58d1cc3f0..9a6266381 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -833,6 +833,7 @@ struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent, wl_signal_init(&scene_buffer->events.output_leave); wl_signal_init(&scene_buffer->events.output_sample); wl_signal_init(&scene_buffer->events.frame_done); + pixman_region32_init(&scene_buffer->opaque_region); wl_list_init(&scene_buffer->buffer_release.link); wl_list_init(&scene_buffer->renderer_destroy.link); diff --git a/types/seat/wlr_seat.c b/types/seat/wlr_seat.c index 5ca69aac8..015a57b52 100644 --- a/types/seat/wlr_seat.c +++ b/types/seat/wlr_seat.c @@ -71,6 +71,8 @@ static void seat_handle_get_touch(struct wl_client *client, static void seat_client_destroy(struct wlr_seat_client *client) { wl_signal_emit_mutable(&client->events.destroy, client); + assert(wl_list_empty(&client->events.destroy.listener_list)); + if (client == client->seat->pointer_state.focused_client) { client->seat->pointer_state.focused_client = NULL; } @@ -151,6 +153,7 @@ static struct wlr_seat_client *seat_client_create(struct wlr_seat *wlr_seat, wl_list_init(&seat_client->keyboards); wl_list_init(&seat_client->touches); wl_list_init(&seat_client->data_devices); + wl_signal_init(&seat_client->events.destroy); wl_list_insert(&wlr_seat->clients, &seat_client->link); @@ -227,6 +230,31 @@ void wlr_seat_destroy(struct wlr_seat *seat) { wl_signal_emit_mutable(&seat->events.destroy, seat); + assert(wl_list_empty(&seat->pointer_state.events.focus_change.listener_list)); + + assert(wl_list_empty(&seat->keyboard_state.events.focus_change.listener_list)); + + assert(wl_list_empty(&seat->events.request_start_drag.listener_list)); + assert(wl_list_empty(&seat->events.start_drag.listener_list)); + + assert(wl_list_empty(&seat->events.request_set_cursor.listener_list)); + + assert(wl_list_empty(&seat->events.request_set_selection.listener_list)); + assert(wl_list_empty(&seat->events.set_selection.listener_list)); + assert(wl_list_empty(&seat->events.request_set_primary_selection.listener_list)); + assert(wl_list_empty(&seat->events.set_primary_selection.listener_list)); + + assert(wl_list_empty(&seat->events.pointer_grab_begin.listener_list)); + assert(wl_list_empty(&seat->events.pointer_grab_end.listener_list)); + + assert(wl_list_empty(&seat->events.keyboard_grab_begin.listener_list)); + assert(wl_list_empty(&seat->events.keyboard_grab_end.listener_list)); + + assert(wl_list_empty(&seat->events.touch_grab_begin.listener_list)); + assert(wl_list_empty(&seat->events.touch_grab_end.listener_list)); + + assert(wl_list_empty(&seat->events.destroy.listener_list)); + wl_list_remove(&seat->display_destroy.link); wlr_data_source_destroy(seat->selection_source); diff --git a/types/seat/wlr_seat_touch.c b/types/seat/wlr_seat_touch.c index 6aac48841..03b72f698 100644 --- a/types/seat/wlr_seat_touch.c +++ b/types/seat/wlr_seat_touch.c @@ -107,6 +107,8 @@ static void touch_point_clear_focus(struct wlr_touch_point *point) { static void touch_point_destroy(struct wlr_touch_point *point) { wl_signal_emit_mutable(&point->events.destroy, point); + assert(wl_list_empty(&point->events.destroy.listener_list)); + touch_point_clear_focus(point); wl_list_remove(&point->surface_destroy.link); wl_list_remove(&point->client_destroy.link); diff --git a/types/tablet_v2/wlr_tablet_v2.c b/types/tablet_v2/wlr_tablet_v2.c index 28671c9b0..a0cf2fa67 100644 --- a/types/tablet_v2/wlr_tablet_v2.c +++ b/types/tablet_v2/wlr_tablet_v2.c @@ -270,6 +270,9 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_tablet_manager_v2 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); + + assert(wl_list_empty(&manager->events.destroy.listener_list)); + wl_list_remove(&manager->display_destroy.link); struct wlr_tablet_seat_v2 *seat, *tmp; @@ -296,6 +299,7 @@ struct wlr_tablet_manager_v2 *wlr_tablet_v2_create(struct wl_display *display) { } wl_signal_init(&tablet->events.destroy); + wl_list_init(&tablet->clients); wl_list_init(&tablet->seats); diff --git a/types/wlr_compositor.c b/types/wlr_compositor.c index 081af3539..afa35ec99 100644 --- a/types/wlr_compositor.c +++ b/types/wlr_compositor.c @@ -727,8 +727,15 @@ static void surface_handle_resource_destroy(struct wl_resource *resource) { surface_destroy_role_object(surface); wl_signal_emit_mutable(&surface->events.destroy, surface); - wlr_addon_set_finish(&surface->addons); + + assert(wl_list_empty(&surface->events.client_commit.listener_list)); + assert(wl_list_empty(&surface->events.commit.listener_list)); + assert(wl_list_empty(&surface->events.map.listener_list)); + assert(wl_list_empty(&surface->events.unmap.listener_list)); + assert(wl_list_empty(&surface->events.destroy.listener_list)); + assert(wl_list_empty(&surface->events.new_subsurface.listener_list)); + assert(wl_list_empty(&surface->synced)); struct wlr_surface_state *cached, *cached_tmp; @@ -782,6 +789,7 @@ static struct wlr_surface *surface_create(struct wl_client *client, wl_signal_init(&surface->events.unmap); wl_signal_init(&surface->events.destroy); wl_signal_init(&surface->events.new_subsurface); + wl_list_init(&surface->current_outputs); wl_list_init(&surface->cached); pixman_region32_init(&surface->buffer_damage); @@ -1337,6 +1345,10 @@ static void compositor_handle_display_destroy( struct wlr_compositor *compositor = wl_container_of(listener, compositor, display_destroy); wl_signal_emit_mutable(&compositor->events.destroy, NULL); + + assert(wl_list_empty(&compositor->events.new_surface.listener_list)); + assert(wl_list_empty(&compositor->events.destroy.listener_list)); + wl_list_remove(&compositor->display_destroy.link); wl_list_remove(&compositor->renderer_destroy.link); wl_global_destroy(compositor->global); @@ -1368,6 +1380,7 @@ struct wlr_compositor *wlr_compositor_create(struct wl_display *display, wl_signal_init(&compositor->events.new_surface); wl_signal_init(&compositor->events.destroy); + wl_list_init(&compositor->renderer_destroy.link); compositor->display_destroy.notify = compositor_handle_display_destroy; diff --git a/types/wlr_cursor.c b/types/wlr_cursor.c index 7c2f56ace..44b74e926 100644 --- a/types/wlr_cursor.c +++ b/types/wlr_cursor.c @@ -247,6 +247,34 @@ static void cursor_reset_image(struct wlr_cursor *cur) { } void wlr_cursor_destroy(struct wlr_cursor *cur) { + // pointer signals + assert(wl_list_empty(&cur->events.motion.listener_list)); + assert(wl_list_empty(&cur->events.motion_absolute.listener_list)); + assert(wl_list_empty(&cur->events.button.listener_list)); + assert(wl_list_empty(&cur->events.axis.listener_list)); + assert(wl_list_empty(&cur->events.frame.listener_list)); + assert(wl_list_empty(&cur->events.swipe_begin.listener_list)); + assert(wl_list_empty(&cur->events.swipe_update.listener_list)); + assert(wl_list_empty(&cur->events.swipe_end.listener_list)); + assert(wl_list_empty(&cur->events.pinch_begin.listener_list)); + assert(wl_list_empty(&cur->events.pinch_update.listener_list)); + assert(wl_list_empty(&cur->events.pinch_end.listener_list)); + assert(wl_list_empty(&cur->events.hold_begin.listener_list)); + assert(wl_list_empty(&cur->events.hold_end.listener_list)); + + // touch signals + assert(wl_list_empty(&cur->events.touch_up.listener_list)); + assert(wl_list_empty(&cur->events.touch_down.listener_list)); + assert(wl_list_empty(&cur->events.touch_motion.listener_list)); + assert(wl_list_empty(&cur->events.touch_cancel.listener_list)); + assert(wl_list_empty(&cur->events.touch_frame.listener_list)); + + // tablet tool signals + assert(wl_list_empty(&cur->events.tablet_tool_tip.listener_list)); + assert(wl_list_empty(&cur->events.tablet_tool_axis.listener_list)); + assert(wl_list_empty(&cur->events.tablet_tool_button.listener_list)); + assert(wl_list_empty(&cur->events.tablet_tool_proximity.listener_list)); + cursor_reset_image(cur); cursor_detach_output_layout(cur); diff --git a/types/wlr_cursor_shape_v1.c b/types/wlr_cursor_shape_v1.c index 1bd1126bd..3e657fb6d 100644 --- a/types/wlr_cursor_shape_v1.c +++ b/types/wlr_cursor_shape_v1.c @@ -187,6 +187,8 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, NULL); + + assert(wl_list_empty(&manager->events.request_set_shape.listener_list)); assert(wl_list_empty(&manager->events.destroy.listener_list)); wl_global_destroy(manager->global); diff --git a/types/wlr_data_control_v1.c b/types/wlr_data_control_v1.c index 88f23ae46..2e26d703d 100644 --- a/types/wlr_data_control_v1.c +++ b/types/wlr_data_control_v1.c @@ -666,6 +666,10 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_data_control_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); + + assert(wl_list_empty(&manager->events.destroy.listener_list)); + assert(wl_list_empty(&manager->events.new_device.listener_list)); + wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); @@ -678,6 +682,7 @@ struct wlr_data_control_manager_v1 *wlr_data_control_manager_v1_create( return NULL; } wl_list_init(&manager->devices); + wl_signal_init(&manager->events.destroy); wl_signal_init(&manager->events.new_device); diff --git a/types/wlr_drm.c b/types/wlr_drm.c index e7e57c3c6..f8db8291b 100644 --- a/types/wlr_drm.c +++ b/types/wlr_drm.c @@ -189,6 +189,8 @@ static const struct wlr_buffer_resource_interface buffer_resource_interface = { static void drm_destroy(struct wlr_drm *drm) { wl_signal_emit_mutable(&drm->events.destroy, NULL); + assert(wl_list_empty(&drm->events.destroy.listener_list)); + wl_list_remove(&drm->display_destroy.link); wlr_drm_format_set_finish(&drm->formats); diff --git a/types/wlr_drm_lease_v1.c b/types/wlr_drm_lease_v1.c index 52010ea0d..50097ae75 100644 --- a/types/wlr_drm_lease_v1.c +++ b/types/wlr_drm_lease_v1.c @@ -684,6 +684,9 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { wl_signal_emit_mutable(&manager->events.destroy, NULL); + assert(wl_list_empty(&manager->events.destroy.listener_list)); + assert(wl_list_empty(&manager->events.request.listener_list)); + struct wlr_drm_lease_device_v1 *device, *tmp; wl_list_for_each_safe(device, tmp, &manager->devices, link) { drm_lease_device_v1_destroy(device); diff --git a/types/wlr_export_dmabuf_v1.c b/types/wlr_export_dmabuf_v1.c index 69245e373..ea4a53880 100644 --- a/types/wlr_export_dmabuf_v1.c +++ b/types/wlr_export_dmabuf_v1.c @@ -192,6 +192,9 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_export_dmabuf_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); + + assert(wl_list_empty(&manager->events.destroy.listener_list)); + wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); @@ -204,6 +207,7 @@ struct wlr_export_dmabuf_manager_v1 *wlr_export_dmabuf_manager_v1_create( return NULL; } wl_list_init(&manager->frames); + wl_signal_init(&manager->events.destroy); manager->global = wl_global_create(display, diff --git a/types/wlr_ext_foreign_toplevel_list_v1.c b/types/wlr_ext_foreign_toplevel_list_v1.c index a773ee8a2..432b27fc2 100644 --- a/types/wlr_ext_foreign_toplevel_list_v1.c +++ b/types/wlr_ext_foreign_toplevel_list_v1.c @@ -91,6 +91,8 @@ void wlr_ext_foreign_toplevel_handle_v1_destroy( wl_signal_emit_mutable(&toplevel->events.destroy, NULL); + assert(wl_list_empty(&toplevel->events.destroy.listener_list)); + struct wl_resource *resource, *tmp; wl_resource_for_each_safe(resource, tmp, &toplevel->resources) { ext_foreign_toplevel_handle_v1_send_closed(resource); @@ -253,6 +255,9 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_ext_foreign_toplevel_list_v1 *list = wl_container_of(listener, list, display_destroy); wl_signal_emit_mutable(&list->events.destroy, NULL); + + assert(wl_list_empty(&list->events.destroy.listener_list)); + wl_list_remove(&list->display_destroy.link); wl_global_destroy(list->global); free(list); @@ -277,6 +282,7 @@ struct wlr_ext_foreign_toplevel_list_v1 *wlr_ext_foreign_toplevel_list_v1_create } wl_signal_init(&list->events.destroy); + wl_list_init(&list->resources); wl_list_init(&list->toplevels); diff --git a/types/wlr_foreign_toplevel_management_v1.c b/types/wlr_foreign_toplevel_management_v1.c index db7e526fd..f06fdb7b7 100644 --- a/types/wlr_foreign_toplevel_management_v1.c +++ b/types/wlr_foreign_toplevel_management_v1.c @@ -452,6 +452,14 @@ void wlr_foreign_toplevel_handle_v1_destroy( wl_signal_emit_mutable(&toplevel->events.destroy, toplevel); + assert(wl_list_empty(&toplevel->events.request_maximize.listener_list)); + assert(wl_list_empty(&toplevel->events.request_minimize.listener_list)); + assert(wl_list_empty(&toplevel->events.request_activate.listener_list)); + assert(wl_list_empty(&toplevel->events.request_fullscreen.listener_list)); + assert(wl_list_empty(&toplevel->events.request_close.listener_list)); + assert(wl_list_empty(&toplevel->events.set_rectangle.listener_list)); + assert(wl_list_empty(&toplevel->events.destroy.listener_list)); + struct wl_resource *resource, *tmp; wl_resource_for_each_safe(resource, tmp, &toplevel->resources) { zwlr_foreign_toplevel_handle_v1_send_closed(resource); @@ -627,6 +635,9 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_foreign_toplevel_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); + + assert(wl_list_empty(&manager->events.destroy.listener_list)); + wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); @@ -650,6 +661,7 @@ struct wlr_foreign_toplevel_manager_v1 *wlr_foreign_toplevel_manager_v1_create( } wl_signal_init(&manager->events.destroy); + wl_list_init(&manager->resources); wl_list_init(&manager->toplevels); diff --git a/types/wlr_fullscreen_shell_v1.c b/types/wlr_fullscreen_shell_v1.c index d59e94a47..53d8c4e91 100644 --- a/types/wlr_fullscreen_shell_v1.c +++ b/types/wlr_fullscreen_shell_v1.c @@ -109,6 +109,10 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_fullscreen_shell_v1 *shell = wl_container_of(listener, shell, display_destroy); wl_signal_emit_mutable(&shell->events.destroy, shell); + + assert(wl_list_empty(&shell->events.destroy.listener_list)); + assert(wl_list_empty(&shell->events.present_surface.listener_list)); + wl_list_remove(&shell->display_destroy.link); wl_global_destroy(shell->global); free(shell); diff --git a/types/wlr_gamma_control_v1.c b/types/wlr_gamma_control_v1.c index 732439de4..087281423 100644 --- a/types/wlr_gamma_control_v1.c +++ b/types/wlr_gamma_control_v1.c @@ -215,6 +215,10 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_gamma_control_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); + + assert(wl_list_empty(&manager->events.destroy.listener_list)); + assert(wl_list_empty(&manager->events.set_gamma.listener_list)); + wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); @@ -237,6 +241,7 @@ struct wlr_gamma_control_manager_v1 *wlr_gamma_control_manager_v1_create( wl_signal_init(&manager->events.destroy); wl_signal_init(&manager->events.set_gamma); + wl_list_init(&manager->controls); manager->display_destroy.notify = handle_display_destroy; diff --git a/types/wlr_idle_inhibit_v1.c b/types/wlr_idle_inhibit_v1.c index 93e8e8546..388778800 100644 --- a/types/wlr_idle_inhibit_v1.c +++ b/types/wlr_idle_inhibit_v1.c @@ -34,6 +34,8 @@ static void idle_inhibitor_v1_destroy(struct wlr_idle_inhibitor_v1 *inhibitor) { wl_signal_emit_mutable(&inhibitor->events.destroy, inhibitor->surface); + assert(wl_list_empty(&inhibitor->events.destroy.listener_list)); + wl_resource_set_user_data(inhibitor->resource, NULL); wl_list_remove(&inhibitor->link); wl_list_remove(&inhibitor->surface_destroy.link); @@ -87,6 +89,7 @@ static void manager_handle_create_inhibitor(struct wl_client *client, inhibitor->resource = inhibitor_resource; inhibitor->surface = surface; + wl_signal_init(&inhibitor->events.destroy); inhibitor->surface_destroy.notify = idle_inhibitor_handle_surface_destroy; @@ -113,6 +116,10 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_idle_inhibit_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); + + assert(wl_list_empty(&manager->events.new_inhibitor.listener_list)); + assert(wl_list_empty(&manager->events.destroy.listener_list)); + wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); @@ -140,6 +147,7 @@ struct wlr_idle_inhibit_manager_v1 *wlr_idle_inhibit_v1_create(struct wl_display } wl_list_init(&manager->inhibitors); + wl_signal_init(&manager->events.new_inhibitor); wl_signal_init(&manager->events.destroy); diff --git a/types/wlr_input_device.c b/types/wlr_input_device.c index ede2e04d8..8d6301589 100644 --- a/types/wlr_input_device.c +++ b/types/wlr_input_device.c @@ -1,4 +1,4 @@ - +#include #include #include #include "interfaces/wlr_input_device.h" @@ -20,7 +20,7 @@ void wlr_input_device_finish(struct wlr_input_device *wlr_device) { wl_signal_emit_mutable(&wlr_device->events.destroy, wlr_device); - wl_list_remove(&wlr_device->events.destroy.listener_list); + assert(wl_list_empty(&wlr_device->events.destroy.listener_list)); free(wlr_device->name); } diff --git a/types/wlr_input_method_v2.c b/types/wlr_input_method_v2.c index e187ea74c..3804a113a 100644 --- a/types/wlr_input_method_v2.c +++ b/types/wlr_input_method_v2.c @@ -27,6 +27,9 @@ static void popup_surface_destroy(struct wlr_input_popup_surface_v2 *popup_surfa wlr_surface_unmap(popup_surface->surface); wl_signal_emit_mutable(&popup_surface->events.destroy, NULL); + + assert(wl_list_empty(&popup_surface->events.destroy.listener_list)); + wl_list_remove(&popup_surface->link); wl_resource_set_user_data(popup_surface->resource, NULL); free(popup_surface); @@ -54,6 +57,12 @@ static void input_method_destroy(struct wlr_input_method_v2 *input_method) { popup_surface_destroy(popup_surface); } wl_signal_emit_mutable(&input_method->events.destroy, input_method); + + assert(wl_list_empty(&input_method->events.commit.listener_list)); + assert(wl_list_empty(&input_method->events.new_popup_surface.listener_list)); + assert(wl_list_empty(&input_method->events.grab_keyboard.listener_list)); + assert(wl_list_empty(&input_method->events.destroy.listener_list)); + wl_list_remove(wl_resource_get_link(input_method->resource)); wl_list_remove(&input_method->seat_client_destroy.link); wlr_input_method_keyboard_grab_v2_destroy(input_method->keyboard_grab); @@ -263,6 +272,9 @@ void wlr_input_method_keyboard_grab_v2_destroy( return; } wl_signal_emit_mutable(&keyboard_grab->events.destroy, keyboard_grab); + + assert(wl_list_empty(&keyboard_grab->events.destroy.listener_list)); + keyboard_grab->input_method->keyboard_grab = NULL; if (keyboard_grab->keyboard) { wl_list_remove(&keyboard_grab->keyboard_keymap.link); @@ -433,7 +445,9 @@ static void im_grab_keyboard(struct wl_client *client, keyboard_grab->resource = keyboard_grab_resource; keyboard_grab->input_method = input_method; input_method->keyboard_grab = keyboard_grab; + wl_signal_init(&keyboard_grab->events.destroy); + wl_signal_emit_mutable(&input_method->events.grab_keyboard, keyboard_grab); } @@ -544,6 +558,7 @@ static void manager_get_input_method(struct wl_client *client, return; } wl_list_init(&input_method->popup_surfaces); + wl_signal_init(&input_method->events.commit); wl_signal_init(&input_method->events.new_popup_surface); wl_signal_init(&input_method->events.grab_keyboard); @@ -592,6 +607,10 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_input_method_manager_v2 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); + + assert(wl_list_empty(&manager->events.input_method.listener_list)); + assert(wl_list_empty(&manager->events.destroy.listener_list)); + wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); @@ -603,8 +622,10 @@ struct wlr_input_method_manager_v2 *wlr_input_method_manager_v2_create( if (!im_manager) { return NULL; } + wl_signal_init(&im_manager->events.input_method); wl_signal_init(&im_manager->events.destroy); + wl_list_init(&im_manager->input_methods); im_manager->global = wl_global_create(display, diff --git a/types/wlr_keyboard.c b/types/wlr_keyboard.c index 5eff3ba39..edf0d3ecb 100644 --- a/types/wlr_keyboard.c +++ b/types/wlr_keyboard.c @@ -171,6 +171,11 @@ void wlr_keyboard_finish(struct wlr_keyboard *kb) { wlr_input_device_finish(&kb->base); + assert(wl_list_empty(&kb->events.key.listener_list)); + assert(wl_list_empty(&kb->events.modifiers.listener_list)); + assert(wl_list_empty(&kb->events.keymap.listener_list)); + assert(wl_list_empty(&kb->events.repeat_info.listener_list)); + keyboard_unset_keymap(kb); } diff --git a/types/wlr_keyboard_group.c b/types/wlr_keyboard_group.c index 25ef66695..734e0b2f3 100644 --- a/types/wlr_keyboard_group.c +++ b/types/wlr_keyboard_group.c @@ -311,7 +311,9 @@ void wlr_keyboard_group_destroy(struct wlr_keyboard_group *group) { wlr_keyboard_group_remove_keyboard(group, device->keyboard); } wlr_keyboard_finish(&group->keyboard); - wl_list_remove(&group->events.enter.listener_list); - wl_list_remove(&group->events.leave.listener_list); + + assert(wl_list_empty(&group->events.enter.listener_list)); + assert(wl_list_empty(&group->events.leave.listener_list)); + free(group); } diff --git a/types/wlr_keyboard_shortcuts_inhibit_v1.c b/types/wlr_keyboard_shortcuts_inhibit_v1.c index 0e4355616..db4f678f7 100644 --- a/types/wlr_keyboard_shortcuts_inhibit_v1.c +++ b/types/wlr_keyboard_shortcuts_inhibit_v1.c @@ -36,6 +36,8 @@ static void keyboard_shortcuts_inhibitor_v1_destroy( wl_signal_emit_mutable(&inhibitor->events.destroy, inhibitor); + assert(wl_list_empty(&inhibitor->events.destroy.listener_list)); + wl_resource_set_user_data(inhibitor->resource, NULL); wl_list_remove(&inhibitor->link); wl_list_remove(&inhibitor->surface_destroy.link); @@ -132,6 +134,7 @@ static void manager_handle_inhibit_shortcuts(struct wl_client *client, inhibitor->surface = surface; inhibitor->seat = seat; inhibitor->active = false; + wl_signal_init(&inhibitor->events.destroy); inhibitor->surface_destroy.notify = @@ -162,6 +165,10 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_keyboard_shortcuts_inhibit_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); + + assert(wl_list_empty(&manager->events.new_inhibitor.listener_list)); + assert(wl_list_empty(&manager->events.destroy.listener_list)); + wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); @@ -191,6 +198,7 @@ wlr_keyboard_shortcuts_inhibit_v1_create(struct wl_display *display) { } wl_list_init(&manager->inhibitors); + wl_signal_init(&manager->events.new_inhibitor); wl_signal_init(&manager->events.destroy); diff --git a/types/wlr_layer_shell_v1.c b/types/wlr_layer_shell_v1.c index d4893871c..b6181c6dc 100644 --- a/types/wlr_layer_shell_v1.c +++ b/types/wlr_layer_shell_v1.c @@ -50,6 +50,10 @@ static void layer_surface_destroy(struct wlr_layer_surface_v1 *surface) { layer_surface_reset(surface); wl_signal_emit_mutable(&surface->events.destroy, surface); + + assert(wl_list_empty(&surface->events.destroy.listener_list)); + assert(wl_list_empty(&surface->events.new_popup.listener_list)); + wlr_surface_synced_finish(&surface->synced); wl_resource_set_user_data(surface->resource, NULL); free(surface->namespace); @@ -541,6 +545,10 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_layer_shell_v1 *layer_shell = wl_container_of(listener, layer_shell, display_destroy); wl_signal_emit_mutable(&layer_shell->events.destroy, layer_shell); + + assert(wl_list_empty(&layer_shell->events.new_surface.listener_list)); + assert(wl_list_empty(&layer_shell->events.destroy.listener_list)); + wl_list_remove(&layer_shell->display_destroy.link); wl_global_destroy(layer_shell->global); free(layer_shell); diff --git a/types/wlr_linux_dmabuf_v1.c b/types/wlr_linux_dmabuf_v1.c index 8ccca906a..3165e8805 100644 --- a/types/wlr_linux_dmabuf_v1.c +++ b/types/wlr_linux_dmabuf_v1.c @@ -859,6 +859,8 @@ static const struct wlr_buffer_resource_interface buffer_resource_interface = { static void linux_dmabuf_v1_destroy(struct wlr_linux_dmabuf_v1 *linux_dmabuf) { wl_signal_emit_mutable(&linux_dmabuf->events.destroy, linux_dmabuf); + assert(wl_list_empty(&linux_dmabuf->events.destroy.listener_list)); + struct wlr_linux_dmabuf_v1_surface *surface, *surface_tmp; wl_list_for_each_safe(surface, surface_tmp, &linux_dmabuf->surfaces, link) { surface_destroy(surface); @@ -958,6 +960,7 @@ struct wlr_linux_dmabuf_v1 *wlr_linux_dmabuf_v1_create(struct wl_display *displa linux_dmabuf->main_device_fd = -1; wl_list_init(&linux_dmabuf->surfaces); + wl_signal_init(&linux_dmabuf->events.destroy); linux_dmabuf->global = wl_global_create(display, &zwp_linux_dmabuf_v1_interface, diff --git a/types/wlr_output_layer.c b/types/wlr_output_layer.c index 7624846f8..068cf58a5 100644 --- a/types/wlr_output_layer.c +++ b/types/wlr_output_layer.c @@ -1,3 +1,4 @@ +#include #include #include @@ -9,6 +10,7 @@ struct wlr_output_layer *wlr_output_layer_create(struct wlr_output *output) { wl_list_insert(&output->layers, &layer->link); wlr_addon_set_init(&layer->addons); + wl_signal_init(&layer->events.feedback); return layer; @@ -20,6 +22,9 @@ void wlr_output_layer_destroy(struct wlr_output_layer *layer) { } wlr_addon_set_finish(&layer->addons); + + assert(wl_list_empty(&layer->events.feedback.listener_list)); + wl_list_remove(&layer->link); free(layer); } diff --git a/types/wlr_output_layout.c b/types/wlr_output_layout.c index 853ff5944..ef2751179 100644 --- a/types/wlr_output_layout.c +++ b/types/wlr_output_layout.c @@ -36,6 +36,9 @@ struct wlr_output_layout *wlr_output_layout_create(struct wl_display *display) { static void output_layout_output_destroy( struct wlr_output_layout_output *l_output) { wl_signal_emit_mutable(&l_output->events.destroy, l_output); + + assert(wl_list_empty(&l_output->events.destroy.listener_list)); + wlr_output_destroy_global(l_output->output); wl_list_remove(&l_output->commit.link); wl_list_remove(&l_output->link); @@ -50,6 +53,10 @@ void wlr_output_layout_destroy(struct wlr_output_layout *layout) { wl_signal_emit_mutable(&layout->events.destroy, layout); + assert(wl_list_empty(&layout->events.add.listener_list)); + assert(wl_list_empty(&layout->events.change.listener_list)); + assert(wl_list_empty(&layout->events.destroy.listener_list)); + struct wlr_output_layout_output *l_output, *temp; wl_list_for_each_safe(l_output, temp, &layout->outputs, link) { output_layout_output_destroy(l_output); @@ -160,6 +167,7 @@ static struct wlr_output_layout_output *output_layout_output_create( } l_output->layout = layout; l_output->output = output; + wl_signal_init(&l_output->events.destroy); /* diff --git a/types/wlr_output_management_v1.c b/types/wlr_output_management_v1.c index 32f6b4ffe..910bcbe5e 100644 --- a/types/wlr_output_management_v1.c +++ b/types/wlr_output_management_v1.c @@ -647,6 +647,11 @@ static void manager_handle_display_destroy(struct wl_listener *listener, struct wlr_output_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); + + assert(wl_list_empty(&manager->events.destroy.listener_list)); + assert(wl_list_empty(&manager->events.apply.listener_list)); + assert(wl_list_empty(&manager->events.test.listener_list)); + wl_list_remove(&manager->display_destroy.link); struct wlr_output_head_v1 *head, *tmp; wl_list_for_each_safe(head, tmp, &manager->heads, link) { @@ -666,6 +671,7 @@ struct wlr_output_manager_v1 *wlr_output_manager_v1_create( wl_list_init(&manager->resources); wl_list_init(&manager->heads); + wl_signal_init(&manager->events.destroy); wl_signal_init(&manager->events.apply); wl_signal_init(&manager->events.test); diff --git a/types/wlr_output_power_management_v1.c b/types/wlr_output_power_management_v1.c index 25b92e40d..2a9948393 100644 --- a/types/wlr_output_power_management_v1.c +++ b/types/wlr_output_power_management_v1.c @@ -193,6 +193,10 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_output_power_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); + + assert(wl_list_empty(&manager->events.set_mode.listener_list)); + assert(wl_list_empty(&manager->events.destroy.listener_list)); + wl_global_destroy(manager->global); free(manager); } @@ -214,6 +218,7 @@ struct wlr_output_power_manager_v1 *wlr_output_power_manager_v1_create( wl_signal_init(&manager->events.set_mode); wl_signal_init(&manager->events.destroy); + wl_list_init(&manager->output_powers); manager->display_destroy.notify = handle_display_destroy; diff --git a/types/wlr_pointer.c b/types/wlr_pointer.c index e4ee10c8e..e99981907 100644 --- a/types/wlr_pointer.c +++ b/types/wlr_pointer.c @@ -51,6 +51,20 @@ void wlr_pointer_finish(struct wlr_pointer *pointer) { wlr_input_device_finish(&pointer->base); + assert(wl_list_empty(&pointer->events.motion.listener_list)); + assert(wl_list_empty(&pointer->events.motion_absolute.listener_list)); + assert(wl_list_empty(&pointer->events.button.listener_list)); + assert(wl_list_empty(&pointer->events.axis.listener_list)); + assert(wl_list_empty(&pointer->events.frame.listener_list)); + assert(wl_list_empty(&pointer->events.swipe_begin.listener_list)); + assert(wl_list_empty(&pointer->events.swipe_update.listener_list)); + assert(wl_list_empty(&pointer->events.swipe_end.listener_list)); + assert(wl_list_empty(&pointer->events.pinch_begin.listener_list)); + assert(wl_list_empty(&pointer->events.pinch_update.listener_list)); + assert(wl_list_empty(&pointer->events.pinch_end.listener_list)); + assert(wl_list_empty(&pointer->events.hold_begin.listener_list)); + assert(wl_list_empty(&pointer->events.hold_end.listener_list)); + free(pointer->output_name); } diff --git a/types/wlr_pointer_constraints_v1.c b/types/wlr_pointer_constraints_v1.c index 4560c273b..94cb48f24 100644 --- a/types/wlr_pointer_constraints_v1.c +++ b/types/wlr_pointer_constraints_v1.c @@ -47,6 +47,9 @@ static void pointer_constraint_destroy(struct wlr_pointer_constraint_v1 *constra wl_signal_emit_mutable(&constraint->events.destroy, constraint); + assert(wl_list_empty(&constraint->events.set_region.listener_list)); + assert(wl_list_empty(&constraint->events.destroy.listener_list)); + wl_resource_set_user_data(constraint->resource, NULL); wlr_surface_synced_finish(&constraint->synced); wl_list_remove(&constraint->link); @@ -323,6 +326,10 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_pointer_constraints_v1 *pointer_constraints = wl_container_of(listener, pointer_constraints, display_destroy); wl_signal_emit_mutable(&pointer_constraints->events.destroy, NULL); + + assert(wl_list_empty(&pointer_constraints->events.destroy.listener_list)); + assert(wl_list_empty(&pointer_constraints->events.new_constraint.listener_list)); + wl_list_remove(&pointer_constraints->display_destroy.link); wl_global_destroy(pointer_constraints->global); free(pointer_constraints); diff --git a/types/wlr_pointer_gestures_v1.c b/types/wlr_pointer_gestures_v1.c index ebb1c8689..a027faac9 100644 --- a/types/wlr_pointer_gestures_v1.c +++ b/types/wlr_pointer_gestures_v1.c @@ -397,6 +397,9 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_pointer_gestures_v1 *gestures = wl_container_of(listener, gestures, display_destroy); wl_signal_emit_mutable(&gestures->events.destroy, NULL); + + assert(wl_list_empty(&gestures->events.destroy.listener_list)); + wl_list_remove(&gestures->display_destroy.link); wl_global_destroy(gestures->global); free(gestures); diff --git a/types/wlr_presentation_time.c b/types/wlr_presentation_time.c index 0e5e88ea6..763cae953 100644 --- a/types/wlr_presentation_time.c +++ b/types/wlr_presentation_time.c @@ -166,6 +166,9 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_presentation *presentation = wl_container_of(listener, presentation, display_destroy); wl_signal_emit_mutable(&presentation->events.destroy, presentation); + + assert(wl_list_empty(&presentation->events.destroy.listener_list)); + wl_list_remove(&presentation->display_destroy.link); wl_global_destroy(presentation->global); free(presentation); diff --git a/types/wlr_primary_selection.c b/types/wlr_primary_selection.c index e3212d6fe..1940a2d82 100644 --- a/types/wlr_primary_selection.c +++ b/types/wlr_primary_selection.c @@ -11,6 +11,7 @@ void wlr_primary_selection_source_init( .impl = impl, }; wl_array_init(&source->mime_types); + wl_signal_init(&source->events.destroy); } @@ -22,6 +23,8 @@ void wlr_primary_selection_source_destroy( wl_signal_emit_mutable(&source->events.destroy, source); + assert(wl_list_empty(&source->events.destroy.listener_list)); + char **p; wl_array_for_each(p, &source->mime_types) { free(*p); diff --git a/types/wlr_primary_selection_v1.c b/types/wlr_primary_selection_v1.c index 0a5f0fbdc..fe70b7784 100644 --- a/types/wlr_primary_selection_v1.c +++ b/types/wlr_primary_selection_v1.c @@ -462,6 +462,9 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { } wl_signal_emit_mutable(&manager->events.destroy, manager); + + assert(wl_list_empty(&manager->events.destroy.listener_list)); + wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); @@ -483,6 +486,7 @@ struct wlr_primary_selection_v1_device_manager * } wl_list_init(&manager->devices); + wl_signal_init(&manager->events.destroy); manager->display_destroy.notify = handle_display_destroy; diff --git a/types/wlr_relative_pointer_v1.c b/types/wlr_relative_pointer_v1.c index 94fb1555e..9f4ab6fd7 100644 --- a/types/wlr_relative_pointer_v1.c +++ b/types/wlr_relative_pointer_v1.c @@ -28,6 +28,8 @@ static struct wlr_relative_pointer_manager_v1 *relative_pointer_manager_from_res static void relative_pointer_destroy(struct wlr_relative_pointer_v1 *relative_pointer) { wl_signal_emit_mutable(&relative_pointer->events.destroy, relative_pointer); + assert(wl_list_empty(&relative_pointer->events.destroy.listener_list)); + wl_list_remove(&relative_pointer->link); wl_list_remove(&relative_pointer->seat_destroy.link); wl_list_remove(&relative_pointer->pointer_destroy.link); @@ -139,6 +141,10 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_relative_pointer_manager_v1 *manager = wl_container_of(listener, manager, display_destroy_listener); wl_signal_emit_mutable(&manager->events.destroy, manager); + + assert(wl_list_empty(&manager->events.destroy.listener_list)); + assert(wl_list_empty(&manager->events.new_relative_pointer.listener_list)); + wl_list_remove(&manager->display_destroy_listener.link); wl_global_destroy(manager->global); free(manager); diff --git a/types/wlr_screencopy_v1.c b/types/wlr_screencopy_v1.c index 47cb5a3cf..7cf583b29 100644 --- a/types/wlr_screencopy_v1.c +++ b/types/wlr_screencopy_v1.c @@ -700,6 +700,9 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_screencopy_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); + + assert(wl_list_empty(&manager->events.destroy.listener_list)); + wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); diff --git a/types/wlr_security_context_v1.c b/types/wlr_security_context_v1.c index 337114487..da430c012 100644 --- a/types/wlr_security_context_v1.c +++ b/types/wlr_security_context_v1.c @@ -390,6 +390,7 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_security_context_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); + assert(wl_list_empty(&manager->events.destroy.listener_list)); assert(wl_list_empty(&manager->events.commit.listener_list)); @@ -419,6 +420,7 @@ struct wlr_security_context_manager_v1 *wlr_security_context_manager_v1_create( } wl_list_init(&manager->contexts); + wl_signal_init(&manager->events.destroy); wl_signal_init(&manager->events.commit); diff --git a/types/wlr_server_decoration.c b/types/wlr_server_decoration.c index 3d3d1ff3f..a6f33b469 100644 --- a/types/wlr_server_decoration.c +++ b/types/wlr_server_decoration.c @@ -36,6 +36,10 @@ static void server_decoration_handle_request_mode(struct wl_client *client, static void server_decoration_destroy( struct wlr_server_decoration *decoration) { wl_signal_emit_mutable(&decoration->events.destroy, decoration); + + assert(wl_list_empty(&decoration->events.destroy.listener_list)); + assert(wl_list_empty(&decoration->events.mode.listener_list)); + wl_list_remove(&decoration->surface_destroy_listener.link); wl_resource_set_user_data(decoration->resource, NULL); wl_list_remove(&decoration->link); @@ -164,6 +168,10 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_server_decoration_manager *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); + + assert(wl_list_empty(&manager->events.new_decoration.listener_list)); + assert(wl_list_empty(&manager->events.destroy.listener_list)); + wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); @@ -185,6 +193,7 @@ struct wlr_server_decoration_manager *wlr_server_decoration_manager_create( manager->default_mode = ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_NONE; wl_list_init(&manager->resources); wl_list_init(&manager->decorations); + wl_signal_init(&manager->events.new_decoration); wl_signal_init(&manager->events.destroy); diff --git a/types/wlr_subcompositor.c b/types/wlr_subcompositor.c index f0d0827b6..062fa69b8 100644 --- a/types/wlr_subcompositor.c +++ b/types/wlr_subcompositor.c @@ -33,6 +33,8 @@ static void subsurface_destroy(struct wlr_subsurface *subsurface) { wl_signal_emit_mutable(&subsurface->events.destroy, subsurface); + assert(wl_list_empty(&subsurface->events.destroy.listener_list)); + wlr_surface_synced_finish(&subsurface->parent_synced); wl_list_remove(&subsurface->surface_client_commit.link); @@ -406,6 +408,9 @@ static void subcompositor_handle_display_destroy( struct wlr_subcompositor *subcompositor = wl_container_of(listener, subcompositor, display_destroy); wl_signal_emit_mutable(&subcompositor->events.destroy, NULL); + + assert(wl_list_empty(&subcompositor->events.destroy.listener_list)); + wl_list_remove(&subcompositor->display_destroy.link); wl_global_destroy(subcompositor->global); free(subcompositor); diff --git a/types/wlr_switch.c b/types/wlr_switch.c index 93033aaba..439c29333 100644 --- a/types/wlr_switch.c +++ b/types/wlr_switch.c @@ -25,4 +25,6 @@ void wlr_switch_init(struct wlr_switch *switch_device, void wlr_switch_finish(struct wlr_switch *switch_device) { wlr_input_device_finish(&switch_device->base); + + assert(wl_list_empty(&switch_device->events.toggle.listener_list)); } diff --git a/types/wlr_tablet_pad.c b/types/wlr_tablet_pad.c index 2c4c04ac3..bfe17e68d 100644 --- a/types/wlr_tablet_pad.c +++ b/types/wlr_tablet_pad.c @@ -33,6 +33,11 @@ void wlr_tablet_pad_init(struct wlr_tablet_pad *pad, void wlr_tablet_pad_finish(struct wlr_tablet_pad *pad) { wlr_input_device_finish(&pad->base); + assert(wl_list_empty(&pad->events.button.listener_list)); + assert(wl_list_empty(&pad->events.ring.listener_list)); + assert(wl_list_empty(&pad->events.strip.listener_list)); + assert(wl_list_empty(&pad->events.attach_tablet.listener_list)); + char **path_ptr; wl_array_for_each(path_ptr, &pad->paths) { free(*path_ptr); diff --git a/types/wlr_tablet_tool.c b/types/wlr_tablet_tool.c index 77f4631e8..2b15f57ee 100644 --- a/types/wlr_tablet_tool.c +++ b/types/wlr_tablet_tool.c @@ -24,12 +24,18 @@ void wlr_tablet_init(struct wlr_tablet *tablet, wl_signal_init(&tablet->events.proximity); wl_signal_init(&tablet->events.tip); wl_signal_init(&tablet->events.button); + wl_array_init(&tablet->paths); } void wlr_tablet_finish(struct wlr_tablet *tablet) { wlr_input_device_finish(&tablet->base); + assert(wl_list_empty(&tablet->events.axis.listener_list)); + assert(wl_list_empty(&tablet->events.proximity.listener_list)); + assert(wl_list_empty(&tablet->events.tip.listener_list)); + assert(wl_list_empty(&tablet->events.button.listener_list)); + char **path_ptr; wl_array_for_each(path_ptr, &tablet->paths) { free(*path_ptr); diff --git a/types/wlr_tearing_control_v1.c b/types/wlr_tearing_control_v1.c index e5a88fec5..6ba0b2740 100644 --- a/types/wlr_tearing_control_v1.c +++ b/types/wlr_tearing_control_v1.c @@ -34,6 +34,9 @@ static void destroy_tearing_hint(struct wlr_tearing_control_v1 *hint) { wl_signal_emit_mutable(&hint->events.destroy, NULL); + assert(wl_list_empty(&hint->events.set_hint.listener_list)); + assert(wl_list_empty(&hint->events.destroy.listener_list)); + wl_list_remove(&hint->link); wl_resource_set_user_data(hint->resource, NULL); @@ -170,6 +173,9 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { wl_signal_emit_mutable(&manager->events.destroy, NULL); + assert(wl_list_empty(&manager->events.new_object.listener_list)); + assert(wl_list_empty(&manager->events.destroy.listener_list)); + struct wlr_tearing_control_v1 *hint, *tmp; wl_list_for_each_safe(hint, tmp, &manager->surface_hints, link) { destroy_tearing_hint(hint); diff --git a/types/wlr_text_input_v3.c b/types/wlr_text_input_v3.c index 0b05ca656..6c9f07964 100644 --- a/types/wlr_text_input_v3.c +++ b/types/wlr_text_input_v3.c @@ -65,6 +65,12 @@ void wlr_text_input_v3_send_done(struct wlr_text_input_v3 *text_input) { static void wlr_text_input_destroy(struct wlr_text_input_v3 *text_input) { wl_signal_emit_mutable(&text_input->events.destroy, text_input); + + assert(wl_list_empty(&text_input->events.enable.listener_list)); + assert(wl_list_empty(&text_input->events.commit.listener_list)); + assert(wl_list_empty(&text_input->events.disable.listener_list)); + assert(wl_list_empty(&text_input->events.destroy.listener_list)); + text_input_clear_focused_surface(text_input); wl_list_remove(&text_input->seat_destroy.link); // remove from manager.text_inputs @@ -308,6 +314,10 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_text_input_manager_v3 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); + + assert(wl_list_empty(&manager->events.text_input.listener_list)); + assert(wl_list_empty(&manager->events.destroy.listener_list)); + wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); @@ -321,6 +331,7 @@ struct wlr_text_input_manager_v3 *wlr_text_input_manager_v3_create( } wl_list_init(&manager->text_inputs); + wl_signal_init(&manager->events.text_input); wl_signal_init(&manager->events.destroy); diff --git a/types/wlr_touch.c b/types/wlr_touch.c index 66b35e544..f8c4175fb 100644 --- a/types/wlr_touch.c +++ b/types/wlr_touch.c @@ -30,5 +30,11 @@ void wlr_touch_init(struct wlr_touch *touch, void wlr_touch_finish(struct wlr_touch *touch) { wlr_input_device_finish(&touch->base); + assert(wl_list_empty(&touch->events.down.listener_list)); + assert(wl_list_empty(&touch->events.up.listener_list)); + assert(wl_list_empty(&touch->events.motion.listener_list)); + assert(wl_list_empty(&touch->events.cancel.listener_list)); + assert(wl_list_empty(&touch->events.frame.listener_list)); + free(touch->output_name); } diff --git a/types/wlr_transient_seat_v1.c b/types/wlr_transient_seat_v1.c index 717b36dc0..af8f87ccf 100644 --- a/types/wlr_transient_seat_v1.c +++ b/types/wlr_transient_seat_v1.c @@ -115,6 +115,10 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_transient_seat_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, NULL); + + assert(wl_list_empty(&manager->events.destroy.listener_list)); + assert(wl_list_empty(&manager->events.create_seat.listener_list)); + wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); diff --git a/types/wlr_viewporter.c b/types/wlr_viewporter.c index 38c204380..4a755113a 100644 --- a/types/wlr_viewporter.c +++ b/types/wlr_viewporter.c @@ -230,6 +230,9 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_viewporter *viewporter = wl_container_of(listener, viewporter, display_destroy); wl_signal_emit_mutable(&viewporter->events.destroy, NULL); + + assert(wl_list_empty(&viewporter->events.destroy.listener_list)); + wl_global_destroy(viewporter->global); free(viewporter); } diff --git a/types/wlr_virtual_keyboard_v1.c b/types/wlr_virtual_keyboard_v1.c index 8a6d107d5..e7dcb3ec3 100644 --- a/types/wlr_virtual_keyboard_v1.c +++ b/types/wlr_virtual_keyboard_v1.c @@ -209,6 +209,10 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_virtual_keyboard_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); + + assert(wl_list_empty(&manager->events.new_virtual_keyboard.listener_list)); + assert(wl_list_empty(&manager->events.destroy.listener_list)); + wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); diff --git a/types/wlr_virtual_pointer_v1.c b/types/wlr_virtual_pointer_v1.c index 812e7d565..b867c8fb1 100644 --- a/types/wlr_virtual_pointer_v1.c +++ b/types/wlr_virtual_pointer_v1.c @@ -308,6 +308,10 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_virtual_pointer_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); + + assert(wl_list_empty(&manager->events.new_virtual_pointer.listener_list)); + assert(wl_list_empty(&manager->events.destroy.listener_list)); + wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); struct wlr_virtual_pointer_v1 *pointer, *pointer_tmp; @@ -329,6 +333,7 @@ struct wlr_virtual_pointer_manager_v1* wlr_virtual_pointer_manager_v1_create( wl_signal_init(&manager->events.new_virtual_pointer); wl_signal_init(&manager->events.destroy); + manager->global = wl_global_create(display, &zwlr_virtual_pointer_manager_v1_interface, 2, manager, virtual_pointer_manager_bind); diff --git a/types/wlr_xdg_activation_v1.c b/types/wlr_xdg_activation_v1.c index 8e0fb0618..4645ccc0e 100644 --- a/types/wlr_xdg_activation_v1.c +++ b/types/wlr_xdg_activation_v1.c @@ -33,6 +33,8 @@ void wlr_xdg_activation_token_v1_destroy( wl_signal_emit_mutable(&token->events.destroy, NULL); + assert(wl_list_empty(&token->events.destroy.listener_list)); + wl_list_remove(&token->link); wl_list_remove(&token->seat_destroy.link); wl_list_remove(&token->surface_destroy.link); @@ -256,6 +258,7 @@ static struct wlr_xdg_activation_token_v1 *activation_token_create( wl_list_init(&token->link); wl_list_init(&token->seat_destroy.link); wl_list_init(&token->surface_destroy.link); + wl_signal_init(&token->events.destroy); token->activation = activation; @@ -340,6 +343,10 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { wl_container_of(listener, activation, display_destroy); wl_signal_emit_mutable(&activation->events.destroy, NULL); + assert(wl_list_empty(&activation->events.destroy.listener_list)); + assert(wl_list_empty(&activation->events.request_activate.listener_list)); + assert(wl_list_empty(&activation->events.new_token.listener_list)); + struct wlr_xdg_activation_token_v1 *token, *token_tmp; wl_list_for_each_safe(token, token_tmp, &activation->tokens, link) { wlr_xdg_activation_token_v1_destroy(token); @@ -359,6 +366,7 @@ struct wlr_xdg_activation_v1 *wlr_xdg_activation_v1_create( activation->token_timeout_msec = 30000; // 30s wl_list_init(&activation->tokens); + wl_signal_init(&activation->events.destroy); wl_signal_init(&activation->events.request_activate); wl_signal_init(&activation->events.new_token); diff --git a/types/wlr_xdg_decoration_v1.c b/types/wlr_xdg_decoration_v1.c index d16ac7c5a..4194942a2 100644 --- a/types/wlr_xdg_decoration_v1.c +++ b/types/wlr_xdg_decoration_v1.c @@ -62,6 +62,10 @@ static void toplevel_decoration_handle_resource_destroy( struct wlr_xdg_toplevel_decoration_v1 *decoration = toplevel_decoration_from_resource(resource); wl_signal_emit_mutable(&decoration->events.destroy, decoration); + + assert(wl_list_empty(&decoration->events.destroy.listener_list)); + assert(wl_list_empty(&decoration->events.request_mode.listener_list)); + wlr_surface_synced_finish(&decoration->synced); wl_list_remove(&decoration->toplevel_destroy.link); wl_list_remove(&decoration->surface_configure.link); @@ -217,6 +221,7 @@ static void decoration_manager_handle_get_toplevel_decoration( decoration->resource); wl_list_init(&decoration->configure_list); + wl_signal_init(&decoration->events.destroy); wl_signal_init(&decoration->events.request_mode); @@ -256,6 +261,10 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_xdg_decoration_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); + + assert(wl_list_empty(&manager->events.new_toplevel_decoration.listener_list)); + assert(wl_list_empty(&manager->events.destroy.listener_list)); + wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); @@ -275,6 +284,7 @@ struct wlr_xdg_decoration_manager_v1 * return NULL; } wl_list_init(&manager->decorations); + wl_signal_init(&manager->events.new_toplevel_decoration); wl_signal_init(&manager->events.destroy); diff --git a/types/wlr_xdg_dialog_v1.c b/types/wlr_xdg_dialog_v1.c index f1eac0207..dc9cb2470 100644 --- a/types/wlr_xdg_dialog_v1.c +++ b/types/wlr_xdg_dialog_v1.c @@ -76,6 +76,9 @@ static void dialog_destroy(struct wlr_xdg_dialog_v1 *dialog) { wl_signal_emit_mutable(&dialog->events.destroy, NULL); + assert(wl_list_empty(&dialog->events.destroy.listener_list)); + assert(wl_list_empty(&dialog->events.set_modal.listener_list)); + wlr_addon_finish(&dialog->surface_addon); wl_list_remove(&dialog->xdg_toplevel_destroy.link); @@ -154,6 +157,9 @@ static void xdg_wm_dialog_handle_display_destroy(struct wl_listener *listener, v struct wlr_xdg_wm_dialog_v1 *wm = wl_container_of(listener, wm, display_destroy); wl_signal_emit_mutable(&wm->events.destroy, NULL); + assert(wl_list_empty(&wm->events.destroy.listener_list)); + assert(wl_list_empty(&wm->events.new_dialog.listener_list)); + wl_list_remove(&wm->display_destroy.link); wl_global_destroy(wm->global); free(wm); diff --git a/types/wlr_xdg_foreign_registry.c b/types/wlr_xdg_foreign_registry.c index c27d84069..ef9c3135c 100644 --- a/types/wlr_xdg_foreign_registry.c +++ b/types/wlr_xdg_foreign_registry.c @@ -17,6 +17,7 @@ bool wlr_xdg_foreign_exported_init( wl_list_insert(®istry->exported_surfaces, &exported->link); wl_signal_init(&exported->events.destroy); + return true; } @@ -38,6 +39,9 @@ struct wlr_xdg_foreign_exported *wlr_xdg_foreign_registry_find_by_handle( void wlr_xdg_foreign_exported_finish(struct wlr_xdg_foreign_exported *surface) { wl_signal_emit_mutable(&surface->events.destroy, NULL); + + assert(wl_list_empty(&surface->events.destroy.listener_list)); + surface->registry = NULL; wl_list_remove(&surface->link); wl_list_init(&surface->link); @@ -50,6 +54,8 @@ static void foreign_registry_handle_display_destroy(struct wl_listener *listener wl_signal_emit_mutable(®istry->events.destroy, NULL); + assert(wl_list_empty(®istry->events.destroy.listener_list)); + // Implementations are supposed to remove all surfaces assert(wl_list_empty(®istry->exported_surfaces)); free(registry); @@ -67,6 +73,8 @@ struct wlr_xdg_foreign_registry *wlr_xdg_foreign_registry_create( wl_display_add_destroy_listener(display, ®istry->display_destroy); wl_list_init(®istry->exported_surfaces); + wl_signal_init(®istry->events.destroy); + return registry; } diff --git a/types/wlr_xdg_foreign_v1.c b/types/wlr_xdg_foreign_v1.c index 9fb2b235f..1c5983951 100644 --- a/types/wlr_xdg_foreign_v1.c +++ b/types/wlr_xdg_foreign_v1.c @@ -345,6 +345,9 @@ static void xdg_foreign_destroy(struct wlr_xdg_foreign_v1 *foreign) { } wl_signal_emit_mutable(&foreign->events.destroy, NULL); + + assert(wl_list_empty(&foreign->events.destroy.listener_list)); + wl_list_remove(&foreign->foreign_registry_destroy.link); wl_list_remove(&foreign->display_destroy.link); @@ -395,6 +398,7 @@ struct wlr_xdg_foreign_v1 *wlr_xdg_foreign_v1_create( foreign->registry = registry; wl_signal_init(&foreign->events.destroy); + wl_list_init(&foreign->exporter.objects); wl_list_init(&foreign->importer.objects); diff --git a/types/wlr_xdg_foreign_v2.c b/types/wlr_xdg_foreign_v2.c index eef93175e..8ea2af2a8 100644 --- a/types/wlr_xdg_foreign_v2.c +++ b/types/wlr_xdg_foreign_v2.c @@ -348,6 +348,9 @@ static void xdg_foreign_destroy(struct wlr_xdg_foreign_v2 *foreign) { } wl_signal_emit_mutable(&foreign->events.destroy, NULL); + + assert(wl_list_empty(&foreign->events.destroy.listener_list)); + wl_list_remove(&foreign->foreign_registry_destroy.link); wl_list_remove(&foreign->display_destroy.link); @@ -398,6 +401,7 @@ struct wlr_xdg_foreign_v2 *wlr_xdg_foreign_v2_create( foreign->registry = registry; wl_signal_init(&foreign->events.destroy); + wl_list_init(&foreign->exporter.objects); wl_list_init(&foreign->importer.objects); diff --git a/types/wlr_xdg_output_v1.c b/types/wlr_xdg_output_v1.c index 895a0f0aa..262123987 100644 --- a/types/wlr_xdg_output_v1.c +++ b/types/wlr_xdg_output_v1.c @@ -140,7 +140,7 @@ static void output_manager_handle_get_xdg_output(struct wl_client *client, output_send_details(xdg_output, xdg_output_resource); uint32_t wl_version = wl_resource_get_version(output_resource); - if (wl_version >= WL_OUTPUT_DONE_SINCE_VERSION && + if (wl_version >= WL_OUTPUT_DONE_SINCE_VERSION && xdg_version >= OUTPUT_DONE_DEPRECATED_SINCE_VERSION) { wl_output_send_done(output_resource); } @@ -234,7 +234,11 @@ static void manager_destroy(struct wlr_xdg_output_manager_v1 *manager) { wl_list_for_each_safe(output, tmp, &manager->outputs, link) { output_destroy(output); } + wl_signal_emit_mutable(&manager->events.destroy, manager); + + assert(wl_list_empty(&manager->events.destroy.listener_list)); + wl_list_remove(&manager->display_destroy.link); wl_list_remove(&manager->layout_add.link); wl_list_remove(&manager->layout_change.link); diff --git a/types/wlr_xdg_system_bell_v1.c b/types/wlr_xdg_system_bell_v1.c index 8a8850540..3cabf6559 100644 --- a/types/wlr_xdg_system_bell_v1.c +++ b/types/wlr_xdg_system_bell_v1.c @@ -52,6 +52,10 @@ static void bell_bind(struct wl_client *client, void *data, static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_xdg_system_bell_v1 *bell = wl_container_of(listener, bell, display_destroy); wl_signal_emit_mutable(&bell->events.destroy, NULL); + + assert(wl_list_empty(&bell->events.destroy.listener_list)); + assert(wl_list_empty(&bell->events.ring.listener_list)); + wl_list_remove(&bell->display_destroy.link); wl_global_destroy(bell->global); free(bell); diff --git a/types/wlr_xdg_toplevel_icon_v1.c b/types/wlr_xdg_toplevel_icon_v1.c index 0995e6d1c..5927b8168 100644 --- a/types/wlr_xdg_toplevel_icon_v1.c +++ b/types/wlr_xdg_toplevel_icon_v1.c @@ -229,6 +229,9 @@ static void manager_handle_display_destroy(struct wl_listener *listener, void *d wl_signal_emit_mutable(&manager->events.destroy, NULL); + assert(wl_list_empty(&manager->events.set_icon.listener_list)); + assert(wl_list_empty(&manager->events.destroy.listener_list)); + wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); diff --git a/types/xdg_shell/wlr_xdg_popup.c b/types/xdg_shell/wlr_xdg_popup.c index df1e75de3..25c07c8c5 100644 --- a/types/xdg_shell/wlr_xdg_popup.c +++ b/types/xdg_shell/wlr_xdg_popup.c @@ -467,6 +467,9 @@ void destroy_xdg_popup(struct wlr_xdg_popup *popup) { wl_signal_emit_mutable(&popup->events.destroy, NULL); + assert(wl_list_empty(&popup->events.destroy.listener_list)); + assert(wl_list_empty(&popup->events.reposition.listener_list)); + wlr_surface_synced_finish(&popup->synced); popup->base->popup = NULL; wl_list_remove(&popup->link); diff --git a/types/xdg_shell/wlr_xdg_shell.c b/types/xdg_shell/wlr_xdg_shell.c index 3baea04eb..0cbadc271 100644 --- a/types/xdg_shell/wlr_xdg_shell.c +++ b/types/xdg_shell/wlr_xdg_shell.c @@ -129,6 +129,12 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_xdg_shell *xdg_shell = wl_container_of(listener, xdg_shell, display_destroy); wl_signal_emit_mutable(&xdg_shell->events.destroy, xdg_shell); + + assert(wl_list_empty(&xdg_shell->events.new_surface.listener_list)); + assert(wl_list_empty(&xdg_shell->events.new_toplevel.listener_list)); + assert(wl_list_empty(&xdg_shell->events.new_popup.listener_list)); + assert(wl_list_empty(&xdg_shell->events.destroy.listener_list)); + wl_list_remove(&xdg_shell->display_destroy.link); wl_global_destroy(xdg_shell->global); free(xdg_shell); diff --git a/types/xdg_shell/wlr_xdg_surface.c b/types/xdg_shell/wlr_xdg_surface.c index 97daa0d30..a752ba316 100644 --- a/types/xdg_shell/wlr_xdg_surface.c +++ b/types/xdg_shell/wlr_xdg_surface.c @@ -523,6 +523,12 @@ void destroy_xdg_surface(struct wlr_xdg_surface *surface) { wl_signal_emit_mutable(&surface->events.destroy, NULL); + assert(wl_list_empty(&surface->events.destroy.listener_list)); + assert(wl_list_empty(&surface->events.ping_timeout.listener_list)); + assert(wl_list_empty(&surface->events.new_popup.listener_list)); + assert(wl_list_empty(&surface->events.configure.listener_list)); + assert(wl_list_empty(&surface->events.ack_configure.listener_list)); + wl_list_remove(&surface->link); wlr_surface_synced_finish(&surface->synced); wl_resource_set_user_data(surface->resource, NULL); diff --git a/types/xdg_shell/wlr_xdg_toplevel.c b/types/xdg_shell/wlr_xdg_toplevel.c index bfb583bcc..f7bc52cc1 100644 --- a/types/xdg_shell/wlr_xdg_toplevel.c +++ b/types/xdg_shell/wlr_xdg_toplevel.c @@ -525,6 +525,17 @@ void destroy_xdg_toplevel(struct wlr_xdg_toplevel *toplevel) { wl_signal_emit_mutable(&toplevel->events.destroy, NULL); + assert(wl_list_empty(&toplevel->events.destroy.listener_list)); + assert(wl_list_empty(&toplevel->events.request_maximize.listener_list)); + assert(wl_list_empty(&toplevel->events.request_fullscreen.listener_list)); + assert(wl_list_empty(&toplevel->events.request_minimize.listener_list)); + assert(wl_list_empty(&toplevel->events.request_move.listener_list)); + assert(wl_list_empty(&toplevel->events.request_resize.listener_list)); + assert(wl_list_empty(&toplevel->events.request_show_window_menu.listener_list)); + assert(wl_list_empty(&toplevel->events.set_parent.listener_list)); + assert(wl_list_empty(&toplevel->events.set_title.listener_list)); + assert(wl_list_empty(&toplevel->events.set_app_id.listener_list)); + wlr_surface_synced_finish(&toplevel->synced); toplevel->base->toplevel = NULL; wl_resource_set_user_data(toplevel->resource, NULL); diff --git a/xwayland/server.c b/xwayland/server.c index 417bbe54b..0e8ad44fe 100644 --- a/xwayland/server.c +++ b/xwayland/server.c @@ -455,7 +455,13 @@ void wlr_xwayland_server_destroy(struct wlr_xwayland_server *server) { } server_finish_process(server); server_finish_display(server); + wl_signal_emit_mutable(&server->events.destroy, NULL); + + assert(wl_list_empty(&server->events.start.listener_list)); + assert(wl_list_empty(&server->events.ready.listener_list)); + assert(wl_list_empty(&server->events.destroy.listener_list)); + free(server); } diff --git a/xwayland/shell.c b/xwayland/shell.c index 1f7e11aff..f048eb627 100644 --- a/xwayland/shell.c +++ b/xwayland/shell.c @@ -178,6 +178,7 @@ struct wlr_xwayland_shell_v1 *wlr_xwayland_shell_v1_create( } wl_list_init(&shell->surfaces); + wl_signal_init(&shell->events.new_surface); wl_signal_init(&shell->events.destroy); @@ -196,6 +197,9 @@ void wlr_xwayland_shell_v1_destroy(struct wlr_xwayland_shell_v1 *shell) { wl_signal_emit_mutable(&shell->events.destroy, NULL); + assert(wl_list_empty(&shell->events.new_surface.listener_list)); + assert(wl_list_empty(&shell->events.destroy.listener_list)); + struct wlr_xwayland_surface_v1 *xwl_surface, *tmp; wl_list_for_each_safe(xwl_surface, tmp, &shell->surfaces, link) { xwl_surface_destroy(xwl_surface); diff --git a/xwayland/xwayland.c b/xwayland/xwayland.c index 7fbc33ce1..7cef4cc8f 100644 --- a/xwayland/xwayland.c +++ b/xwayland/xwayland.c @@ -81,6 +81,11 @@ void wlr_xwayland_destroy(struct wlr_xwayland *xwayland) { wl_signal_emit_mutable(&xwayland->events.destroy, NULL); + assert(wl_list_empty(&xwayland->events.destroy.listener_list)); + assert(wl_list_empty(&xwayland->events.new_surface.listener_list)); + assert(wl_list_empty(&xwayland->events.ready.listener_list)); + assert(wl_list_empty(&xwayland->events.remove_startup_info.listener_list)); + wl_list_remove(&xwayland->server_destroy.link); wl_list_remove(&xwayland->server_start.link); wl_list_remove(&xwayland->server_ready.link); diff --git a/xwayland/xwm.c b/xwayland/xwm.c index e77ad1a11..08faed9b6 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -209,6 +209,7 @@ static struct wlr_xwayland_surface *xwayland_surface_create( wl_list_init(&surface->stack_link); wl_list_init(&surface->parent_link); wl_list_init(&surface->unpaired_link); + wl_signal_init(&surface->events.destroy); wl_signal_init(&surface->events.request_configure); wl_signal_init(&surface->events.request_move); @@ -569,6 +570,40 @@ static void xwayland_surface_destroy(struct wlr_xwayland_surface *xsurface) { wl_signal_emit_mutable(&xsurface->events.destroy, NULL); + assert(wl_list_empty(&xsurface->events.destroy.listener_list)); + assert(wl_list_empty(&xsurface->events.request_configure.listener_list)); + assert(wl_list_empty(&xsurface->events.request_move.listener_list)); + assert(wl_list_empty(&xsurface->events.request_resize.listener_list)); + assert(wl_list_empty(&xsurface->events.request_minimize.listener_list)); + assert(wl_list_empty(&xsurface->events.request_maximize.listener_list)); + assert(wl_list_empty(&xsurface->events.request_fullscreen.listener_list)); + assert(wl_list_empty(&xsurface->events.request_activate.listener_list)); + assert(wl_list_empty(&xsurface->events.request_close.listener_list)); + assert(wl_list_empty(&xsurface->events.request_sticky.listener_list)); + assert(wl_list_empty(&xsurface->events.request_shaded.listener_list)); + assert(wl_list_empty(&xsurface->events.request_skip_taskbar.listener_list)); + assert(wl_list_empty(&xsurface->events.request_skip_pager.listener_list)); + assert(wl_list_empty(&xsurface->events.request_above.listener_list)); + assert(wl_list_empty(&xsurface->events.request_below.listener_list)); + assert(wl_list_empty(&xsurface->events.request_demands_attention.listener_list)); + assert(wl_list_empty(&xsurface->events.associate.listener_list)); + assert(wl_list_empty(&xsurface->events.dissociate.listener_list)); + assert(wl_list_empty(&xsurface->events.set_class.listener_list)); + assert(wl_list_empty(&xsurface->events.set_role.listener_list)); + assert(wl_list_empty(&xsurface->events.set_title.listener_list)); + assert(wl_list_empty(&xsurface->events.set_parent.listener_list)); + assert(wl_list_empty(&xsurface->events.set_startup_id.listener_list)); + assert(wl_list_empty(&xsurface->events.set_window_type.listener_list)); + assert(wl_list_empty(&xsurface->events.set_hints.listener_list)); + assert(wl_list_empty(&xsurface->events.set_decorations.listener_list)); + assert(wl_list_empty(&xsurface->events.set_strut_partial.listener_list)); + assert(wl_list_empty(&xsurface->events.set_override_redirect.listener_list)); + assert(wl_list_empty(&xsurface->events.set_geometry.listener_list)); + assert(wl_list_empty(&xsurface->events.focus_in.listener_list)); + assert(wl_list_empty(&xsurface->events.grab_focus.listener_list)); + assert(wl_list_empty(&xsurface->events.map_request.listener_list)); + assert(wl_list_empty(&xsurface->events.ping_timeout.listener_list)); + if (xsurface == xsurface->xwm->focus_surface) { xwm_surface_activate(xsurface->xwm, NULL); } From 9ab87167b548e3281eaacf64c9a44596a1036925 Mon Sep 17 00:00:00 2001 From: llyyr Date: Thu, 16 Jan 2025 19:10:11 +0530 Subject: [PATCH 258/519] backend/drm: don't leak mgpu_formats --- backend/drm/backend.c | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/drm/backend.c b/backend/drm/backend.c index fa6dd2625..9d9d5c229 100644 --- a/backend/drm/backend.c +++ b/backend/drm/backend.c @@ -55,6 +55,7 @@ static void backend_destroy(struct wlr_backend *backend) { wl_list_remove(&drm->dev_remove.link); if (drm->mgpu_renderer.wlr_rend) { + wlr_drm_format_set_finish(&drm->mgpu_formats); finish_drm_renderer(&drm->mgpu_renderer); } From e3596abc9abc924622e969490b7d11e3d8b715cb Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Wed, 8 Jan 2025 18:47:52 +0300 Subject: [PATCH 259/519] pointer-constraints: fix deactivating oneshot constraint on destroy --- include/wlr/types/wlr_pointer_constraints_v1.h | 2 ++ types/wlr_pointer_constraints_v1.c | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/include/wlr/types/wlr_pointer_constraints_v1.h b/include/wlr/types/wlr_pointer_constraints_v1.h index 4c541d879..a1fa88633 100644 --- a/include/wlr/types/wlr_pointer_constraints_v1.h +++ b/include/wlr/types/wlr_pointer_constraints_v1.h @@ -70,6 +70,8 @@ struct wlr_pointer_constraint_v1 { struct wl_listener seat_destroy; struct wlr_surface_synced synced; + + bool destroying; } WLR_PRIVATE; }; diff --git a/types/wlr_pointer_constraints_v1.c b/types/wlr_pointer_constraints_v1.c index 94cb48f24..7bc62311b 100644 --- a/types/wlr_pointer_constraints_v1.c +++ b/types/wlr_pointer_constraints_v1.c @@ -39,10 +39,15 @@ static void resource_destroy(struct wl_client *client, } static void pointer_constraint_destroy(struct wlr_pointer_constraint_v1 *constraint) { - if (constraint == NULL) { + if (constraint == NULL || constraint->destroying) { return; } + // Calling wlr_pointer_constraint_v1_send_deactivated() for a oneshot constraint + // that is being destroyed results in another pointer_constraint_destroy() call. + // Avoid finalizing the state twice by setting a flag. + constraint->destroying = true; + wlr_log(WLR_DEBUG, "destroying constraint %p", constraint); wl_signal_emit_mutable(&constraint->events.destroy, constraint); From c3224d41604d866458631ac717ec81828fcc4890 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Tue, 7 Jan 2025 21:34:00 +0300 Subject: [PATCH 260/519] compositor: add wlr_surface_synced commit hook --- include/wlr/types/wlr_compositor.h | 7 +++++++ types/wlr_compositor.c | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/include/wlr/types/wlr_compositor.h b/include/wlr/types/wlr_compositor.h index d26f8e2ec..63a8c16ce 100644 --- a/include/wlr/types/wlr_compositor.h +++ b/include/wlr/types/wlr_compositor.h @@ -484,6 +484,8 @@ void wlr_surface_set_preferred_buffer_scale(struct wlr_surface *surface, void wlr_surface_set_preferred_buffer_transform(struct wlr_surface *surface, enum wl_output_transform transform); +struct wlr_surface_synced; + /** * Implementation for struct wlr_surface_synced. * @@ -500,6 +502,11 @@ struct wlr_surface_synced_impl { void (*finish_state)(void *state); // Move a state. If NULL, memcpy() is used. void (*move_state)(void *dst, void *src); + + // Called when the state is committed. If NULL, this is a no-op. + // If an object is a surface role object which has state synchronized with + // the surface state, the role commit hook should be preferred over this. + void (*commit)(struct wlr_surface_synced *synced); }; /** diff --git a/types/wlr_compositor.c b/types/wlr_compositor.c index afa35ec99..7bc8ea473 100644 --- a/types/wlr_compositor.c +++ b/types/wlr_compositor.c @@ -552,6 +552,13 @@ static void surface_commit_state(struct wlr_surface *surface, surface->pending.seq++; } + struct wlr_surface_synced *synced; + wl_list_for_each(synced, &surface->synced, link) { + if (synced->impl->commit) { + synced->impl->commit(synced); + } + } + if (surface->role != NULL && surface->role->commit != NULL && (surface->role_resource != NULL || surface->role->no_object)) { surface->role->commit(surface); From f95270bb5e517a3985e0c155d0920aa75b6c7e72 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Wed, 8 Jan 2025 12:15:49 +0300 Subject: [PATCH 261/519] pointer-constraints: rewrite doc comments --- include/wlr/types/wlr_pointer_constraints_v1.h | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/include/wlr/types/wlr_pointer_constraints_v1.h b/include/wlr/types/wlr_pointer_constraints_v1.h index a1fa88633..f97413e22 100644 --- a/include/wlr/types/wlr_pointer_constraints_v1.h +++ b/include/wlr/types/wlr_pointer_constraints_v1.h @@ -55,8 +55,7 @@ struct wlr_pointer_constraint_v1 { struct { /** - * Called when a pointer constraint's region is updated, - * post-surface-commit. + * Emitted when a pointer constraint's region is updated. */ struct wl_signal set_region; struct wl_signal destroy; @@ -81,13 +80,7 @@ struct wlr_pointer_constraints_v1 { struct { struct wl_signal destroy; - - /** - * Called when a new pointer constraint is created. - * - * The data pointer is a struct wlr_pointer_constraint_v1. - */ - struct wl_signal new_constraint; + struct wl_signal new_constraint; // struct wlr_pointer_constraint_v1 } events; void *data; From b25f98d583871173f5586c09e78ddcaa7caf6486 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Wed, 8 Jan 2025 12:16:24 +0300 Subject: [PATCH 262/519] pointer-constraints: use wlr_surface_synced.commit hook This fixes a problem where an outdated surface input region was used to compute the effective confinement region. Additionally, this commit fixes a bug in pointer_constraint_create() which caused the initial region to not be applied immediately. This is a breaking change: set_region is now emitted before the role commit hook is called, and it's not emitted if the region hasn't actually changed. --- .../wlr/types/wlr_pointer_constraints_v1.h | 1 - types/wlr_pointer_constraints_v1.c | 67 ++++++++++--------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/include/wlr/types/wlr_pointer_constraints_v1.h b/include/wlr/types/wlr_pointer_constraints_v1.h index f97413e22..bccb23090 100644 --- a/include/wlr/types/wlr_pointer_constraints_v1.h +++ b/include/wlr/types/wlr_pointer_constraints_v1.h @@ -64,7 +64,6 @@ struct wlr_pointer_constraint_v1 { void *data; struct { - struct wl_listener surface_commit; struct wl_listener surface_destroy; struct wl_listener seat_destroy; diff --git a/types/wlr_pointer_constraints_v1.c b/types/wlr_pointer_constraints_v1.c index 7bc62311b..42329da82 100644 --- a/types/wlr_pointer_constraints_v1.c +++ b/types/wlr_pointer_constraints_v1.c @@ -58,7 +58,6 @@ static void pointer_constraint_destroy(struct wlr_pointer_constraint_v1 *constra wl_resource_set_user_data(constraint->resource, NULL); wlr_surface_synced_finish(&constraint->synced); wl_list_remove(&constraint->link); - wl_list_remove(&constraint->surface_commit.link); wl_list_remove(&constraint->surface_destroy.link); wl_list_remove(&constraint->seat_destroy.link); pixman_region32_fini(&constraint->region); @@ -72,19 +71,6 @@ static void pointer_constraint_destroy_resource(struct wl_resource *resource) { pointer_constraint_destroy(constraint); } -static void pointer_constraint_set_region( - struct wlr_pointer_constraint_v1 *constraint, - struct wl_resource *region_resource) { - pixman_region32_clear(&constraint->pending.region); - - if (region_resource) { - const pixman_region32_t *region = wlr_region_from_resource(region_resource); - pixman_region32_copy(&constraint->pending.region, region); - } - - constraint->pending.committed |= WLR_POINTER_CONSTRAINT_V1_STATE_REGION; -} - static void pointer_constraint_handle_set_region(struct wl_client *client, struct wl_resource *resource, struct wl_resource *region_resource) { struct wlr_pointer_constraint_v1 *constraint = @@ -93,7 +79,13 @@ static void pointer_constraint_handle_set_region(struct wl_client *client, return; } - pointer_constraint_set_region(constraint, region_resource); + pixman_region32_clear(&constraint->pending.region); + if (region_resource) { + const pixman_region32_t *region = wlr_region_from_resource(region_resource); + pixman_region32_copy(&constraint->pending.region, region); + } + + constraint->pending.committed |= WLR_POINTER_CONSTRAINT_V1_STATE_REGION; } static void pointer_constraint_set_cursor_position_hint(struct wl_client *client, @@ -110,27 +102,27 @@ static void pointer_constraint_set_cursor_position_hint(struct wl_client *client constraint->pending.committed |= WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT; } -static void pointer_constraint_commit( - struct wlr_pointer_constraint_v1 *constraint) { - pixman_region32_clear(&constraint->region); +// Returns true if the region has changed +static bool update_region(struct wlr_pointer_constraint_v1 *constraint) { + pixman_region32_t region; + pixman_region32_init(®ion); + if (pixman_region32_not_empty(&constraint->current.region)) { - pixman_region32_intersect(&constraint->region, + pixman_region32_intersect(®ion, &constraint->surface->input_region, &constraint->current.region); } else { - pixman_region32_copy(&constraint->region, - &constraint->surface->input_region); + pixman_region32_copy(®ion, &constraint->surface->input_region); } - if (constraint->current.committed & WLR_POINTER_CONSTRAINT_V1_STATE_REGION) { - wl_signal_emit_mutable(&constraint->events.set_region, NULL); + if (pixman_region32_equal(®ion, &constraint->region)) { + pixman_region32_fini(®ion); + return false; } -} -static void handle_surface_commit(struct wl_listener *listener, void *data) { - struct wlr_pointer_constraint_v1 *constraint = - wl_container_of(listener, constraint, surface_commit); + pixman_region32_fini(&constraint->region); + constraint->region = region; - pointer_constraint_commit(constraint); + return true; } static void handle_surface_destroy(struct wl_listener *listener, void *data) { @@ -182,11 +174,20 @@ static void surface_synced_move_state(void *_dst, void *_src) { src->committed = 0; } +static void surface_synced_commit(struct wlr_surface_synced *synced) { + struct wlr_pointer_constraint_v1 *constraint = wl_container_of(synced, constraint, synced); + + if (update_region(constraint)) { + wl_signal_emit_mutable(&constraint->events.set_region, NULL); + } +} + static const struct wlr_surface_synced_impl surface_synced_impl = { .state_size = sizeof(struct wlr_pointer_constraint_v1_state), .init_state = surface_synced_init_state, .finish_state = surface_synced_finish_state, .move_state = surface_synced_move_state, + .commit = surface_synced_commit, }; static void pointer_constraint_create(struct wl_client *client, @@ -264,11 +265,11 @@ static void pointer_constraint_create(struct wl_client *client, pixman_region32_init(&constraint->region); - pointer_constraint_set_region(constraint, region_resource); - pointer_constraint_commit(constraint); - - constraint->surface_commit.notify = handle_surface_commit; - wl_signal_add(&surface->events.commit, &constraint->surface_commit); + if (region_resource) { + pixman_region32_copy(&constraint->current.region, + wlr_region_from_resource(region_resource)); + update_region(constraint); + } constraint->surface_destroy.notify = handle_surface_destroy; wl_signal_add(&surface->events.destroy, &constraint->surface_destroy); From 980ac9e4c8a11c6d52ec754b429d5061e05b1de1 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Mon, 20 Jan 2025 16:17:12 +0300 Subject: [PATCH 263/519] CONTRIBUTING.md: mention listener list assertions --- CONTRIBUTING.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f740c9359..aea7d6960 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -213,6 +213,27 @@ reinitialized to be used again. it, and free the memory. Such functions should always be able to accept a NULL pointer. +If the object has signals, the destructor function must assert that their +listener lists are empty. + +```c +void wlr_thing_init(struct wlr_thing *thing) { + *thing = (struct wlr_thing){ + // ... + }; + + wl_signal_init(&thing->events.destroy); + wl_signal_init(&thing->events.foo); +} + +void wlr_thing_finish(struct wlr_thing *thing) { + wl_signal_emit_mutable(&thing->events.destroy, NULL); + + assert(wl_list_empty(&thing->events.destroy.listener_list)); + assert(wl_list_empty(&thing->events.foo.listener_list)); +} +``` + ### Error Codes For functions not returning a value, they should return a (stdbool.h) bool to From 4f6dd01e5a39b2efcb7e88f5ffbf7ec0a83b8b6b Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Fri, 24 Jan 2025 21:46:02 +0300 Subject: [PATCH 264/519] backend/wayland: remove syncobj waiter on buffer destroy --- backend/wayland/output.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/backend/wayland/output.c b/backend/wayland/output.c index c3e4866da..53f4d7772 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -134,6 +134,12 @@ static const struct wp_presentation_feedback_listener .discarded = presentation_feedback_handle_discarded, }; +static void buffer_remove_drm_syncobj_waiter(struct wlr_wl_buffer *buffer) { + wl_list_remove(&buffer->drm_syncobj_ready.link); + wlr_drm_syncobj_timeline_waiter_finish(&buffer->drm_syncobj_waiter); + buffer->has_drm_syncobj_waiter = false; +} + void destroy_wl_buffer(struct wlr_wl_buffer *buffer) { if (buffer == NULL) { return; @@ -141,6 +147,9 @@ void destroy_wl_buffer(struct wlr_wl_buffer *buffer) { wl_list_remove(&buffer->buffer_destroy.link); wl_list_remove(&buffer->link); wl_buffer_destroy(buffer->wl_buffer); + if (buffer->has_drm_syncobj_waiter) { + buffer_remove_drm_syncobj_waiter(buffer); + } if (!buffer->released) { wlr_buffer_unlock(buffer->buffer); } @@ -170,11 +179,7 @@ static const struct wl_buffer_listener buffer_listener = { static void buffer_handle_drm_syncobj_ready(struct wl_listener *listener, void *data) { struct wlr_wl_buffer *buffer = wl_container_of(listener, buffer, drm_syncobj_ready); - - wl_list_remove(&buffer->drm_syncobj_ready.link); - wlr_drm_syncobj_timeline_waiter_finish(&buffer->drm_syncobj_waiter); - buffer->has_drm_syncobj_waiter = false; - + buffer_remove_drm_syncobj_waiter(buffer); buffer_release(buffer); } From 1c2cb4c8020a00367b4175eb82f3349545c0d9ef Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Fri, 24 Jan 2025 21:57:28 +0300 Subject: [PATCH 265/519] drm-syncobj: return false instead of NULL --- render/drm_syncobj.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/render/drm_syncobj.c b/render/drm_syncobj.c index 7d52f4118..af93876d4 100644 --- a/render/drm_syncobj.c +++ b/render/drm_syncobj.c @@ -210,7 +210,7 @@ bool wlr_drm_syncobj_timeline_waiter_init(struct wlr_drm_syncobj_timeline_waiter wlr_log(WLR_ERROR, "eventfd() is unavailable"); #endif if (ev_fd < 0) { - return NULL; + return false; } struct drm_syncobj_eventfd syncobj_eventfd = { @@ -222,14 +222,14 @@ bool wlr_drm_syncobj_timeline_waiter_init(struct wlr_drm_syncobj_timeline_waiter if (drmIoctl(timeline->drm_fd, DRM_IOCTL_SYNCOBJ_EVENTFD, &syncobj_eventfd) != 0) { wlr_log_errno(WLR_ERROR, "DRM_IOCTL_SYNCOBJ_EVENTFD failed"); close(ev_fd); - return NULL; + return false; } struct wl_event_source *source = wl_event_loop_add_fd(loop, ev_fd, WL_EVENT_READABLE, handle_eventfd_ready, waiter); if (source == NULL) { wlr_log(WLR_ERROR, "Failed to add FD to event loop"); close(ev_fd); - return NULL; + return false; } *waiter = (struct wlr_drm_syncobj_timeline_waiter){ From 1ee3ed43102d8a469d3da90f9d64d39431ce4dbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Sat, 25 Jan 2025 23:38:17 +0100 Subject: [PATCH 266/519] buffer: Make wlr_buffer_is_opaque public It's useful for compositors. --- include/types/wlr_buffer.h | 2 +- types/buffer/buffer.c | 2 +- types/scene/wlr_scene.c | 2 +- types/wlr_compositor.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/types/wlr_buffer.h b/include/types/wlr_buffer.h index 947ccde7b..98d5d3aea 100644 --- a/include/types/wlr_buffer.h +++ b/include/types/wlr_buffer.h @@ -57,7 +57,7 @@ bool dmabuf_buffer_drop(struct wlr_dmabuf_buffer *buffer); * reverse is not true: false may be returned in cases where the buffer is fully * opaque. */ -bool buffer_is_opaque(struct wlr_buffer *buffer); +bool wlr_buffer_is_opaque(struct wlr_buffer *buffer); /** * Creates a struct wlr_client_buffer from a given struct wlr_buffer by creating diff --git a/types/buffer/buffer.c b/types/buffer/buffer.c index 2a7576c7e..d56255b0d 100644 --- a/types/buffer/buffer.c +++ b/types/buffer/buffer.c @@ -107,7 +107,7 @@ bool wlr_buffer_get_shm(struct wlr_buffer *buffer, return buffer->impl->get_shm(buffer, attribs); } -bool buffer_is_opaque(struct wlr_buffer *buffer) { +bool wlr_buffer_is_opaque(struct wlr_buffer *buffer) { void *data; uint32_t format; size_t stride; diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 9a6266381..915468773 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -781,7 +781,7 @@ static void scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer, scene_buffer->buffer = wlr_buffer_lock(buffer); scene_buffer->buffer_width = buffer->width; scene_buffer->buffer_height = buffer->height; - scene_buffer->buffer_is_opaque = buffer_is_opaque(buffer); + scene_buffer->buffer_is_opaque = wlr_buffer_is_opaque(buffer); scene_buffer->buffer_release.notify = scene_buffer_handle_buffer_release; wl_signal_add(&buffer->events.release, &scene_buffer->buffer_release); diff --git a/types/wlr_compositor.c b/types/wlr_compositor.c index 7bc8ea473..b956d8dbc 100644 --- a/types/wlr_compositor.c +++ b/types/wlr_compositor.c @@ -415,7 +415,7 @@ static void surface_apply_damage(struct wlr_surface *surface) { return; } - surface->opaque = buffer_is_opaque(surface->current.buffer); + surface->opaque = wlr_buffer_is_opaque(surface->current.buffer); if (surface->buffer != NULL) { if (wlr_client_buffer_apply_damage(surface->buffer, From fa97f7f1f0b27b6dbcf4636b892a6bb52a93d1e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Sun, 26 Jan 2025 00:16:40 +0100 Subject: [PATCH 267/519] buffer: Move wlr_buffer_is_opaque to public header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: 1ee3ed43 ("buffer: Make wlr_buffer_is_opaque public") Signed-off-by: Guido Günther --- include/types/wlr_buffer.h | 9 --------- include/wlr/types/wlr_buffer.h | 9 +++++++++ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/include/types/wlr_buffer.h b/include/types/wlr_buffer.h index 98d5d3aea..9d882d47d 100644 --- a/include/types/wlr_buffer.h +++ b/include/types/wlr_buffer.h @@ -50,15 +50,6 @@ struct wlr_dmabuf_buffer *dmabuf_buffer_create( */ bool dmabuf_buffer_drop(struct wlr_dmabuf_buffer *buffer); -/** - * Check whether a buffer is fully opaque. - * - * When true is returned, the buffer is guaranteed to be fully opaque, but the - * reverse is not true: false may be returned in cases where the buffer is fully - * opaque. - */ -bool wlr_buffer_is_opaque(struct wlr_buffer *buffer); - /** * Creates a struct wlr_client_buffer from a given struct wlr_buffer by creating * a texture from it, and copying its struct wl_resource. diff --git a/include/wlr/types/wlr_buffer.h b/include/wlr/types/wlr_buffer.h index fadc560a3..19a8b73b6 100644 --- a/include/wlr/types/wlr_buffer.h +++ b/include/wlr/types/wlr_buffer.h @@ -109,6 +109,15 @@ bool wlr_buffer_get_shm(struct wlr_buffer *buffer, */ struct wlr_buffer *wlr_buffer_try_from_resource(struct wl_resource *resource); +/** + * Check whether a buffer is fully opaque. + * + * When true is returned, the buffer is guaranteed to be fully opaque, but the + * reverse is not true: false may be returned in cases where the buffer is fully + * opaque. + */ +bool wlr_buffer_is_opaque(struct wlr_buffer *buffer); + /** * Buffer data pointer access flags. */ From 9b55737cf512ae5132b10633b83e099d46fa259f Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 26 Jan 2025 17:46:50 +0100 Subject: [PATCH 268/519] Make wlr_matrix private API 36cc698bc551 ("matrix: deprecate") has deprecated wlr_matrix more than one year ago. It's now time to drop it from our public API. --- include/types/wlr_matrix.h | 35 ++++++++++++++++++++++++++- include/wlr/types/wlr_matrix.h | 44 ---------------------------------- render/gles2/pass.c | 1 - render/gles2/renderer.c | 1 - types/wlr_matrix.c | 1 - 5 files changed, 34 insertions(+), 48 deletions(-) delete mode 100644 include/wlr/types/wlr_matrix.h diff --git a/include/types/wlr_matrix.h b/include/types/wlr_matrix.h index ce599dc1b..e9af2f636 100644 --- a/include/types/wlr_matrix.h +++ b/include/types/wlr_matrix.h @@ -1,7 +1,40 @@ #ifndef TYPES_WLR_MATRIX_H #define TYPES_WLR_MATRIX_H -#include +#include + +struct wlr_box; + +/** Writes the identity matrix into mat */ +void wlr_matrix_identity(float mat[static 9]); + +/** mat ← a × b */ +void wlr_matrix_multiply(float mat[static 9], const float a[static 9], + const float b[static 9]); + +void wlr_matrix_transpose(float mat[static 9], const float a[static 9]); + +/** Writes a 2D translation matrix to mat of magnitude (x, y) */ +void wlr_matrix_translate(float mat[static 9], float x, float y); + +/** Writes a 2D scale matrix to mat of magnitude (x, y) */ +void wlr_matrix_scale(float mat[static 9], float x, float y); + +/** Writes a 2D rotation matrix to mat at an angle of rad radians */ +void wlr_matrix_rotate(float mat[static 9], float rad); + +/** Writes a transformation matrix which applies the specified + * wl_output_transform to mat */ +void wlr_matrix_transform(float mat[static 9], + enum wl_output_transform transform); + +/** Shortcut for the various matrix operations involved in projecting the + * specified wlr_box onto a given orthographic projection with a given + * rotation. The result is written to mat, which can be applied to each + * coordinate of the box to get a new coordinate from [-1,1]. */ +void wlr_matrix_project_box(float mat[static 9], const struct wlr_box *box, + enum wl_output_transform transform, float rotation, + const float projection[static 9]); /** * Writes a 2D orthographic projection matrix to mat of (width, height) with a diff --git a/include/wlr/types/wlr_matrix.h b/include/wlr/types/wlr_matrix.h deleted file mode 100644 index 674cb5382..000000000 --- a/include/wlr/types/wlr_matrix.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * This is a deprecated interface of wlroots. It will be removed in a future - * version. - */ - -#ifndef WLR_TYPES_WLR_MATRIX_H -#define WLR_TYPES_WLR_MATRIX_H - -#include - -struct wlr_box; - -/** Writes the identity matrix into mat */ -void wlr_matrix_identity(float mat[static 9]); - -/** mat ← a × b */ -void wlr_matrix_multiply(float mat[static 9], const float a[static 9], - const float b[static 9]); - -void wlr_matrix_transpose(float mat[static 9], const float a[static 9]); - -/** Writes a 2D translation matrix to mat of magnitude (x, y) */ -void wlr_matrix_translate(float mat[static 9], float x, float y); - -/** Writes a 2D scale matrix to mat of magnitude (x, y) */ -void wlr_matrix_scale(float mat[static 9], float x, float y); - -/** Writes a 2D rotation matrix to mat at an angle of rad radians */ -void wlr_matrix_rotate(float mat[static 9], float rad); - -/** Writes a transformation matrix which applies the specified - * wl_output_transform to mat */ -void wlr_matrix_transform(float mat[static 9], - enum wl_output_transform transform); - -/** Shortcut for the various matrix operations involved in projecting the - * specified wlr_box onto a given orthographic projection with a given - * rotation. The result is written to mat, which can be applied to each - * coordinate of the box to get a new coordinate from [-1,1]. */ -void wlr_matrix_project_box(float mat[static 9], const struct wlr_box *box, - enum wl_output_transform transform, float rotation, - const float projection[static 9]); - -#endif diff --git a/render/gles2/pass.c b/render/gles2/pass.c index 71baf2cec..96b66065e 100644 --- a/render/gles2/pass.c +++ b/render/gles2/pass.c @@ -4,7 +4,6 @@ #include #include #include -#include #include #include "render/egl.h" #include "render/gles2.h" diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c index 4cdbf3f6e..d10311e1c 100644 --- a/render/gles2/renderer.c +++ b/render/gles2/renderer.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff --git a/types/wlr_matrix.c b/types/wlr_matrix.c index 7b6671f57..8f8f73fa1 100644 --- a/types/wlr_matrix.c +++ b/types/wlr_matrix.c @@ -1,7 +1,6 @@ #include #include #include -#include #include #include #include "types/wlr_matrix.h" From 7d1f535e49d95c3b963a1b4cc01b36abba144241 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 26 Jan 2025 17:52:39 +0100 Subject: [PATCH 269/519] matrix: drop wlr_matrix_transpose() It's unused. --- include/types/wlr_matrix.h | 2 -- types/wlr_matrix.c | 9 --------- 2 files changed, 11 deletions(-) diff --git a/include/types/wlr_matrix.h b/include/types/wlr_matrix.h index e9af2f636..2e86e1e4d 100644 --- a/include/types/wlr_matrix.h +++ b/include/types/wlr_matrix.h @@ -12,8 +12,6 @@ void wlr_matrix_identity(float mat[static 9]); void wlr_matrix_multiply(float mat[static 9], const float a[static 9], const float b[static 9]); -void wlr_matrix_transpose(float mat[static 9], const float a[static 9]); - /** Writes a 2D translation matrix to mat of magnitude (x, y) */ void wlr_matrix_translate(float mat[static 9], float x, float y); diff --git a/types/wlr_matrix.c b/types/wlr_matrix.c index 8f8f73fa1..e2f758575 100644 --- a/types/wlr_matrix.c +++ b/types/wlr_matrix.c @@ -33,15 +33,6 @@ void wlr_matrix_multiply(float mat[static 9], const float a[static 9], memcpy(mat, product, sizeof(product)); } -void wlr_matrix_transpose(float mat[static 9], const float a[static 9]) { - float transposition[9] = { - a[0], a[3], a[6], - a[1], a[4], a[7], - a[2], a[5], a[8], - }; - memcpy(mat, transposition, sizeof(transposition)); -} - void wlr_matrix_translate(float mat[static 9], float x, float y) { float translate[9] = { 1.0f, 0.0f, x, From 211eb9d60ee11e85b78fd4480a33f48a669a4353 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 26 Jan 2025 17:55:28 +0100 Subject: [PATCH 270/519] matrix: drop rotation It's unused. --- include/types/wlr_matrix.h | 6 +----- render/vulkan/pass.c | 4 ++-- types/wlr_matrix.c | 19 +------------------ 3 files changed, 4 insertions(+), 25 deletions(-) diff --git a/include/types/wlr_matrix.h b/include/types/wlr_matrix.h index 2e86e1e4d..07929f961 100644 --- a/include/types/wlr_matrix.h +++ b/include/types/wlr_matrix.h @@ -18,9 +18,6 @@ void wlr_matrix_translate(float mat[static 9], float x, float y); /** Writes a 2D scale matrix to mat of magnitude (x, y) */ void wlr_matrix_scale(float mat[static 9], float x, float y); -/** Writes a 2D rotation matrix to mat at an angle of rad radians */ -void wlr_matrix_rotate(float mat[static 9], float rad); - /** Writes a transformation matrix which applies the specified * wl_output_transform to mat */ void wlr_matrix_transform(float mat[static 9], @@ -31,8 +28,7 @@ void wlr_matrix_transform(float mat[static 9], * rotation. The result is written to mat, which can be applied to each * coordinate of the box to get a new coordinate from [-1,1]. */ void wlr_matrix_project_box(float mat[static 9], const struct wlr_box *box, - enum wl_output_transform transform, float rotation, - const float projection[static 9]); + enum wl_output_transform transform, const float projection[static 9]); /** * Writes a 2D orthographic projection matrix to mat of (width, height) with a diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 8be1cc5d9..cf03822d7 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -620,7 +620,7 @@ static void render_pass_add_rect(struct wlr_render_pass *wlr_pass, case WLR_RENDER_BLEND_MODE_PREMULTIPLIED:; float proj[9], matrix[9]; wlr_matrix_identity(proj); - wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, proj); + wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, proj); wlr_matrix_multiply(matrix, pass->projection, matrix); struct wlr_vk_render_format_setup *setup = pass->srgb_pathway ? @@ -711,7 +711,7 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, float proj[9], matrix[9]; wlr_matrix_identity(proj); - wlr_matrix_project_box(matrix, &dst_box, options->transform, 0, proj); + wlr_matrix_project_box(matrix, &dst_box, options->transform, proj); wlr_matrix_multiply(matrix, pass->projection, matrix); struct wlr_vk_vert_pcr_data vert_pcr_data = { diff --git a/types/wlr_matrix.c b/types/wlr_matrix.c index e2f758575..32a268003 100644 --- a/types/wlr_matrix.c +++ b/types/wlr_matrix.c @@ -1,4 +1,3 @@ -#include #include #include #include @@ -51,15 +50,6 @@ void wlr_matrix_scale(float mat[static 9], float x, float y) { wlr_matrix_multiply(mat, mat, scale); } -void wlr_matrix_rotate(float mat[static 9], float rad) { - float rotate[9] = { - cos(rad), -sin(rad), 0.0f, - sin(rad), cos(rad), 0.0f, - 0.0f, 0.0f, 1.0f, - }; - wlr_matrix_multiply(mat, mat, rotate); -} - static const float transforms[][9] = { [WL_OUTPUT_TRANSFORM_NORMAL] = { 1.0f, 0.0f, 0.0f, @@ -131,8 +121,7 @@ void matrix_projection(float mat[static 9], int width, int height, } void wlr_matrix_project_box(float mat[static 9], const struct wlr_box *box, - enum wl_output_transform transform, float rotation, - const float projection[static 9]) { + enum wl_output_transform transform, const float projection[static 9]) { int x = box->x; int y = box->y; int width = box->width; @@ -141,12 +130,6 @@ void wlr_matrix_project_box(float mat[static 9], const struct wlr_box *box, wlr_matrix_identity(mat); wlr_matrix_translate(mat, x, y); - if (rotation != 0) { - wlr_matrix_translate(mat, width/2, height/2); - wlr_matrix_rotate(mat, rotation); - wlr_matrix_translate(mat, -width/2, -height/2); - } - wlr_matrix_scale(mat, width, height); if (transform != WL_OUTPUT_TRANSFORM_NORMAL) { From 82223e451a7efc8e8885f27e5a375c6bb2f72ba6 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sun, 26 Jan 2025 17:35:47 -0500 Subject: [PATCH 271/519] render/drm_syncobj: Add a callback when ready The old approach of using a signal is fundamentally broken for a common usecase: When the waiter is ready, it's common to immediately finish and free any resources associated with it. Because of the semantics of wl_signal_emit_mutable() this is UB. wl_signal_emit_mutable() always excepts that the waiter hasn't been freed until the signal has finished being emitted. Instead of over engineering the solution, let's just add a callback required by wlr_drm_syncobj_timeline_waiter_init(). In this callback, the implementation is free to finish() or free() any resource it likes. --- backend/wayland/output.c | 12 ++++-------- include/backend/wayland.h | 1 - include/wlr/render/drm_syncobj.h | 10 +++++++++- render/drm_syncobj.c | 6 +++++- types/wlr_linux_drm_syncobj_v1.c | 11 +++-------- 5 files changed, 21 insertions(+), 19 deletions(-) diff --git a/backend/wayland/output.c b/backend/wayland/output.c index 53f4d7772..bbe0230f4 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -135,7 +135,6 @@ static const struct wp_presentation_feedback_listener }; static void buffer_remove_drm_syncobj_waiter(struct wlr_wl_buffer *buffer) { - wl_list_remove(&buffer->drm_syncobj_ready.link); wlr_drm_syncobj_timeline_waiter_finish(&buffer->drm_syncobj_waiter); buffer->has_drm_syncobj_waiter = false; } @@ -177,8 +176,8 @@ static const struct wl_buffer_listener buffer_listener = { .release = buffer_handle_release, }; -static void buffer_handle_drm_syncobj_ready(struct wl_listener *listener, void *data) { - struct wlr_wl_buffer *buffer = wl_container_of(listener, buffer, drm_syncobj_ready); +static void buffer_handle_drm_syncobj_ready(struct wlr_drm_syncobj_timeline_waiter *waiter) { + struct wlr_wl_buffer *buffer = wl_container_of(waiter, buffer, drm_syncobj_waiter); buffer_remove_drm_syncobj_waiter(buffer); buffer_release(buffer); } @@ -819,14 +818,11 @@ static bool output_commit(struct wlr_output *wlr_output, signal_timeline->wl, signal_point_hi, signal_point_lo); if (!wlr_drm_syncobj_timeline_waiter_init(&buffer->drm_syncobj_waiter, - signal_timeline->base, signal_point, 0, output->backend->event_loop)) { + signal_timeline->base, signal_point, 0, output->backend->event_loop, + buffer_handle_drm_syncobj_ready)) { return false; } buffer->has_drm_syncobj_waiter = true; - - buffer->drm_syncobj_ready.notify = buffer_handle_drm_syncobj_ready; - wl_signal_add(&buffer->drm_syncobj_waiter.events.ready, - &buffer->drm_syncobj_ready); } else { if (output->drm_syncobj_surface_v1 != NULL) { wp_linux_drm_syncobj_surface_v1_destroy(output->drm_syncobj_surface_v1); diff --git a/include/backend/wayland.h b/include/backend/wayland.h index 46e8df95c..daa98ef5e 100644 --- a/include/backend/wayland.h +++ b/include/backend/wayland.h @@ -64,7 +64,6 @@ struct wlr_wl_buffer { bool has_drm_syncobj_waiter; struct wlr_drm_syncobj_timeline_waiter drm_syncobj_waiter; - struct wl_listener drm_syncobj_ready; struct wlr_drm_syncobj_timeline *fallback_signal_timeline; uint64_t fallback_signal_point; diff --git a/include/wlr/render/drm_syncobj.h b/include/wlr/render/drm_syncobj.h index bea86d191..30b4ddc35 100644 --- a/include/wlr/render/drm_syncobj.h +++ b/include/wlr/render/drm_syncobj.h @@ -37,6 +37,11 @@ struct wlr_drm_syncobj_timeline { } WLR_PRIVATE; }; +struct wlr_drm_syncobj_timeline_waiter; + +typedef void (*wlr_drm_syncobj_timeline_ready_callback)( + struct wlr_drm_syncobj_timeline_waiter *waiter); + struct wlr_drm_syncobj_timeline_waiter { struct { struct wl_signal ready; @@ -45,6 +50,7 @@ struct wlr_drm_syncobj_timeline_waiter { struct { int ev_fd; struct wl_event_source *event_source; + wlr_drm_syncobj_timeline_ready_callback callback; } WLR_PRIVATE; }; @@ -92,10 +98,12 @@ bool wlr_drm_syncobj_timeline_check(struct wlr_drm_syncobj_timeline *timeline, * Asynchronously wait for a timeline point. * * See wlr_drm_syncobj_timeline_check() for a definition of flags. + * + * A callback must be provided that will be invoked when the waiter has finished. */ bool wlr_drm_syncobj_timeline_waiter_init(struct wlr_drm_syncobj_timeline_waiter *waiter, struct wlr_drm_syncobj_timeline *timeline, uint64_t point, uint32_t flags, - struct wl_event_loop *loop); + struct wl_event_loop *loop, wlr_drm_syncobj_timeline_ready_callback callback); /** * Cancel a timeline waiter. */ diff --git a/render/drm_syncobj.c b/render/drm_syncobj.c index af93876d4..e0493b462 100644 --- a/render/drm_syncobj.c +++ b/render/drm_syncobj.c @@ -193,12 +193,15 @@ static int handle_eventfd_ready(int ev_fd, uint32_t mask, void *data) { } wl_signal_emit_mutable(&waiter->events.ready, NULL); + waiter->callback(waiter); return 0; } bool wlr_drm_syncobj_timeline_waiter_init(struct wlr_drm_syncobj_timeline_waiter *waiter, struct wlr_drm_syncobj_timeline *timeline, uint64_t point, uint32_t flags, - struct wl_event_loop *loop) { + struct wl_event_loop *loop, wlr_drm_syncobj_timeline_ready_callback callback) { + assert(callback); + int ev_fd; #if HAVE_EVENTFD ev_fd = eventfd(0, EFD_CLOEXEC); @@ -235,6 +238,7 @@ bool wlr_drm_syncobj_timeline_waiter_init(struct wlr_drm_syncobj_timeline_waiter *waiter = (struct wlr_drm_syncobj_timeline_waiter){ .ev_fd = ev_fd, .event_source = source, + .callback = callback, }; wl_signal_init(&waiter->events.ready); return true; diff --git a/types/wlr_linux_drm_syncobj_v1.c b/types/wlr_linux_drm_syncobj_v1.c index 0713bcce6..a10e6a450 100644 --- a/types/wlr_linux_drm_syncobj_v1.c +++ b/types/wlr_linux_drm_syncobj_v1.c @@ -29,7 +29,6 @@ struct wlr_linux_drm_syncobj_surface_v1_commit { struct wlr_drm_syncobj_timeline_waiter waiter; uint32_t cached_seq; - struct wl_listener waiter_ready; struct wl_listener surface_destroy; }; @@ -194,14 +193,13 @@ static struct wlr_linux_drm_syncobj_surface_v1 *surface_from_wlr_surface( static void surface_commit_destroy(struct wlr_linux_drm_syncobj_surface_v1_commit *commit) { wlr_surface_unlock_cached(commit->surface->surface, commit->cached_seq); wl_list_remove(&commit->surface_destroy.link); - wl_list_remove(&commit->waiter_ready.link); wlr_drm_syncobj_timeline_waiter_finish(&commit->waiter); free(commit); } -static void surface_commit_handle_waiter_ready(struct wl_listener *listener, void *data) { +static void surface_commit_handle_waiter_ready(struct wlr_drm_syncobj_timeline_waiter *waiter) { struct wlr_linux_drm_syncobj_surface_v1_commit *commit = - wl_container_of(listener, commit, waiter_ready); + wl_container_of(waiter, commit, waiter); surface_commit_destroy(commit); } @@ -233,7 +231,7 @@ static bool lock_surface_commit(struct wlr_linux_drm_syncobj_surface_v1 *surface struct wl_display *display = wl_client_get_display(client); struct wl_event_loop *loop = wl_display_get_event_loop(display); if (!wlr_drm_syncobj_timeline_waiter_init(&commit->waiter, timeline, point, - flags, loop)) { + flags, loop, surface_commit_handle_waiter_ready)) { free(commit); return false; } @@ -241,9 +239,6 @@ static bool lock_surface_commit(struct wlr_linux_drm_syncobj_surface_v1 *surface commit->surface = surface; commit->cached_seq = wlr_surface_lock_pending(surface->surface); - commit->waiter_ready.notify = surface_commit_handle_waiter_ready; - wl_signal_add(&commit->waiter.events.ready, &commit->waiter_ready); - commit->surface_destroy.notify = surface_commit_handle_surface_destroy; wl_signal_add(&surface->surface->events.destroy, &commit->surface_destroy); From c1eb053f5e9dcc72323d34eea09a3764d3668668 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sun, 26 Jan 2025 17:35:38 -0500 Subject: [PATCH 272/519] render/drm_syncobj: Remove the ready signal from timeline_waiter It's unused. --- include/wlr/render/drm_syncobj.h | 4 ---- render/drm_syncobj.c | 4 ---- 2 files changed, 8 deletions(-) diff --git a/include/wlr/render/drm_syncobj.h b/include/wlr/render/drm_syncobj.h index 30b4ddc35..deef72dc9 100644 --- a/include/wlr/render/drm_syncobj.h +++ b/include/wlr/render/drm_syncobj.h @@ -43,10 +43,6 @@ typedef void (*wlr_drm_syncobj_timeline_ready_callback)( struct wlr_drm_syncobj_timeline_waiter *waiter); struct wlr_drm_syncobj_timeline_waiter { - struct { - struct wl_signal ready; - } events; - struct { int ev_fd; struct wl_event_source *event_source; diff --git a/render/drm_syncobj.c b/render/drm_syncobj.c index e0493b462..7f479f570 100644 --- a/render/drm_syncobj.c +++ b/render/drm_syncobj.c @@ -192,7 +192,6 @@ static int handle_eventfd_ready(int ev_fd, uint32_t mask, void *data) { } } - wl_signal_emit_mutable(&waiter->events.ready, NULL); waiter->callback(waiter); return 0; } @@ -240,13 +239,10 @@ bool wlr_drm_syncobj_timeline_waiter_init(struct wlr_drm_syncobj_timeline_waiter .event_source = source, .callback = callback, }; - wl_signal_init(&waiter->events.ready); return true; } void wlr_drm_syncobj_timeline_waiter_finish(struct wlr_drm_syncobj_timeline_waiter *waiter) { - assert(wl_list_empty(&waiter->events.ready.listener_list)); - wl_event_source_remove(waiter->event_source); close(waiter->ev_fd); } From 639ca05d352560c6a292c696f945d326e0acfc00 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 27 Jan 2025 17:48:18 +0100 Subject: [PATCH 273/519] matrix: move to util/ wlr_matrix is not a standalone type like other headers in types/, it's more of an internal utility. Move it to the appropriate place. --- include/{types/wlr_matrix.h => util/matrix.h} | 4 ++-- render/gles2/pass.c | 2 +- render/gles2/renderer.c | 1 - render/vulkan/pass.c | 2 +- render/vulkan/renderer.c | 1 - types/meson.build | 1 - types/wlr_matrix.c => util/matrix.c | 2 +- util/meson.build | 1 + 8 files changed, 6 insertions(+), 8 deletions(-) rename include/{types/wlr_matrix.h => util/matrix.h} (96%) rename types/wlr_matrix.c => util/matrix.c (99%) diff --git a/include/types/wlr_matrix.h b/include/util/matrix.h similarity index 96% rename from include/types/wlr_matrix.h rename to include/util/matrix.h index 07929f961..059a3bb1f 100644 --- a/include/types/wlr_matrix.h +++ b/include/util/matrix.h @@ -1,5 +1,5 @@ -#ifndef TYPES_WLR_MATRIX_H -#define TYPES_WLR_MATRIX_H +#ifndef UTIL_MATRIX_H +#define UTIL_MATRIX_H #include diff --git a/render/gles2/pass.c b/render/gles2/pass.c index 96b66065e..b10ac047d 100644 --- a/render/gles2/pass.c +++ b/render/gles2/pass.c @@ -7,7 +7,7 @@ #include #include "render/egl.h" #include "render/gles2.h" -#include "types/wlr_matrix.h" +#include "util/matrix.h" #define MAX_QUADS 86 // 4kb diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c index d10311e1c..e362daee8 100644 --- a/render/gles2/renderer.c +++ b/render/gles2/renderer.c @@ -18,7 +18,6 @@ #include "render/egl.h" #include "render/gles2.h" #include "render/pixel_format.h" -#include "types/wlr_matrix.h" #include "util/time.h" #include "common_vert_src.h" diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index cf03822d7..032fcc7b2 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -8,7 +8,7 @@ #include "render/color.h" #include "render/vulkan.h" -#include "types/wlr_matrix.h" +#include "util/matrix.h" static const struct wlr_render_pass_impl render_pass_impl; static const struct wlr_addon_interface vk_color_transform_impl; diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index 21805ea09..4051dbf05 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -27,7 +27,6 @@ #include "render/vulkan/shaders/quad.frag.h" #include "render/vulkan/shaders/output.frag.h" #include "types/wlr_buffer.h" -#include "types/wlr_matrix.h" #include "util/time.h" // TODO: diff --git a/types/meson.build b/types/meson.build index 466c1de63..c6b33cec8 100644 --- a/types/meson.build +++ b/types/meson.build @@ -60,7 +60,6 @@ wlr_files += files( 'wlr_layer_shell_v1.c', 'wlr_linux_dmabuf_v1.c', 'wlr_linux_drm_syncobj_v1.c', - 'wlr_matrix.c', 'wlr_output_layer.c', 'wlr_output_layout.c', 'wlr_output_management_v1.c', diff --git a/types/wlr_matrix.c b/util/matrix.c similarity index 99% rename from types/wlr_matrix.c rename to util/matrix.c index 32a268003..dd1a3e566 100644 --- a/types/wlr_matrix.c +++ b/util/matrix.c @@ -2,7 +2,7 @@ #include #include #include -#include "types/wlr_matrix.h" +#include "util/matrix.h" void wlr_matrix_identity(float mat[static 9]) { static const float identity[9] = { diff --git a/util/meson.build b/util/meson.build index 053e2c5eb..fbdd54bbb 100644 --- a/util/meson.build +++ b/util/meson.build @@ -5,6 +5,7 @@ wlr_files += files( 'env.c', 'global.c', 'log.c', + 'matrix.c', 'rect_union.c', 'region.c', 'set.c', From 0d056a0315d4353d85628f2cd13880cc71826c47 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 27 Jan 2025 17:50:18 +0100 Subject: [PATCH 274/519] util/matrix: remove unnecessary include --- util/matrix.c | 1 - 1 file changed, 1 deletion(-) diff --git a/util/matrix.c b/util/matrix.c index dd1a3e566..86b434c64 100644 --- a/util/matrix.c +++ b/util/matrix.c @@ -1,6 +1,5 @@ #include #include -#include #include #include "util/matrix.h" From a818251aec2e4aaec33d67abbe9cd1343d3b11a7 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 4 Jan 2024 12:00:45 +0100 Subject: [PATCH 275/519] render/pixman: drop cast for pixman_image_set_clip_region32() The new Pixman release has made this const. --- meson.build | 2 +- render/pixman/pass.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index 5a1146692..fb1ea12ba 100644 --- a/meson.build +++ b/meson.build @@ -116,7 +116,7 @@ xkbcommon = dependency( ], ) pixman = dependency('pixman-1', - version: '>=0.42.0', + version: '>=0.43.0', fallback: 'pixman', default_options: ['werror=false'], ) diff --git a/render/pixman/pass.c b/render/pixman/pass.c index 30883c8b4..4ae742cab 100644 --- a/render/pixman/pass.c +++ b/render/pixman/pass.c @@ -48,7 +48,7 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, } pixman_op_t op = get_pixman_blending(options->blend_mode); - pixman_image_set_clip_region32(buffer->image, (pixman_region32_t *)options->clip); + pixman_image_set_clip_region32(buffer->image, options->clip); struct wlr_fbox src_fbox; wlr_render_texture_options_get_src_box(options, &src_fbox); @@ -217,7 +217,7 @@ static void render_pass_add_rect(struct wlr_render_pass *wlr_pass, pixman_image_t *fill = pixman_image_create_solid_fill(&color); - pixman_image_set_clip_region32(buffer->image, (pixman_region32_t *)options->clip); + pixman_image_set_clip_region32(buffer->image, options->clip); pixman_image_composite32(op, fill, NULL, buffer->image, 0, 0, 0, 0, box.x, box.y, box.width, box.height); pixman_image_set_clip_region32(buffer->image, NULL); From 83c5b15194d3bd7b4e4da3e5f04a8e6d37c5b476 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 4 Jan 2024 12:04:17 +0100 Subject: [PATCH 276/519] Remove all calls to pixman_region32_not_empty() Replace them with pixman_region32_empty(), which avoids using a double-negative when checking if a region is empty. Also use that new function when checking for non-empty regions so that only one variant of the Pixman API is used. --- types/output/cursor.c | 2 +- types/scene/wlr_scene.c | 23 ++++++++++++----------- types/wlr_ext_image_copy_capture_v1.c | 4 ++-- types/wlr_pointer_constraints_v1.c | 2 +- types/wlr_screencopy_v1.c | 2 +- 5 files changed, 17 insertions(+), 16 deletions(-) diff --git a/types/output/cursor.c b/types/output/cursor.c index 63341d275..af1663574 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -116,7 +116,7 @@ void wlr_output_add_software_cursors_to_render_pass(struct wlr_output *output, pixman_region32_intersect(&cursor_damage, &cursor_damage, damage); } - if (!pixman_region32_not_empty(&cursor_damage)) { + if (pixman_region32_empty(&cursor_damage)) { pixman_region32_fini(&cursor_damage); continue; } diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 915468773..62ff576bf 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -370,7 +370,7 @@ static void scene_output_damage(struct wlr_scene_output *scene_output, pixman_region32_init(&clipped); pixman_region32_intersect_rect(&clipped, damage, 0, 0, output->width, output->height); - if (pixman_region32_not_empty(&clipped)) { + if (!pixman_region32_empty(&clipped)) { wlr_output_schedule_frame(scene_output->output); wlr_damage_ring_add(&scene_output->damage_ring, &clipped); @@ -391,7 +391,7 @@ static void scene_output_damage_whole(struct wlr_scene_output *scene_output) { } static void scene_damage_outputs(struct wlr_scene *scene, pixman_region32_t *damage) { - if (!pixman_region32_not_empty(damage)) { + if (pixman_region32_empty(damage)) { return; } @@ -453,7 +453,7 @@ static void update_node_update_outputs(struct wlr_scene_node *node, pixman_region32_intersect_rect(&intersection, &node->visible, output_box.x, output_box.y, output_box.width, output_box.height); - if (pixman_region32_not_empty(&intersection)) { + if (!pixman_region32_empty(&intersection)) { uint32_t overlap = region_area(&intersection); if (overlap >= largest_overlap) { largest_overlap = overlap; @@ -1049,7 +1049,7 @@ void wlr_scene_buffer_set_transform(struct wlr_scene_buffer *scene_buffer, void wlr_scene_buffer_send_frame_done(struct wlr_scene_buffer *scene_buffer, struct timespec *now) { - if (pixman_region32_not_empty(&scene_buffer->node.visible)) { + if (!pixman_region32_empty(&scene_buffer->node.visible)) { wl_signal_emit_mutable(&scene_buffer->events.frame_done, now); } } @@ -1344,7 +1344,7 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren pixman_region32_translate(&render_region, -data->logical.x, -data->logical.y); logical_to_buffer_coords(&render_region, data); pixman_region32_intersect(&render_region, &render_region, &data->damage); - if (!pixman_region32_not_empty(&render_region)) { + if (pixman_region32_empty(&render_region)) { pixman_region32_fini(&render_region); return; } @@ -1406,7 +1406,7 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren .alpha = &scene_buffer->opacity, .filter_mode = scene_buffer->filter_mode, .blend_mode = !data->output->scene->calculate_visibility || - pixman_region32_not_empty(&opaque) ? + !pixman_region32_empty(&opaque) ? WLR_RENDER_BLEND_MODE_PREMULTIPLIED : WLR_RENDER_BLEND_MODE_NONE, .wait_timeline = scene_buffer->wait_timeline, .wait_point = scene_buffer->wait_point, @@ -1763,7 +1763,7 @@ static bool construct_render_list_iterator(struct wlr_scene_node *node, pixman_region32_intersect_rect(&intersection, &node->visible, data->box.x, data->box.y, data->box.width, data->box.height); - if (!pixman_region32_not_empty(&intersection)) { + if (pixman_region32_empty(&intersection)) { pixman_region32_fini(&intersection); return false; } @@ -1914,8 +1914,9 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry, } bool wlr_scene_output_needs_frame(struct wlr_scene_output *scene_output) { - return scene_output->output->needs_frame || pixman_region32_not_empty( - &scene_output->pending_commit_damage) || scene_output->gamma_lut_changed; + return scene_output->output->needs_frame || + !pixman_region32_empty(&scene_output->pending_commit_damage) || + scene_output->gamma_lut_changed; } bool wlr_scene_output_commit(struct wlr_scene_output *scene_output, @@ -2054,7 +2055,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, clock_gettime(CLOCK_MONOTONIC, &now); // add the current frame's damage if there is damage - if (pixman_region32_not_empty(&scene_output->damage_ring.current)) { + if (!pixman_region32_empty(&scene_output->damage_ring.current)) { struct highlight_region *current_damage = calloc(1, sizeof(*current_damage)); if (current_damage) { pixman_region32_init(¤t_damage->region); @@ -2077,7 +2078,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, struct timespec time_diff; timespec_sub(&time_diff, &now, &damage->when); if (timespec_to_msec(&time_diff) >= HIGHLIGHT_DAMAGE_FADEOUT_TIME || - !pixman_region32_not_empty(&damage->region)) { + pixman_region32_empty(&damage->region)) { highlight_region_destroy(damage); } } diff --git a/types/wlr_ext_image_copy_capture_v1.c b/types/wlr_ext_image_copy_capture_v1.c index 592e4e46c..6f5556139 100644 --- a/types/wlr_ext_image_copy_capture_v1.c +++ b/types/wlr_ext_image_copy_capture_v1.c @@ -280,7 +280,7 @@ static void frame_handle_capture(struct wl_client *client, frame->capturing = true; - bool need_frame = pixman_region32_not_empty(&frame->session->damage); + bool need_frame = !pixman_region32_empty(&frame->session->damage); struct wlr_ext_image_capture_source_v1 *source = frame->session->source; if (need_frame && source->impl->schedule_frame) { source->impl->schedule_frame(source); @@ -423,7 +423,7 @@ static void session_handle_source_frame(struct wl_listener *listener, void *data struct wlr_ext_image_copy_capture_frame_v1 *frame = session->frame; if (frame != NULL && frame->capturing && - pixman_region32_not_empty(&session->damage)) { + !pixman_region32_empty(&session->damage)) { pixman_region32_union(&frame->buffer_damage, &frame->buffer_damage, &session->damage); diff --git a/types/wlr_pointer_constraints_v1.c b/types/wlr_pointer_constraints_v1.c index 42329da82..6bc5efc1b 100644 --- a/types/wlr_pointer_constraints_v1.c +++ b/types/wlr_pointer_constraints_v1.c @@ -107,7 +107,7 @@ static bool update_region(struct wlr_pointer_constraint_v1 *constraint) { pixman_region32_t region; pixman_region32_init(®ion); - if (pixman_region32_not_empty(&constraint->current.region)) { + if (!pixman_region32_empty(&constraint->current.region)) { pixman_region32_intersect(®ion, &constraint->surface->input_region, &constraint->current.region); } else { diff --git a/types/wlr_screencopy_v1.c b/types/wlr_screencopy_v1.c index 7cf583b29..d039659d4 100644 --- a/types/wlr_screencopy_v1.c +++ b/types/wlr_screencopy_v1.c @@ -299,7 +299,7 @@ static void frame_handle_output_commit(struct wl_listener *listener, if (frame->with_damage) { struct screencopy_damage *damage = screencopy_damage_get_or_create(frame->client, output); - if (damage && !pixman_region32_not_empty(&damage->damage)) { + if (damage && pixman_region32_empty(&damage->damage)) { return; } } From a64e1a58b19cc63a76830145a51375890fe66308 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 28 Nov 2024 20:31:10 +0100 Subject: [PATCH 277/519] backend/drm: log when creating multi-GPU renderer Creating a renderer results in lots of logs. Make it clear that the logs belong to a multi-GPU renderer (as opposed to a primary renderer created by the compositor). --- backend/drm/renderer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c index 721e64bf2..af9908e91 100644 --- a/backend/drm/renderer.c +++ b/backend/drm/renderer.c @@ -14,6 +14,7 @@ bool init_drm_renderer(struct wlr_drm_backend *drm, struct wlr_drm_renderer *renderer) { + wlr_log(WLR_DEBUG, "Creating multi-GPU renderer"); renderer->wlr_rend = renderer_autocreate_with_drm_fd(drm->fd); if (!renderer->wlr_rend) { return false; From 714a0264a6d3e4111a06d52b0975513fc7acf26b Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 30 Jun 2023 16:34:17 +0200 Subject: [PATCH 278/519] ci: generate and publish HTML documentation --- .builds/alpine.yml | 18 ++++++++++++++++++ .gitlab-ci.yml | 1 + 2 files changed, 19 insertions(+) diff --git a/.builds/alpine.yml b/.builds/alpine.yml index 20cb8212e..399adc84f 100644 --- a/.builds/alpine.yml +++ b/.builds/alpine.yml @@ -20,8 +20,13 @@ packages: - xwayland-dev - libseat-dev - hwdata-dev + # for docs + - go + - zip sources: - https://gitlab.freedesktop.org/wlroots/wlroots.git +artifacts: + - public.zip tasks: - setup: | cd wlroots @@ -37,3 +42,16 @@ tasks: - tinywl: | cd wlroots/tinywl make + - docs: | + go install 'git.sr.ht/~emersion/gyosu@latest' + include_dir="$(echo /usr/local/include/wlroots-*)" + ~/go/bin/gyosu \ + -DWLR_USE_UNSTABLE \ + $(pkg-config --cflags-only-I $(basename "$include_dir")) \ + -Iwlroots/build/protocol/ \ + -fexported-symbols='wlr_*' -fexported-symbols='WLR_*' \ + -ffile-prefix-map="$include_dir/"= \ + -fsite-name=wlroots \ + -o public \ + "$include_dir/wlr/" + zip -r ~/public.zip public/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b0942ab31..e02463a9c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,6 +1,7 @@ include: https://git.sr.ht/~emersion/dalligi/blob/master/templates/multi.yml alpine: extends: .dalligi + pages: true archlinux: extends: .dalligi freebsd: From d305934ebe6852785a1f425ee96861f0b7280d76 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Thu, 30 Jan 2025 19:58:53 -0500 Subject: [PATCH 279/519] ext_data_control: Add protocol implementation --- include/wlr/types/wlr_ext_data_control_v1.h | 47 ++ protocol/meson.build | 1 + types/meson.build | 1 + types/wlr_ext_data_control_v1.c | 702 ++++++++++++++++++++ 4 files changed, 751 insertions(+) create mode 100644 include/wlr/types/wlr_ext_data_control_v1.h create mode 100644 types/wlr_ext_data_control_v1.c diff --git a/include/wlr/types/wlr_ext_data_control_v1.h b/include/wlr/types/wlr_ext_data_control_v1.h new file mode 100644 index 000000000..de5ad8d52 --- /dev/null +++ b/include/wlr/types/wlr_ext_data_control_v1.h @@ -0,0 +1,47 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_TYPES_WLR_EXT_DATA_CONTROL_V1_H +#define WLR_TYPES_WLR_EXT_DATA_CONTROL_V1_H + +#include +#include + +struct wlr_ext_data_control_manager_v1 { + struct wl_global *global; + struct wl_list devices; // wlr_data_control_device_v1.link + + struct { + struct wl_signal destroy; + struct wl_signal new_device; // wlr_data_control_device_v1 + } events; + + struct wl_listener display_destroy; +}; + +struct wlr_ext_data_control_device_v1 { + struct wl_resource *resource; + struct wlr_ext_data_control_manager_v1 *manager; + struct wl_list link; // wlr_data_control_manager_v1.devices + + struct wlr_seat *seat; + struct wl_resource *selection_offer_resource; // current selection offer + struct wl_resource *primary_selection_offer_resource; // current primary selection offer + + struct wl_listener seat_destroy; + struct wl_listener seat_set_selection; + struct wl_listener seat_set_primary_selection; +}; + +struct wlr_ext_data_control_manager_v1 *wlr_ext_data_control_manager_v1_create( + struct wl_display *display, uint32_t version); + +void wlr_ext_data_control_device_v1_destroy( + struct wlr_ext_data_control_device_v1 *device); + +#endif diff --git a/protocol/meson.build b/protocol/meson.build index 931aad56f..f3b634ae8 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -32,6 +32,7 @@ protocols = { 'ext-image-capture-source-v1': wl_protocol_dir / 'staging/ext-image-capture-source/ext-image-capture-source-v1.xml', 'ext-image-copy-capture-v1': wl_protocol_dir / 'staging/ext-image-copy-capture/ext-image-copy-capture-v1.xml', 'ext-session-lock-v1': wl_protocol_dir / 'staging/ext-session-lock/ext-session-lock-v1.xml', + 'ext-data-control-v1': wl_protocol_dir / 'staging/ext-data-control/ext-data-control-v1.xml', 'fractional-scale-v1': wl_protocol_dir / 'staging/fractional-scale/fractional-scale-v1.xml', 'linux-drm-syncobj-v1': wl_protocol_dir / 'staging/linux-drm-syncobj/linux-drm-syncobj-v1.xml', 'security-context-v1': wl_protocol_dir / 'staging/security-context/security-context-v1.xml', diff --git a/types/meson.build b/types/meson.build index c6b33cec8..1d17fd647 100644 --- a/types/meson.build +++ b/types/meson.build @@ -47,6 +47,7 @@ wlr_files += files( 'wlr_foreign_toplevel_management_v1.c', 'wlr_ext_image_copy_capture_v1.c', 'wlr_ext_foreign_toplevel_list_v1.c', + 'wlr_ext_data_control_v1.c', 'wlr_fractional_scale_v1.c', 'wlr_fullscreen_shell_v1.c', 'wlr_gamma_control_v1.c', diff --git a/types/wlr_ext_data_control_v1.c b/types/wlr_ext_data_control_v1.c new file mode 100644 index 000000000..80869f1c5 --- /dev/null +++ b/types/wlr_ext_data_control_v1.c @@ -0,0 +1,702 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "ext-data-control-v1-protocol.h" + +#define EXT_DATA_CONTROL_MANAGER_VERSION 1 + +struct data_control_source { + struct wl_resource *resource; + struct wl_array mime_types; + bool finalized; + + // Only one of these is non-NULL. + struct wlr_data_source *active_source; + struct wlr_primary_selection_source *active_primary_source; +}; + +static const struct ext_data_control_source_v1_interface source_impl; + +static struct data_control_source *source_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &ext_data_control_source_v1_interface, &source_impl)); + return wl_resource_get_user_data(resource); +} + +static void source_handle_offer(struct wl_client *client, + struct wl_resource *resource, const char *mime_type) { + struct data_control_source *source = source_from_resource(resource); + if (source == NULL) { + return; + } + + if (source->finalized) { + wl_resource_post_error(resource, + EXT_DATA_CONTROL_SOURCE_V1_ERROR_INVALID_OFFER, + "cannot mutate offer after set_selection or " + "set_primary_selection"); + return; + } + + const char **mime_type_ptr; + wl_array_for_each(mime_type_ptr, &source->mime_types) { + if (strcmp(*mime_type_ptr, mime_type) == 0) { + wlr_log(WLR_DEBUG, "Ignoring duplicate MIME type offer %s", + mime_type); + return; + } + } + + char *dup_mime_type = strdup(mime_type); + if (dup_mime_type == NULL) { + wl_resource_post_no_memory(resource); + return; + } + + char **p = wl_array_add(&source->mime_types, sizeof(char *)); + if (p == NULL) { + free(dup_mime_type); + wl_resource_post_no_memory(resource); + return; + } + + *p = dup_mime_type; +} + +static void source_handle_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static const struct ext_data_control_source_v1_interface source_impl = { + .offer = source_handle_offer, + .destroy = source_handle_destroy, +}; + +static void data_control_source_destroy(struct data_control_source *source) { + if (source == NULL) { + return; + } + + char **p; + wl_array_for_each(p, &source->mime_types) { + free(*p); + } + wl_array_release(&source->mime_types); + + // Prevent destructors below from calling this recursively. + wl_resource_set_user_data(source->resource, NULL); + + if (source->active_source != NULL) { + wlr_data_source_destroy(source->active_source); + } else if (source->active_primary_source != NULL) { + wlr_primary_selection_source_destroy( + source->active_primary_source); + } + + free(source); +} + +static void source_handle_resource_destroy(struct wl_resource *resource) { + struct data_control_source *source = source_from_resource(resource); + data_control_source_destroy(source); +} + + +struct client_data_source { + struct wlr_data_source source; + struct wl_resource *resource; +}; + +static const struct wlr_data_source_impl client_source_impl; + +static struct client_data_source * + client_data_source_from_source(struct wlr_data_source *wlr_source) { + assert(wlr_source->impl == &client_source_impl); + struct client_data_source *source = wl_container_of(wlr_source, source, source); + return source; +} + +static void client_source_send(struct wlr_data_source *wlr_source, + const char *mime_type, int fd) { + struct client_data_source *source = + client_data_source_from_source(wlr_source); + ext_data_control_source_v1_send_send(source->resource, mime_type, fd); + close(fd); +} + +static void client_source_destroy(struct wlr_data_source *wlr_source) { + struct client_data_source *client_source = + client_data_source_from_source(wlr_source); + struct data_control_source *source = + source_from_resource(client_source->resource); + free(client_source); + + if (source == NULL) { + return; + } + + source->active_source = NULL; + + ext_data_control_source_v1_send_cancelled(source->resource); + data_control_source_destroy(source); +} + +static const struct wlr_data_source_impl client_source_impl = { + .send = client_source_send, + .destroy = client_source_destroy, +}; + + +struct client_primary_selection_source { + struct wlr_primary_selection_source source; + struct wl_resource *resource; +}; + +static const struct wlr_primary_selection_source_impl +client_primary_selection_source_impl; + +static struct client_primary_selection_source * + client_primary_selection_source_from_source( + struct wlr_primary_selection_source *wlr_source) { + assert(wlr_source->impl == &client_primary_selection_source_impl); + struct client_primary_selection_source *source = wl_container_of(wlr_source, source, source); + return source; +} + +static void client_primary_selection_source_send( + struct wlr_primary_selection_source *wlr_source, + const char *mime_type, int fd) { + struct client_primary_selection_source *source = + client_primary_selection_source_from_source(wlr_source); + ext_data_control_source_v1_send_send(source->resource, mime_type, fd); + close(fd); +} + +static void client_primary_selection_source_destroy( + struct wlr_primary_selection_source *wlr_source) { + struct client_primary_selection_source *client_source = + client_primary_selection_source_from_source(wlr_source); + struct data_control_source *source = + source_from_resource(client_source->resource); + free(client_source); + + if (source == NULL) { + return; + } + + source->active_primary_source = NULL; + + ext_data_control_source_v1_send_cancelled(source->resource); + data_control_source_destroy(source); +} + +static const struct wlr_primary_selection_source_impl +client_primary_selection_source_impl = { + .send = client_primary_selection_source_send, + .destroy = client_primary_selection_source_destroy, +}; + + +struct data_offer { + struct wl_resource *resource; + struct wlr_ext_data_control_device_v1 *device; + bool is_primary; +}; + +static void data_offer_destroy(struct data_offer *offer) { + if (offer == NULL) { + return; + } + + struct wlr_ext_data_control_device_v1 *device = offer->device; + if (device != NULL) { + if (offer->is_primary) { + device->primary_selection_offer_resource = NULL; + } else { + device->selection_offer_resource = NULL; + } + } + + wl_resource_set_user_data(offer->resource, NULL); + free(offer); +} + +static const struct ext_data_control_offer_v1_interface offer_impl; + +static struct data_offer *data_offer_from_offer_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &ext_data_control_offer_v1_interface, &offer_impl)); + return wl_resource_get_user_data(resource); +} + +static void offer_handle_receive(struct wl_client *client, + struct wl_resource *resource, const char *mime_type, int fd) { + struct data_offer *offer = data_offer_from_offer_resource(resource); + if (offer == NULL) { + close(fd); + return; + } + + struct wlr_ext_data_control_device_v1 *device = offer->device; + if (device == NULL) { + close(fd); + return; + } + + if (offer->is_primary) { + if (device->seat->primary_selection_source == NULL) { + close(fd); + return; + } + wlr_primary_selection_source_send( + device->seat->primary_selection_source, + mime_type, fd); + } else { + if (device->seat->selection_source == NULL) { + close(fd); + return; + } + wlr_data_source_send(device->seat->selection_source, mime_type, fd); + } +} + +static void offer_handle_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static const struct ext_data_control_offer_v1_interface offer_impl = { + .receive = offer_handle_receive, + .destroy = offer_handle_destroy, +}; + +static void offer_handle_resource_destroy(struct wl_resource *resource) { + struct data_offer *offer = data_offer_from_offer_resource(resource); + data_offer_destroy(offer); +} + +static struct wl_resource *create_offer(struct wlr_ext_data_control_device_v1 *device, + struct wl_array *mime_types, bool is_primary) { + struct wl_client *client = wl_resource_get_client(device->resource); + + struct data_offer *offer = calloc(1, sizeof(*offer)); + if (offer == NULL) { + wl_client_post_no_memory(client); + return NULL; + } + + offer->device = device; + offer->is_primary = is_primary; + + uint32_t version = wl_resource_get_version(device->resource); + struct wl_resource *resource = wl_resource_create(client, + &ext_data_control_offer_v1_interface, version, 0); + if (resource == NULL) { + free(offer); + return NULL; + } + + offer->resource = resource; + + wl_resource_set_implementation(resource, &offer_impl, offer, + offer_handle_resource_destroy); + + ext_data_control_device_v1_send_data_offer(device->resource, resource); + + char **p; + wl_array_for_each(p, mime_types) { + ext_data_control_offer_v1_send_offer(resource, *p); + } + + return resource; +} + + +static const struct ext_data_control_device_v1_interface control_impl; + +static struct wlr_ext_data_control_device_v1 *control_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &ext_data_control_device_v1_interface, &control_impl)); + return wl_resource_get_user_data(resource); +} + +static void control_handle_set_selection(struct wl_client *client, + struct wl_resource *control_resource, + struct wl_resource *source_resource) { + struct wlr_ext_data_control_device_v1 *device = + control_from_resource(control_resource); + if (device == NULL) { + return; + } + + struct data_control_source *source = NULL; + if (source_resource != NULL) { + source = source_from_resource(source_resource); + } + + if (source == NULL) { + wlr_seat_request_set_selection(device->seat, NULL, NULL, + wl_display_next_serial(device->seat->display)); + + return; + } + + if (source->active_source != NULL || + source->active_primary_source != NULL) { + wl_resource_post_error(control_resource, + EXT_DATA_CONTROL_DEVICE_V1_ERROR_USED_SOURCE, + "cannot use a data source in set_selection or " + "set_primary_selection more than once"); + + return; + } + + struct client_data_source *client_source = calloc(1, sizeof(*client_source)); + if (client_source == NULL) { + wl_client_post_no_memory(client); + return; + } + client_source->resource = source_resource; + + struct wlr_data_source *wlr_source = &client_source->source; + wlr_data_source_init(wlr_source, &client_source_impl); + source->active_source = wlr_source; + + wl_array_release(&wlr_source->mime_types); + wlr_source->mime_types = source->mime_types; + wl_array_init(&source->mime_types); + + source->finalized = true; + + wlr_seat_request_set_selection(device->seat, NULL, wlr_source, + wl_display_next_serial(device->seat->display)); +} + +static void control_handle_set_primary_selection(struct wl_client *client, + struct wl_resource *control_resource, + struct wl_resource *source_resource) { + struct wlr_ext_data_control_device_v1 *device = + control_from_resource(control_resource); + if (device == NULL) { + return; + } + + struct data_control_source *source = NULL; + if (source_resource != NULL) { + source = source_from_resource(source_resource); + } + + if (source == NULL) { + wlr_seat_request_set_primary_selection(device->seat, NULL, NULL, + wl_display_next_serial(device->seat->display)); + + return; + } + + if (source->active_source != NULL || + source->active_primary_source != NULL) { + wl_resource_post_error(control_resource, + EXT_DATA_CONTROL_DEVICE_V1_ERROR_USED_SOURCE, + "cannot use a data source in set_selection or " + "set_primary_selection more than once"); + + return; + } + + struct client_primary_selection_source *client_source = calloc(1, sizeof(*client_source)); + if (client_source == NULL) { + wl_client_post_no_memory(client); + return; + } + client_source->resource = source_resource; + + struct wlr_primary_selection_source *wlr_source = &client_source->source; + wlr_primary_selection_source_init(wlr_source, &client_primary_selection_source_impl); + source->active_primary_source = wlr_source; + + wl_array_release(&wlr_source->mime_types); + wlr_source->mime_types = source->mime_types; + wl_array_init(&source->mime_types); + + source->finalized = true; + + wlr_seat_request_set_primary_selection(device->seat, NULL, wlr_source, + wl_display_next_serial(device->seat->display)); +} + +static void control_handle_destroy(struct wl_client *client, + struct wl_resource *control_resource) { + wl_resource_destroy(control_resource); +} + +static const struct ext_data_control_device_v1_interface control_impl = { + .set_selection = control_handle_set_selection, + .set_primary_selection = control_handle_set_primary_selection, + .destroy = control_handle_destroy, +}; + +static void control_send_selection(struct wlr_ext_data_control_device_v1 *device) { + struct wlr_data_source *source = device->seat->selection_source; + + if (device->selection_offer_resource != NULL) { + // Make the offer inert + struct data_offer *offer = data_offer_from_offer_resource( + device->selection_offer_resource); + data_offer_destroy(offer); + } + + device->selection_offer_resource = NULL; + if (source != NULL) { + device->selection_offer_resource = + create_offer(device, &source->mime_types, false); + if (device->selection_offer_resource == NULL) { + wl_resource_post_no_memory(device->resource); + return; + } + } + + ext_data_control_device_v1_send_selection(device->resource, + device->selection_offer_resource); +} + +static void control_send_primary_selection( + struct wlr_ext_data_control_device_v1 *device) { + uint32_t version = wl_resource_get_version(device->resource); + if (version < EXT_DATA_CONTROL_DEVICE_V1_PRIMARY_SELECTION_SINCE_VERSION) { + return; + } + + struct wlr_primary_selection_source *source = + device->seat->primary_selection_source; + + if (device->primary_selection_offer_resource != NULL) { + // Make the offer inert + struct data_offer *offer = data_offer_from_offer_resource( + device->primary_selection_offer_resource); + data_offer_destroy(offer); + } + + device->primary_selection_offer_resource = NULL; + if (source != NULL) { + device->primary_selection_offer_resource = + create_offer(device, &source->mime_types, true); + if (device->primary_selection_offer_resource == NULL) { + wl_resource_post_no_memory(device->resource); + return; + } + } + + ext_data_control_device_v1_send_primary_selection(device->resource, + device->primary_selection_offer_resource); +} + +static void control_handle_resource_destroy(struct wl_resource *resource) { + struct wlr_ext_data_control_device_v1 *device = control_from_resource(resource); + wlr_ext_data_control_device_v1_destroy(device); +} + +static void control_handle_seat_destroy(struct wl_listener *listener, + void *data) { + struct wlr_ext_data_control_device_v1 *device = + wl_container_of(listener, device, seat_destroy); + wlr_ext_data_control_device_v1_destroy(device); +} + +static void control_handle_seat_set_selection(struct wl_listener *listener, + void *data) { + struct wlr_ext_data_control_device_v1 *device = + wl_container_of(listener, device, seat_set_selection); + control_send_selection(device); +} + +static void control_handle_seat_set_primary_selection( + struct wl_listener *listener, + void *data) { + struct wlr_ext_data_control_device_v1 *device = + wl_container_of(listener, device, seat_set_primary_selection); + control_send_primary_selection(device); +} + +void wlr_ext_data_control_device_v1_destroy(struct wlr_ext_data_control_device_v1 *device) { + if (device == NULL) { + return; + } + ext_data_control_device_v1_send_finished(device->resource); + // Make the resources inert + wl_resource_set_user_data(device->resource, NULL); + if (device->selection_offer_resource != NULL) { + struct data_offer *offer = data_offer_from_offer_resource( + device->selection_offer_resource); + data_offer_destroy(offer); + } + if (device->primary_selection_offer_resource != NULL) { + struct data_offer *offer = data_offer_from_offer_resource( + device->primary_selection_offer_resource); + data_offer_destroy(offer); + } + wl_list_remove(&device->seat_destroy.link); + wl_list_remove(&device->seat_set_selection.link); + wl_list_remove(&device->seat_set_primary_selection.link); + wl_list_remove(&device->link); + free(device); +} + + +static const struct ext_data_control_manager_v1_interface manager_impl; + +static struct wlr_ext_data_control_manager_v1 *manager_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &ext_data_control_manager_v1_interface, &manager_impl)); + return wl_resource_get_user_data(resource); +} + +static void manager_handle_create_data_source(struct wl_client *client, + struct wl_resource *manager_resource, uint32_t id) { + struct data_control_source *source = calloc(1, sizeof(*source)); + if (source == NULL) { + wl_resource_post_no_memory(manager_resource); + return; + } + + wl_array_init(&source->mime_types); + + uint32_t version = wl_resource_get_version(manager_resource); + source->resource = wl_resource_create(client, + &ext_data_control_source_v1_interface, version, id); + if (source->resource == NULL) { + wl_resource_post_no_memory(manager_resource); + wl_array_release(&source->mime_types); + free(source); + return; + } + wl_resource_set_implementation(source->resource, &source_impl, source, + source_handle_resource_destroy); +} + +static void manager_handle_get_data_device(struct wl_client *client, + struct wl_resource *manager_resource, uint32_t id, + struct wl_resource *seat_resource) { + struct wlr_ext_data_control_manager_v1 *manager = + manager_from_resource(manager_resource); + struct wlr_seat_client *seat_client = + wlr_seat_client_from_resource(seat_resource); + + uint32_t version = wl_resource_get_version(manager_resource); + struct wl_resource *resource = wl_resource_create(client, + &ext_data_control_device_v1_interface, version, id); + if (resource == NULL) { + wl_resource_post_no_memory(manager_resource); + return; + } + wl_resource_set_implementation(resource, &control_impl, NULL, + control_handle_resource_destroy); + if (seat_client == NULL) { + return; + } + + struct wlr_ext_data_control_device_v1 *device = calloc(1, sizeof(*device)); + if (device == NULL) { + wl_resource_post_no_memory(manager_resource); + return; + } + device->manager = manager; + device->seat = seat_client->seat; + device->resource = resource; + wl_resource_set_user_data(resource, device); + + device->seat_destroy.notify = control_handle_seat_destroy; + wl_signal_add(&device->seat->events.destroy, &device->seat_destroy); + + device->seat_set_selection.notify = control_handle_seat_set_selection; + wl_signal_add(&device->seat->events.set_selection, + &device->seat_set_selection); + + device->seat_set_primary_selection.notify = + control_handle_seat_set_primary_selection; + wl_signal_add(&device->seat->events.set_primary_selection, + &device->seat_set_primary_selection); + + wl_list_insert(&manager->devices, &device->link); + wl_signal_emit_mutable(&manager->events.new_device, device); + + // At this point maybe the compositor decided to destroy the device. If + // it's the case then the resource will be inert. + device = control_from_resource(resource); + if (device != NULL) { + control_send_selection(device); + control_send_primary_selection(device); + } +} + +static void manager_handle_destroy(struct wl_client *client, + struct wl_resource *manager_resource) { + wl_resource_destroy(manager_resource); +} + +static const struct ext_data_control_manager_v1_interface manager_impl = { + .create_data_source = manager_handle_create_data_source, + .get_data_device = manager_handle_get_data_device, + .destroy = manager_handle_destroy, +}; + +static void manager_bind(struct wl_client *client, void *data, uint32_t version, + uint32_t id) { + struct wlr_ext_data_control_manager_v1 *manager = data; + + struct wl_resource *resource = wl_resource_create(client, + &ext_data_control_manager_v1_interface, version, id); + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &manager_impl, manager, NULL); +} + +static void handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_ext_data_control_manager_v1 *manager = + wl_container_of(listener, manager, display_destroy); + + wl_signal_emit_mutable(&manager->events.destroy, manager); + + assert(wl_list_empty(&manager->events.destroy.listener_list)); + + wl_list_remove(&manager->display_destroy.link); + wl_global_destroy(manager->global); + free(manager); +} + +struct wlr_ext_data_control_manager_v1 *wlr_ext_data_control_manager_v1_create( + struct wl_display *display, uint32_t version) { + assert(version <= EXT_DATA_CONTROL_MANAGER_VERSION); + + struct wlr_ext_data_control_manager_v1 *manager = calloc(1, sizeof(*manager)); + if (manager == NULL) { + return NULL; + } + wl_list_init(&manager->devices); + wl_signal_init(&manager->events.destroy); + wl_signal_init(&manager->events.new_device); + + manager->global = wl_global_create(display, + &ext_data_control_manager_v1_interface, + version, manager, manager_bind); + if (manager->global == NULL) { + free(manager); + return NULL; + } + + manager->display_destroy.notify = handle_display_destroy; + wl_display_add_destroy_listener(display, &manager->display_destroy); + + return manager; +} From fef4f3637a5dfb7022887dfb36ccdd69932d2753 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Mon, 27 Jan 2025 17:48:39 +0100 Subject: [PATCH 280/519] seat: Don't forget to destroy touch points on touch up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Otherwise the number of touch points goes up constantly and d'n'd via touch can't work as validation always fails. Fixes 75ecba44 ("seat: add serials to touch up events") Signed-off-by: Guido Günther --- types/seat/wlr_seat_touch.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/types/seat/wlr_seat_touch.c b/types/seat/wlr_seat_touch.c index 03b72f698..192ae5890 100644 --- a/types/seat/wlr_seat_touch.c +++ b/types/seat/wlr_seat_touch.c @@ -214,9 +214,10 @@ uint32_t wlr_seat_touch_notify_up(struct wlr_seat *seat, uint32_t time, return 0; } - return grab->interface->up(grab, time, point); + uint32_t serial = grab->interface->up(grab, time, point); touch_point_destroy(point); + return serial; } void wlr_seat_touch_notify_motion(struct wlr_seat *seat, uint32_t time, From 16f607f0089741300afc755979e1840201146c03 Mon Sep 17 00:00:00 2001 From: James Ramsey Date: Sun, 8 Dec 2024 14:41:37 +0000 Subject: [PATCH 281/519] Implement updated version of ext-idle-notify protocol Signed-off-by: James Ramsey --- types/wlr_idle_notify_v1.c | 43 +++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/types/wlr_idle_notify_v1.c b/types/wlr_idle_notify_v1.c index 686ddccc5..b4b9afcc1 100644 --- a/types/wlr_idle_notify_v1.c +++ b/types/wlr_idle_notify_v1.c @@ -4,7 +4,7 @@ #include #include "ext-idle-notify-v1-protocol.h" -#define IDLE_NOTIFIER_VERSION 1 +#define IDLE_NOTIFIER_VERSION 2 struct wlr_idle_notification_v1 { struct wl_resource *resource; @@ -16,6 +16,7 @@ struct wlr_idle_notification_v1 { struct wl_event_source *timer; bool idle; + bool obey_inhibitors; struct wl_listener seat_destroy; }; @@ -81,7 +82,8 @@ static void notification_destroy(struct wlr_idle_notification_v1 *notification) } static void notification_reset_timer(struct wlr_idle_notification_v1 *notification) { - if (notification->notifier->inhibited) { + if (notification->notifier->inhibited && + notification->obey_inhibitors) { notification_set_idle(notification, false); if (notification->timer != NULL) { wl_event_source_timer_update(notification->timer, 0); @@ -115,9 +117,9 @@ static void notification_handle_resource_destroy(struct wl_resource *resource) { notification_destroy(notification); } -static void notifier_handle_get_idle_notification(struct wl_client *client, +static void construct_idle_notification(struct wl_client *client, struct wl_resource *notifier_resource, uint32_t id, uint32_t timeout, - struct wl_resource *seat_resource) { + struct wl_resource *seat_resource, bool obey_inhibitors) { struct wlr_idle_notifier_v1 *notifier = notifier_from_resource(notifier_resource); struct wlr_seat_client *seat_client = @@ -148,6 +150,7 @@ static void notifier_handle_get_idle_notification(struct wl_client *client, notification->resource = resource; notification->timeout_ms = timeout; notification->seat = seat_client->seat; + notification->obey_inhibitors = obey_inhibitors; if (timeout > 0) { struct wl_display *display = wl_client_get_display(client); @@ -170,9 +173,28 @@ static void notifier_handle_get_idle_notification(struct wl_client *client, notification_reset_timer(notification); } +static void notifier_handle_get_input_idle_notification( + struct wl_client *client, + struct wl_resource *notifier_resource, uint32_t id, + uint32_t timeout, struct wl_resource *seat_resource) { + construct_idle_notification(client, notifier_resource, id, + timeout, seat_resource, false); +} + +static void notifier_handle_get_idle_notification( + struct wl_client *client, + struct wl_resource *notifier_resource, uint32_t id, + uint32_t timeout, struct wl_resource *seat_resource) { + construct_idle_notification(client, notifier_resource, id, + timeout, seat_resource, true); +} + static const struct ext_idle_notifier_v1_interface notifier_impl = { .destroy = resource_handle_destroy, - .get_idle_notification = notifier_handle_get_idle_notification, + .get_idle_notification = + notifier_handle_get_idle_notification, + .get_input_idle_notification = + notifier_handle_get_input_idle_notification, }; static void notifier_bind(struct wl_client *client, void *data, @@ -227,19 +249,20 @@ void wlr_idle_notifier_v1_set_inhibited(struct wlr_idle_notifier_v1 *notifier, struct wlr_idle_notification_v1 *notification; wl_list_for_each(notification, ¬ifier->notifications, link) { - notification_reset_timer(notification); + if (notification->obey_inhibitors) { + notification_reset_timer(notification); + } } } void wlr_idle_notifier_v1_notify_activity(struct wlr_idle_notifier_v1 *notifier, struct wlr_seat *seat) { - if (notifier->inhibited) { - return; - } struct wlr_idle_notification_v1 *notification; wl_list_for_each(notification, ¬ifier->notifications, link) { - if (notification->seat == seat) { + if (notification->seat == seat && + !(notifier->inhibited && + notification->obey_inhibitors)) { notification_handle_activity(notification); } } From edd8df76d89f127ec95e040c89049850e71166c9 Mon Sep 17 00:00:00 2001 From: James Ramsey Date: Thu, 30 Jan 2025 11:11:53 -0500 Subject: [PATCH 282/519] Bump required w-p version to 1.40 Signed-off-by: James Ramsey --- protocol/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/meson.build b/protocol/meson.build index f3b634ae8..a84f9f42a 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -1,5 +1,5 @@ wayland_protos = dependency('wayland-protocols', - version: '>=1.38', + version: '>=1.40', fallback: 'wayland-protocols', default_options: ['tests=false'], ) From dc7dba8b1fde967b8da9ad1703001fa5bd2266d9 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 4 Feb 2025 10:01:08 +0100 Subject: [PATCH 283/519] scene/surface: Do not use buffer dimensions for clip The surface's buffer dimensions were used to scale the clip's x/y offset. If a surface had a larger buffer than src_box, the calculations to scale the x/y portion of the clip would be incorrect, yielding graphical glitches. This was noticed with Chromium in sway, which during resize uses a viewport with a src_box to avoid immediate buffer reallocation. While the viewport was in use, the surface would be shifted so that too much content was cropped in the upper left, and damage glitching was visible in the lower right. Use the buffer source box dimensions instead. --- types/scene/surface.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/types/scene/surface.c b/types/scene/surface.c index 2aff5af37..ae020f07d 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -129,8 +129,8 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { buffer_width, buffer_height); wlr_output_transform_coords(state->transform, &buffer_width, &buffer_height); - src_box.x += (double)(clip->x * buffer_width) / state->width; - src_box.y += (double)(clip->y * buffer_height) / state->height; + src_box.x += (double)(clip->x * src_box.width) / state->width; + src_box.y += (double)(clip->y * src_box.height) / state->height; src_box.width *= (double)width / state->width; src_box.height *= (double)height / state->height; From 1380a48b4dc4d59cc71db95c8097c0009591e6f2 Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 13 Feb 2025 14:12:27 +0000 Subject: [PATCH 284/519] Declare struct wlr_surface in a less weird place wlr_compositor.h contains references to `struct wlr_surface` in function arguments before it actually defines it. This generally works because wlr_compositor.h includes wlr_output.h which contains a forward-declaration for `struct wlr_surface` (despite not actually referencing it). This is all pretty weird, and gives very confusing errors if you manage to end up with wlr_output.h including wlr_compositor.h (eg. via an indirect route) so make it less weird. --- include/wlr/types/wlr_compositor.h | 2 ++ include/wlr/types/wlr_cursor.h | 1 + include/wlr/types/wlr_output.h | 2 -- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/wlr/types/wlr_compositor.h b/include/wlr/types/wlr_compositor.h index 63a8c16ce..f30646bed 100644 --- a/include/wlr/types/wlr_compositor.h +++ b/include/wlr/types/wlr_compositor.h @@ -18,6 +18,8 @@ #include #include +struct wlr_surface; + enum wlr_surface_state_field { WLR_SURFACE_STATE_BUFFER = 1 << 0, WLR_SURFACE_STATE_SURFACE_DAMAGE = 1 << 1, diff --git a/include/wlr/types/wlr_cursor.h b/include/wlr/types/wlr_cursor.h index 57d6e508d..d02687685 100644 --- a/include/wlr/types/wlr_cursor.h +++ b/include/wlr/types/wlr_cursor.h @@ -14,6 +14,7 @@ #include struct wlr_input_device; +struct wlr_surface; struct wlr_xcursor_manager; /** diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index e0ee7d092..9c08a2c34 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -296,8 +296,6 @@ struct wlr_output_event_request_state { const struct wlr_output_state *state; }; -struct wlr_surface; - void wlr_output_create_global(struct wlr_output *output, struct wl_display *display); void wlr_output_destroy_global(struct wlr_output *output); /** From c9d6339b605391728772d8a2ca5baf4f66f2d9bb Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 13 Feb 2025 15:12:17 +0000 Subject: [PATCH 285/519] Fix includes of wlr_output.h Remove unneeded includes of wlr_output.h from wlr_compositor.h and wlr_cursor.h (unneeded now that we forward-declare struct wlr_surface) and put the actually-required includes in the right places. --- include/wlr/types/wlr_compositor.h | 1 - include/wlr/types/wlr_cursor.h | 1 - types/scene/surface.c | 1 + types/wlr_linux_drm_syncobj_v1.c | 1 + types/xdg_shell/wlr_xdg_toplevel.c | 1 + 5 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/wlr/types/wlr_compositor.h b/include/wlr/types/wlr_compositor.h index f30646bed..9331e8af6 100644 --- a/include/wlr/types/wlr_compositor.h +++ b/include/wlr/types/wlr_compositor.h @@ -14,7 +14,6 @@ #include #include #include -#include #include #include diff --git a/include/wlr/types/wlr_cursor.h b/include/wlr/types/wlr_cursor.h index d02687685..041f735cf 100644 --- a/include/wlr/types/wlr_cursor.h +++ b/include/wlr/types/wlr_cursor.h @@ -11,7 +11,6 @@ #include #include -#include struct wlr_input_device; struct wlr_surface; diff --git a/types/scene/surface.c b/types/scene/surface.c index ae020f07d..e6d0fb8e2 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include "types/wlr_scene.h" diff --git a/types/wlr_linux_drm_syncobj_v1.c b/types/wlr_linux_drm_syncobj_v1.c index a10e6a450..7873a09c4 100644 --- a/types/wlr_linux_drm_syncobj_v1.c +++ b/types/wlr_linux_drm_syncobj_v1.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include diff --git a/types/xdg_shell/wlr_xdg_toplevel.c b/types/xdg_shell/wlr_xdg_toplevel.c index f7bc52cc1..950a22b2d 100644 --- a/types/xdg_shell/wlr_xdg_toplevel.c +++ b/types/xdg_shell/wlr_xdg_toplevel.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include "types/wlr_xdg_shell.h" From e83b06e73295b8b04840c23c4756cacc2363ebb0 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 23 Feb 2025 15:39:10 +0100 Subject: [PATCH 286/519] render/vulkan: unify alpha pre-multiplication in output shader Instead of handling alpha pre-multiplication in each branch, add some common logic before and after handling OUTPUT_TRANSFORM. --- render/vulkan/shaders/output.frag | 39 ++++++++++++++----------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/render/vulkan/shaders/output.frag b/render/vulkan/shaders/output.frag index 2a744aa24..e351079da 100644 --- a/render/vulkan/shaders/output.frag +++ b/render/vulkan/shaders/output.frag @@ -23,38 +23,35 @@ float linear_channel_to_srgb(float x) { return max(min(x * 12.92, 0.04045), 1.055 * pow(x, 1. / 2.4) - 0.055); } -vec4 linear_color_to_srgb(vec4 color) { - if (color.a == 0) { - return vec4(0); - } - color.rgb /= color.a; - color.rgb = vec3( +vec3 linear_color_to_srgb(vec3 color) { + return vec3( linear_channel_to_srgb(color.r), linear_channel_to_srgb(color.g), linear_channel_to_srgb(color.b) ); - color.rgb *= color.a; - return color; } void main() { - vec4 val = subpassLoad(in_color).rgba; - if (OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_LUT_3D) { - if (val.a == 0) { - out_color = vec4(0); - return; - } - // Convert from pre-multiplied alpha to straight alpha - vec3 rgb = val.rgb / val.a; + vec4 in_color = subpassLoad(in_color).rgba; + // Convert from pre-multiplied alpha to straight alpha + float alpha = in_color.a; + vec3 rgb; + if (alpha == 0) { + rgb = vec3(0); + } else { + rgb = in_color.rgb / alpha; + } + + if (OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_LUT_3D) { // Apply 3D LUT vec3 pos = data.lut_3d_offset + rgb * data.lut_3d_scale; rgb = texture(lut_3d, pos).rgb; - - // Back to pre-multiplied alpha - out_color = vec4(rgb * val.a, val.a); } else { // OUTPUT_TRANSFORM_INVERSE_SRGB - // Produce post-premultiplied sRGB encoded values - out_color = linear_color_to_srgb(val); + // Produce sRGB encoded values + rgb = linear_color_to_srgb(rgb); } + + // Back to pre-multiplied alpha + out_color = vec4(rgb * alpha, alpha); } From 90b5f0dde5be0ae62eed8f4099be8a008cb215d9 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Fri, 24 Jan 2025 19:28:58 +0300 Subject: [PATCH 287/519] backend/wayland: don't send request_state when enabling an output --- backend/wayland/output.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/backend/wayland/output.c b/backend/wayland/output.c index bbe0230f4..bb645dd2f 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -1035,6 +1035,12 @@ static void xdg_surface_handle_configure(void *data, output->has_configure_serial = true; output->configure_serial = serial; + if (!output->wlr_output.enabled) { + // We're waiting for a configure event after an initial commit to enable + // the output. Do not notify the compositor about the requested state. + return; + } + struct wlr_output_state state; wlr_output_state_init(&state); wlr_output_state_set_custom_mode(&state, req_width, req_height, 0); From 35ff09a7545760189b737f5cdd3da7ca16384fac Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Fri, 24 Jan 2025 19:34:17 +0300 Subject: [PATCH 288/519] backend/output: extract backend to a variable in output_commit() --- backend/wayland/output.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/backend/wayland/output.c b/backend/wayland/output.c index bb645dd2f..e14b66a4c 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -714,15 +714,15 @@ static const struct wl_callback_listener unmap_callback_listener = { .done = unmap_callback_handle_done, }; -static bool output_commit(struct wlr_output *wlr_output, - const struct wlr_output_state *state) { - struct wlr_wl_output *output = - get_wl_output_from_output(wlr_output); +static bool output_commit(struct wlr_output *wlr_output, const struct wlr_output_state *state) { + struct wlr_wl_output *output = get_wl_output_from_output(wlr_output); if (!output_test(wlr_output, state)) { return false; } + struct wlr_wl_backend *wl = output->backend; + bool pending_enabled = output_pending_enabled(wlr_output, state); if (wlr_output->enabled && !pending_enabled) { @@ -747,9 +747,9 @@ static bool output_commit(struct wlr_output *wlr_output, wl_surface_commit(output->surface); output->initialized = true; - wl_display_flush(output->backend->remote_display); + wl_display_flush(wl->remote_display); while (!output->configured) { - if (wl_display_dispatch(output->backend->remote_display) == -1) { + if (wl_display_dispatch(wl->remote_display) == -1) { wlr_log(WLR_ERROR, "wl_display_dispatch() failed"); return false; } @@ -764,7 +764,7 @@ static bool output_commit(struct wlr_output *wlr_output, } struct wlr_buffer *wlr_buffer = state->buffer; - buffer = get_or_create_wl_buffer(output->backend, wlr_buffer); + buffer = get_or_create_wl_buffer(wl, wlr_buffer); if (buffer == NULL) { return false; } @@ -775,23 +775,23 @@ static bool output_commit(struct wlr_output *wlr_output, if (state->committed & WLR_OUTPUT_STATE_WAIT_TIMELINE) { struct wlr_wl_drm_syncobj_timeline *wait_timeline = - get_or_create_drm_syncobj_timeline(output->backend, state->wait_timeline); + get_or_create_drm_syncobj_timeline(wl, state->wait_timeline); struct wlr_wl_drm_syncobj_timeline *signal_timeline; uint64_t signal_point; if (state->committed & WLR_OUTPUT_STATE_SIGNAL_TIMELINE) { - signal_timeline = get_or_create_drm_syncobj_timeline(output->backend, state->signal_timeline); + signal_timeline = get_or_create_drm_syncobj_timeline(wl, state->signal_timeline); signal_point = state->signal_point; } else { if (buffer->fallback_signal_timeline == NULL) { buffer->fallback_signal_timeline = - wlr_drm_syncobj_timeline_create(output->backend->drm_fd); + wlr_drm_syncobj_timeline_create(wl->drm_fd); if (buffer->fallback_signal_timeline == NULL) { return false; } } signal_timeline = - get_or_create_drm_syncobj_timeline(output->backend, buffer->fallback_signal_timeline); + get_or_create_drm_syncobj_timeline(wl, buffer->fallback_signal_timeline); signal_point = ++buffer->fallback_signal_point; } @@ -801,7 +801,7 @@ static bool output_commit(struct wlr_output *wlr_output, if (output->drm_syncobj_surface_v1 == NULL) { output->drm_syncobj_surface_v1 = wp_linux_drm_syncobj_manager_v1_get_surface( - output->backend->drm_syncobj_manager_v1, output->surface); + wl->drm_syncobj_manager_v1, output->surface); if (output->drm_syncobj_surface_v1 == NULL) { return false; } @@ -818,7 +818,7 @@ static bool output_commit(struct wlr_output *wlr_output, signal_timeline->wl, signal_point_hi, signal_point_lo); if (!wlr_drm_syncobj_timeline_waiter_init(&buffer->drm_syncobj_waiter, - signal_timeline->base, signal_point, 0, output->backend->event_loop, + signal_timeline->base, signal_point, 0, wl->event_loop, buffer_handle_drm_syncobj_ready)) { return false; } @@ -843,9 +843,8 @@ static bool output_commit(struct wlr_output *wlr_output, wl_callback_add_listener(output->frame_callback, &frame_listener, output); struct wp_presentation_feedback *wp_feedback = NULL; - if (output->backend->presentation != NULL) { - wp_feedback = wp_presentation_feedback(output->backend->presentation, - output->surface); + if (wl->presentation != NULL) { + wp_feedback = wp_presentation_feedback(wl->presentation, output->surface); } if (output->has_configure_serial) { @@ -878,7 +877,7 @@ static bool output_commit(struct wlr_output *wlr_output, } } - wl_display_flush(output->backend->remote_display); + wl_display_flush(wl->remote_display); return true; } From b13fe9b3a1e8832fbfd821db71f4fbe5484f96f7 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Fri, 24 Jan 2025 19:36:47 +0300 Subject: [PATCH 289/519] backend/wayland: use a separate event queue for busy loops This avoids processing events which we're not interested in. Specifically, this fixes a case where output_commit() could be indirectly called from itself either from import_dmabuf() or while waiting for a configure event when enabling the output. --- backend/wayland/backend.c | 11 ++++++++++- backend/wayland/output.c | 33 +++++++++++++++++++++++++++------ include/backend/wayland.h | 1 + 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/backend/wayland/backend.c b/backend/wayland/backend.c index 978209995..ff95a7b4b 100644 --- a/backend/wayland/backend.c +++ b/backend/wayland/backend.c @@ -555,6 +555,7 @@ static void backend_destroy(struct wlr_backend *backend) { wl_compositor_destroy(wl->compositor); wl_registry_destroy(wl->registry); wl_display_flush(wl->remote_display); + wl_event_queue_destroy(wl->busy_loop_queue); if (wl->own_remote_display) { wl_display_disconnect(wl->remote_display); } @@ -610,10 +611,16 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_event_loop *loop, wl->own_remote_display = true; } + wl->busy_loop_queue = wl_display_create_queue(wl->remote_display); + if (wl->busy_loop_queue == NULL) { + wlr_log_errno(WLR_ERROR, "Could not create a Wayland event queue"); + goto error_display; + } + wl->registry = wl_display_get_registry(wl->remote_display); if (!wl->registry) { wlr_log_errno(WLR_ERROR, "Could not obtain reference to remote registry"); - goto error_display; + goto error_queue; } wl_registry_add_listener(wl->registry, ®istry_listener, wl); @@ -715,6 +722,8 @@ error_registry: xdg_wm_base_destroy(wl->xdg_wm_base); } wl_registry_destroy(wl->registry); +error_queue: + wl_event_queue_destroy(wl->busy_loop_queue); error_display: if (wl->own_remote_display) { wl_display_disconnect(wl->remote_display); diff --git a/backend/wayland/output.c b/backend/wayland/output.c index e14b66a4c..fb4d1f914 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -243,15 +243,24 @@ static struct wl_buffer *import_dmabuf(struct wlr_wl_backend *wl, zwp_linux_buffer_params_v1_add_listener(params, &dmabuf_listener, &data); zwp_linux_buffer_params_v1_create(params, dmabuf->width, dmabuf->height, dmabuf->format, 0); + struct wl_event_queue *display_queue = + wl_proxy_get_queue((struct wl_proxy *)wl->remote_display); + wl_proxy_set_queue((struct wl_proxy *)params, wl->busy_loop_queue); + while (!data.done) { - if (wl_display_dispatch(wl->remote_display) < 0) { - wlr_log(WLR_ERROR, "wl_display_dispatch() failed"); + if (wl_display_dispatch_queue(wl->remote_display, wl->busy_loop_queue) < 0) { + wlr_log(WLR_ERROR, "wl_display_dispatch_queue() failed"); break; } } + struct wl_buffer *buffer = data.wl_buffer; + if (buffer != NULL) { + wl_proxy_set_queue((struct wl_proxy *)buffer, display_queue); + } + zwp_linux_buffer_params_v1_destroy(params); - return data.wl_buffer; + return buffer; } static struct wl_buffer *import_shm(struct wlr_wl_backend *wl, @@ -747,13 +756,25 @@ static bool output_commit(struct wlr_output *wlr_output, const struct wlr_output wl_surface_commit(output->surface); output->initialized = true; + struct wl_event_queue *display_queue = + wl_proxy_get_queue((struct wl_proxy *)wl->remote_display); + wl_proxy_set_queue((struct wl_proxy *)output->xdg_surface, wl->busy_loop_queue); + wl_proxy_set_queue((struct wl_proxy *)output->xdg_toplevel, wl->busy_loop_queue); + wl_display_flush(wl->remote_display); while (!output->configured) { - if (wl_display_dispatch(wl->remote_display) == -1) { - wlr_log(WLR_ERROR, "wl_display_dispatch() failed"); - return false; + if (wl_display_dispatch_queue(wl->remote_display, wl->busy_loop_queue) == -1) { + wlr_log(WLR_ERROR, "wl_display_dispatch_queue() failed"); + break; } } + + wl_proxy_set_queue((struct wl_proxy *)output->xdg_surface, display_queue); + wl_proxy_set_queue((struct wl_proxy *)output->xdg_toplevel, display_queue); + + if (!output->configured) { + return false; + } } struct wlr_wl_buffer *buffer = NULL; diff --git a/include/backend/wayland.h b/include/backend/wayland.h index daa98ef5e..e24eb9fdf 100644 --- a/include/backend/wayland.h +++ b/include/backend/wayland.h @@ -22,6 +22,7 @@ struct wlr_wl_backend { /* local state */ bool started; struct wl_event_loop *event_loop; + struct wl_event_queue *busy_loop_queue; struct wl_list outputs; int drm_fd; struct wl_list buffers; // wlr_wl_buffer.link From 66dfb7f49b0e18150b109a75649fc85a487f15bc Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Sat, 29 Jun 2024 15:12:44 +0300 Subject: [PATCH 290/519] xdg-shell,layer-shell: assert that configure events are only sent to initialized surfaces This helps to ensure that compositors behave correctly and don't confuse clients. --- types/wlr_layer_shell_v1.c | 5 +---- types/xdg_shell/wlr_xdg_surface.c | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/types/wlr_layer_shell_v1.c b/types/wlr_layer_shell_v1.c index b6181c6dc..2d8384981 100644 --- a/types/wlr_layer_shell_v1.c +++ b/types/wlr_layer_shell_v1.c @@ -315,10 +315,7 @@ static const struct zwlr_layer_surface_v1_interface layer_surface_implementation uint32_t wlr_layer_surface_v1_configure(struct wlr_layer_surface_v1 *surface, uint32_t width, uint32_t height) { - if (!surface->initialized) { - wlr_log(WLR_ERROR, "A configure is sent to an uninitialized wlr_layer_surface_v1 %p", - surface); - } + assert(surface->initialized); struct wl_display *display = wl_client_get_display(wl_resource_get_client(surface->resource)); diff --git a/types/xdg_shell/wlr_xdg_surface.c b/types/xdg_shell/wlr_xdg_surface.c index a752ba316..5652fc26c 100644 --- a/types/xdg_shell/wlr_xdg_surface.c +++ b/types/xdg_shell/wlr_xdg_surface.c @@ -165,10 +165,7 @@ uint32_t wlr_xdg_surface_schedule_configure(struct wlr_xdg_surface *surface) { struct wl_display *display = wl_client_get_display(surface->client->client); struct wl_event_loop *loop = wl_display_get_event_loop(display); - if (!surface->initialized) { - wlr_log(WLR_ERROR, "A configure is scheduled for an uninitialized xdg_surface %p", - surface); - } + assert(surface->initialized); if (surface->configure_idle == NULL) { surface->scheduled_serial = wl_display_next_serial(display); From 602a00ec1f7d6c5a95204c182440230a0a60af61 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Mon, 3 Mar 2025 13:53:45 +0300 Subject: [PATCH 291/519] scene: don't always round scaled regions up It makes sense for damage, less so for opaque regions. --- types/scene/wlr_scene.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 62ff576bf..96ec20141 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -14,7 +14,6 @@ #include #include #include -#include "types/wlr_buffer.h" #include "types/wlr_output.h" #include "types/wlr_scene.h" #include "util/array.h" @@ -311,10 +310,10 @@ static uint32_t region_area(pixman_region32_t *region) { return area; } -static void scale_output_damage(pixman_region32_t *damage, float scale) { +static void scale_output_damage(pixman_region32_t *damage, float scale, bool round_up) { wlr_region_scale(damage, damage, scale); - if (floor(scale) != scale) { + if (round_up && floor(scale) != scale) { wlr_region_expand(damage, damage, 1); } } @@ -331,9 +330,10 @@ struct render_data { pixman_region32_t damage; }; -static void logical_to_buffer_coords(pixman_region32_t *damage, const struct render_data *data) { +static void logical_to_buffer_coords(pixman_region32_t *damage, const struct render_data *data, + bool round_up) { enum wl_output_transform transform = wlr_output_transform_invert(data->transform); - scale_output_damage(damage, data->scale); + scale_output_damage(damage, data->scale, round_up); wlr_region_transform(damage, damage, transform, data->trans_width, data->trans_height); } @@ -402,7 +402,7 @@ static void scene_damage_outputs(struct wlr_scene *scene, pixman_region32_t *dam pixman_region32_copy(&output_damage, damage); pixman_region32_translate(&output_damage, -scene_output->x, -scene_output->y); - scale_output_damage(&output_damage, scene_output->output->scale); + scale_output_damage(&output_damage, scene_output->output->scale, true); output_to_buffer_coords(&output_damage, scene_output->output); scene_output_damage(scene_output, &output_damage); pixman_region32_fini(&output_damage); @@ -959,7 +959,7 @@ void wlr_scene_buffer_set_buffer_with_options(struct wlr_scene_buffer *scene_buf pixman_region32_t cull_region; pixman_region32_init(&cull_region); pixman_region32_copy(&cull_region, &scene_buffer->node.visible); - scale_output_damage(&cull_region, output_scale); + scale_output_damage(&cull_region, output_scale, true); pixman_region32_translate(&cull_region, -lx * output_scale, -ly * output_scale); pixman_region32_intersect(&output_damage, &output_damage, &cull_region); pixman_region32_fini(&cull_region); @@ -1342,7 +1342,7 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren pixman_region32_init(&render_region); pixman_region32_copy(&render_region, &node->visible); pixman_region32_translate(&render_region, -data->logical.x, -data->logical.y); - logical_to_buffer_coords(&render_region, data); + logical_to_buffer_coords(&render_region, data, true); pixman_region32_intersect(&render_region, &render_region, &data->damage); if (pixman_region32_empty(&render_region)) { pixman_region32_fini(&render_region); @@ -1362,7 +1362,7 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren pixman_region32_t opaque; pixman_region32_init(&opaque); scene_node_opaque_region(node, x, y, &opaque); - logical_to_buffer_coords(&opaque, data); + logical_to_buffer_coords(&opaque, data, false); pixman_region32_subtract(&opaque, &render_region, &opaque); switch (node->type) { @@ -2181,7 +2181,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, pixman_region32_intersect(&opaque, &opaque, &entry->node->visible); pixman_region32_translate(&opaque, -scene_output->x, -scene_output->y); - logical_to_buffer_coords(&opaque, &render_data); + logical_to_buffer_coords(&opaque, &render_data, false); pixman_region32_subtract(&background, &background, &opaque); pixman_region32_fini(&opaque); } From a9542b9565260a84c148f277fd9d6a0548405489 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Mon, 3 Mar 2025 15:39:27 +0300 Subject: [PATCH 292/519] scene: don't mention damage in region-scaling functions --- types/scene/wlr_scene.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 96ec20141..d64ee4ada 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -310,11 +310,11 @@ static uint32_t region_area(pixman_region32_t *region) { return area; } -static void scale_output_damage(pixman_region32_t *damage, float scale, bool round_up) { - wlr_region_scale(damage, damage, scale); +static void scale_region(pixman_region32_t *region, float scale, bool round_up) { + wlr_region_scale(region, region, scale); if (round_up && floor(scale) != scale) { - wlr_region_expand(damage, damage, 1); + wlr_region_expand(region, region, 1); } } @@ -330,11 +330,11 @@ struct render_data { pixman_region32_t damage; }; -static void logical_to_buffer_coords(pixman_region32_t *damage, const struct render_data *data, +static void logical_to_buffer_coords(pixman_region32_t *region, const struct render_data *data, bool round_up) { enum wl_output_transform transform = wlr_output_transform_invert(data->transform); - scale_output_damage(damage, data->scale, round_up); - wlr_region_transform(damage, damage, transform, data->trans_width, data->trans_height); + scale_region(region, data->scale, round_up); + wlr_region_transform(region, region, transform, data->trans_width, data->trans_height); } static void output_to_buffer_coords(pixman_region32_t *damage, struct wlr_output *output) { @@ -402,7 +402,7 @@ static void scene_damage_outputs(struct wlr_scene *scene, pixman_region32_t *dam pixman_region32_copy(&output_damage, damage); pixman_region32_translate(&output_damage, -scene_output->x, -scene_output->y); - scale_output_damage(&output_damage, scene_output->output->scale, true); + scale_region(&output_damage, scene_output->output->scale, true); output_to_buffer_coords(&output_damage, scene_output->output); scene_output_damage(scene_output, &output_damage); pixman_region32_fini(&output_damage); @@ -959,7 +959,7 @@ void wlr_scene_buffer_set_buffer_with_options(struct wlr_scene_buffer *scene_buf pixman_region32_t cull_region; pixman_region32_init(&cull_region); pixman_region32_copy(&cull_region, &scene_buffer->node.visible); - scale_output_damage(&cull_region, output_scale, true); + scale_region(&cull_region, output_scale, true); pixman_region32_translate(&cull_region, -lx * output_scale, -ly * output_scale); pixman_region32_intersect(&output_damage, &output_damage, &cull_region); pixman_region32_fini(&cull_region); From 350507982346fc70a3ecae24f97422ff0fa7185d Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 6 Mar 2025 10:32:09 +0100 Subject: [PATCH 293/519] ci: fetch gyosu from new upstream gyosu has moved to Codeberg. --- .builds/alpine.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.builds/alpine.yml b/.builds/alpine.yml index 399adc84f..affd85411 100644 --- a/.builds/alpine.yml +++ b/.builds/alpine.yml @@ -43,7 +43,7 @@ tasks: cd wlroots/tinywl make - docs: | - go install 'git.sr.ht/~emersion/gyosu@latest' + go install 'codeberg.org/emersion/gyosu@latest' include_dir="$(echo /usr/local/include/wlroots-*)" ~/go/bin/gyosu \ -DWLR_USE_UNSTABLE \ From 3c76b93272077bed3eb6caa90b738e22e033d068 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 6 Mar 2025 11:30:55 +0100 Subject: [PATCH 294/519] output/cursor: drop output_cursor_reset() This function is ill-named: it doesn't reset anything. It probably contained more logic in the past and got stripped down. --- types/output/cursor.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/types/output/cursor.c b/types/output/cursor.c index af1663574..137dab736 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -149,12 +149,6 @@ static void output_cursor_damage_whole(struct wlr_output_cursor *cursor) { pixman_region32_fini(&damage); } -static void output_cursor_reset(struct wlr_output_cursor *cursor) { - if (cursor->output->hardware_cursor != cursor) { - output_cursor_damage_whole(cursor); - } -} - static void output_cursor_update_visible(struct wlr_output_cursor *cursor) { struct wlr_box output_box; output_box.x = output_box.y = 0; @@ -382,7 +376,9 @@ bool output_cursor_set_texture(struct wlr_output_cursor *cursor, struct wlr_drm_syncobj_timeline *wait_timeline, uint64_t wait_point) { struct wlr_output *output = cursor->output; - output_cursor_reset(cursor); + if (cursor->output->hardware_cursor != cursor) { + output_cursor_damage_whole(cursor); + } cursor->enabled = texture != NULL; if (texture != NULL) { @@ -481,10 +477,11 @@ void wlr_output_cursor_destroy(struct wlr_output_cursor *cursor) { if (cursor == NULL) { return; } - output_cursor_reset(cursor); if (cursor->output->hardware_cursor == cursor) { // If this cursor was the hardware cursor, disable it output_disable_hardware_cursor(cursor->output); + } else { + output_cursor_damage_whole(cursor); } wl_list_remove(&cursor->renderer_destroy.link); if (cursor->own_texture) { From a63e21d94c96a8444ec9b10c6d54d5606952d83c Mon Sep 17 00:00:00 2001 From: yuiiio Date: Thu, 6 Mar 2025 21:06:56 +0900 Subject: [PATCH 295/519] output: don't update disabled cursor texture --- types/output/cursor.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/types/output/cursor.c b/types/output/cursor.c index 137dab736..154b91120 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -374,6 +374,11 @@ bool output_cursor_set_texture(struct wlr_output_cursor *cursor, int dst_width, int dst_height, enum wl_output_transform transform, int32_t hotspot_x, int32_t hotspot_y, struct wlr_drm_syncobj_timeline *wait_timeline, uint64_t wait_point) { + if (texture == NULL && !cursor->enabled) { + // Cursor is still disabled, do nothing + return true; + } + struct wlr_output *output = cursor->output; if (cursor->output->hardware_cursor != cursor) { From e752e3ec06fc7c2b83079a5980218ae359b1c869 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Tue, 4 Mar 2025 20:37:49 +0100 Subject: [PATCH 296/519] xwm: Handle NET_WM_WINDOW_OPACITY MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lot of clients use it (e.g. both Qt and GTK) although it never made it into the spec at https://specifications.freedesktop.org/wm-spec/latest-single/ until recently https://gitlab.freedesktop.org/xdg/xdg-specs/-/merge_requests/97 Signed-off-by: Guido Günther --- include/wlr/xwayland/xwayland.h | 2 ++ include/xwayland/xwm.h | 1 + xwayland/xwm.c | 22 ++++++++++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/include/wlr/xwayland/xwayland.h b/include/wlr/xwayland/xwayland.h index 4238c1f07..b1b2247e6 100644 --- a/include/wlr/xwayland/xwayland.h +++ b/include/wlr/xwayland/xwayland.h @@ -138,6 +138,7 @@ struct wlr_xwayland_surface { int16_t x, y; uint16_t width, height; bool override_redirect; + float opacity; char *title; char *class; @@ -220,6 +221,7 @@ struct wlr_xwayland_surface { struct wl_signal set_strut_partial; struct wl_signal set_override_redirect; struct wl_signal set_geometry; + struct wl_signal set_opacity; struct wl_signal focus_in; struct wl_signal grab_focus; /* can be used to set initial maximized/fullscreen geometry */ diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h index 2a31902ca..799cebc47 100644 --- a/include/xwayland/xwm.h +++ b/include/xwayland/xwm.h @@ -70,6 +70,7 @@ enum atom_name { NET_STARTUP_ID, NET_STARTUP_INFO, NET_STARTUP_INFO_BEGIN, + NET_WM_WINDOW_OPACITY, NET_WM_WINDOW_TYPE_NORMAL, NET_WM_WINDOW_TYPE_UTILITY, NET_WM_WINDOW_TYPE_TOOLTIP, diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 08faed9b6..4b085d040 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -69,6 +69,7 @@ static const char *const atom_map[ATOM_LAST] = { [NET_STARTUP_ID] = "_NET_STARTUP_ID", [NET_STARTUP_INFO] = "_NET_STARTUP_INFO", [NET_STARTUP_INFO_BEGIN] = "_NET_STARTUP_INFO_BEGIN", + [NET_WM_WINDOW_OPACITY] = "_NET_WM_WINDOW_OPACITY", [NET_WM_WINDOW_TYPE_NORMAL] = "_NET_WM_WINDOW_TYPE_NORMAL", [NET_WM_WINDOW_TYPE_UTILITY] = "_NET_WM_WINDOW_TYPE_UTILITY", [NET_WM_WINDOW_TYPE_TOOLTIP] = "_NET_WM_WINDOW_TYPE_TOOLTIP", @@ -205,6 +206,7 @@ static struct wlr_xwayland_surface *xwayland_surface_create( surface->width = width; surface->height = height; surface->override_redirect = override_redirect; + surface->opacity = 1.0; wl_list_init(&surface->children); wl_list_init(&surface->stack_link); wl_list_init(&surface->parent_link); @@ -239,6 +241,7 @@ static struct wlr_xwayland_surface *xwayland_surface_create( wl_signal_init(&surface->events.set_strut_partial); wl_signal_init(&surface->events.set_override_redirect); wl_signal_init(&surface->events.set_geometry); + wl_signal_init(&surface->events.set_opacity); wl_signal_init(&surface->events.focus_in); wl_signal_init(&surface->events.grab_focus); wl_signal_init(&surface->events.map_request); @@ -599,6 +602,7 @@ static void xwayland_surface_destroy(struct wlr_xwayland_surface *xsurface) { assert(wl_list_empty(&xsurface->events.set_strut_partial.listener_list)); assert(wl_list_empty(&xsurface->events.set_override_redirect.listener_list)); assert(wl_list_empty(&xsurface->events.set_geometry.listener_list)); + assert(wl_list_empty(&xsurface->events.set_opacity.listener_list)); assert(wl_list_empty(&xsurface->events.focus_in.listener_list)); assert(wl_list_empty(&xsurface->events.grab_focus.listener_list)); assert(wl_list_empty(&xsurface->events.map_request.listener_list)); @@ -690,6 +694,22 @@ static void read_surface_startup_id(struct wlr_xwm *xwm, wl_signal_emit_mutable(&xsurface->events.set_startup_id, NULL); } +static void read_surface_opacity(struct wlr_xwm *xwm, + struct wlr_xwayland_surface *xsurface, + xcb_get_property_reply_t *reply) { + if (reply->type == XCB_ATOM_CARDINAL && reply->format == 32 && + xcb_get_property_value_length(reply) == + sizeof(uint32_t)) { + uint32_t *val = xcb_get_property_value(reply); + + xsurface->opacity = (double)*val / UINT32_MAX; + wl_signal_emit_mutable(&xsurface->events.set_opacity, NULL); + } else if (reply->type == XCB_ATOM_NONE) { + xsurface->opacity = 1.0; + wl_signal_emit_mutable(&xsurface->events.set_opacity, NULL); + } +} + static void read_surface_role(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, xcb_get_property_reply_t *reply) { @@ -1018,6 +1038,8 @@ static void read_surface_property(struct wlr_xwm *xwm, read_surface_role(xwm, xsurface, reply); } else if (property == xwm->atoms[NET_STARTUP_ID]) { read_surface_startup_id(xwm, xsurface, reply); + } else if (property == xwm->atoms[NET_WM_WINDOW_OPACITY]) { + read_surface_opacity(xwm, xsurface, reply); } else if (wlr_log_get_verbosity() >= WLR_DEBUG) { char *prop_name = xwm_get_atom_name(xwm, property); wlr_log(WLR_DEBUG, "unhandled X11 property %" PRIu32 " (%s) for window %" PRIu32, From 5175b6e94e9967314d9ec5269f686a38ece0a84b Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 5 Mar 2025 22:34:41 +0100 Subject: [PATCH 297/519] xwayland/xwm: handle deleted properties X11 clients might delete window properties. In that case, reset our state to the initial value. --- xwayland/xwm.c | 101 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 66 insertions(+), 35 deletions(-) diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 4b085d040..5318cb1be 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -644,8 +644,8 @@ static void xwayland_surface_destroy(struct wlr_xwayland_surface *xsurface) { static void read_surface_class(struct wlr_xwm *xwm, struct wlr_xwayland_surface *surface, xcb_get_property_reply_t *reply) { - if (reply->type != XCB_ATOM_STRING && - reply->type != xwm->atoms[UTF8_STRING]) { + if (reply->type != XCB_ATOM_STRING && reply->type != xwm->atoms[UTF8_STRING] && + reply->type != XCB_ATOM_NONE) { return; } @@ -674,8 +674,8 @@ static void read_surface_class(struct wlr_xwm *xwm, static void read_surface_startup_id(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, xcb_get_property_reply_t *reply) { - if (reply->type != XCB_ATOM_STRING && - reply->type != xwm->atoms[UTF8_STRING]) { + if (reply->type != XCB_ATOM_STRING && reply->type != xwm->atoms[UTF8_STRING] && + reply->type != XCB_ATOM_NONE) { return; } @@ -713,8 +713,8 @@ static void read_surface_opacity(struct wlr_xwm *xwm, static void read_surface_role(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, xcb_get_property_reply_t *reply) { - if (reply->type != XCB_ATOM_STRING && - reply->type != xwm->atoms[UTF8_STRING]) { + if (reply->type != XCB_ATOM_STRING && reply->type != xwm->atoms[UTF8_STRING] && + reply->type != XCB_ATOM_NONE) { return; } @@ -734,13 +734,13 @@ static void read_surface_role(struct wlr_xwm *xwm, static void read_surface_title(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, xcb_get_property_reply_t *reply) { - if (reply->type != XCB_ATOM_STRING && - reply->type != xwm->atoms[UTF8_STRING]) { + if (reply->type != XCB_ATOM_STRING && reply->type != xwm->atoms[UTF8_STRING] && + reply->type != XCB_ATOM_NONE) { return; } bool is_utf8 = reply->type == xwm->atoms[UTF8_STRING]; - if (!is_utf8 && xsurface->has_utf8_title) { + if (!is_utf8 && xsurface->has_utf8_title && reply->type != XCB_ATOM_NONE) { return; } @@ -774,13 +774,13 @@ static bool has_parent(struct wlr_xwayland_surface *parent, static void read_surface_parent(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, xcb_get_property_reply_t *reply) { - struct wlr_xwayland_surface *found_parent = NULL; - if (reply->type != XCB_ATOM_WINDOW) { + if (reply->type != XCB_ATOM_WINDOW && reply->type != XCB_ATOM_NONE) { return; } + struct wlr_xwayland_surface *found_parent = NULL; xcb_window_t *xid = xcb_get_property_value(reply); - if (xid != NULL) { + if (reply->type != XCB_ATOM_NONE && xid != NULL) { found_parent = lookup_surface(xwm, *xid); if (!has_parent(found_parent, xsurface)) { xsurface->parent = found_parent; @@ -792,7 +792,6 @@ static void read_surface_parent(struct wlr_xwm *xwm, xsurface->parent = NULL; } - wl_list_remove(&xsurface->parent_link); if (xsurface->parent != NULL) { wl_list_insert(&xsurface->parent->children, &xsurface->parent_link); @@ -806,7 +805,7 @@ static void read_surface_parent(struct wlr_xwm *xwm, static void read_surface_window_type(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, xcb_get_property_reply_t *reply) { - if (reply->type != XCB_ATOM_ATOM) { + if (reply->type != XCB_ATOM_ATOM && reply->type != XCB_ATOM_NONE) { return; } @@ -815,11 +814,15 @@ static void read_surface_window_type(struct wlr_xwm *xwm, size_t atoms_size = sizeof(xcb_atom_t) * atoms_len; free(xsurface->window_type); - xsurface->window_type = malloc(atoms_size); - if (xsurface->window_type == NULL) { - return; + if (atoms_len > 0) { + xsurface->window_type = malloc(atoms_size); + if (xsurface->window_type == NULL) { + return; + } + memcpy(xsurface->window_type, atoms, atoms_size); + } else { + xsurface->window_type = NULL; } - memcpy(xsurface->window_type, atoms, atoms_size); xsurface->window_type_len = atoms_len; wl_signal_emit_mutable(&xsurface->events.set_window_type, NULL); @@ -828,7 +831,7 @@ static void read_surface_window_type(struct wlr_xwm *xwm, static void read_surface_protocols(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, xcb_get_property_reply_t *reply) { - if (reply->type != XCB_ATOM_ATOM) { + if (reply->type != XCB_ATOM_ATOM && reply->type != XCB_ATOM_NONE) { return; } @@ -837,11 +840,15 @@ static void read_surface_protocols(struct wlr_xwm *xwm, size_t atoms_size = sizeof(xcb_atom_t) * atoms_len; free(xsurface->protocols); - xsurface->protocols = malloc(atoms_size); - if (xsurface->protocols == NULL) { - return; + if (atoms_len > 0) { + xsurface->protocols = malloc(atoms_size); + if (xsurface->protocols == NULL) { + return; + } + memcpy(xsurface->protocols, atoms, atoms_size); + } else { + xsurface->protocols = NULL; } - memcpy(xsurface->protocols, atoms, atoms_size); xsurface->protocols_len = atoms_len; } @@ -850,21 +857,26 @@ static void read_surface_hints(struct wlr_xwm *xwm, xcb_get_property_reply_t *reply) { // According to the docs, reply->type == xwm->atoms[WM_HINTS] // In practice, reply->type == XCB_ATOM_ATOM - if (reply->value_len == 0) { + if (reply->type != xwm->atoms[WM_HINTS] && reply->type != XCB_ATOM_ATOM && + reply->type != XCB_ATOM_NONE) { return; } free(xsurface->hints); - xsurface->hints = calloc(1, sizeof(*xsurface->hints)); - if (xsurface->hints == NULL) { - return; - } - xcb_icccm_get_wm_hints_from_reply(xsurface->hints, reply); + if (reply->value_len > 0) { + xsurface->hints = calloc(1, sizeof(*xsurface->hints)); + if (xsurface->hints == NULL) { + return; + } + xcb_icccm_get_wm_hints_from_reply(xsurface->hints, reply); - if (!(xsurface->hints->flags & XCB_ICCCM_WM_HINT_INPUT)) { - // The client didn't specify whether it wants input. - // Assume it does. - xsurface->hints->input = true; + if (!(xsurface->hints->flags & XCB_ICCCM_WM_HINT_INPUT)) { + // The client didn't specify whether it wants input. + // Assume it does. + xsurface->hints->input = true; + } + } else { + xsurface->hints = NULL; } wl_signal_emit_mutable(&xsurface->events.set_hints, NULL); @@ -873,11 +885,17 @@ static void read_surface_hints(struct wlr_xwm *xwm, static void read_surface_normal_hints(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, xcb_get_property_reply_t *reply) { - if (reply->type != xwm->atoms[WM_SIZE_HINTS] || reply->value_len == 0) { + if (reply->type != xwm->atoms[WM_SIZE_HINTS] && reply->type != XCB_ATOM_NONE) { return; } free(xsurface->size_hints); + xsurface->size_hints = NULL; + + if (reply->value_len == 0) { + return; + } + xsurface->size_hints = calloc(1, sizeof(*xsurface->size_hints)); if (xsurface->size_hints == NULL) { return; @@ -919,6 +937,12 @@ static void read_surface_normal_hints(struct wlr_xwm *xwm, static void read_surface_motif_hints(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, xcb_get_property_reply_t *reply) { + if (reply->value_len == 0) { + xsurface->decorations = 0; + wl_signal_emit_mutable(&xsurface->events.set_decorations, NULL); + return; + } + if (reply->value_len < 5) { return; } @@ -944,13 +968,20 @@ static void read_surface_motif_hints(struct wlr_xwm *xwm, static void read_surface_strut_partial(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, xcb_get_property_reply_t *reply) { + free(xsurface->strut_partial); + xsurface->strut_partial = NULL; + + if (reply->type == XCB_ATOM_NONE) { + wl_signal_emit_mutable(&xsurface->events.set_strut_partial, NULL); + return; + } + if (reply->type != XCB_ATOM_CARDINAL || reply->format != 32 || xcb_get_property_value_length(reply) != sizeof(xcb_ewmh_wm_strut_partial_t)) { return; } - free(xsurface->strut_partial); xsurface->strut_partial = calloc(1, sizeof(*xsurface->strut_partial)); if (xsurface->strut_partial == NULL) { return; From 08c74f36a9ac6b59a0f82316cf6829b1ac4c4748 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 5 Mar 2025 22:40:38 +0100 Subject: [PATCH 298/519] xwayland/xwm: log when property type is invalid --- xwayland/xwm.c | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 5318cb1be..8e3ea3b4e 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -646,6 +646,7 @@ static void read_surface_class(struct wlr_xwm *xwm, struct wlr_xwayland_surface *surface, xcb_get_property_reply_t *reply) { if (reply->type != XCB_ATOM_STRING && reply->type != xwm->atoms[UTF8_STRING] && reply->type != XCB_ATOM_NONE) { + wlr_log(WLR_DEBUG, "Invalid WM_CLASS property type"); return; } @@ -676,6 +677,7 @@ static void read_surface_startup_id(struct wlr_xwm *xwm, xcb_get_property_reply_t *reply) { if (reply->type != XCB_ATOM_STRING && reply->type != xwm->atoms[UTF8_STRING] && reply->type != XCB_ATOM_NONE) { + wlr_log(WLR_DEBUG, "Invalid NET_STARTUP_ID property type"); return; } @@ -697,17 +699,21 @@ static void read_surface_startup_id(struct wlr_xwm *xwm, static void read_surface_opacity(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, xcb_get_property_reply_t *reply) { - if (reply->type == XCB_ATOM_CARDINAL && reply->format == 32 && - xcb_get_property_value_length(reply) == - sizeof(uint32_t)) { - uint32_t *val = xcb_get_property_value(reply); - - xsurface->opacity = (double)*val / UINT32_MAX; - wl_signal_emit_mutable(&xsurface->events.set_opacity, NULL); - } else if (reply->type == XCB_ATOM_NONE) { + if (reply->type == XCB_ATOM_NONE) { xsurface->opacity = 1.0; wl_signal_emit_mutable(&xsurface->events.set_opacity, NULL); + return; } + + if (reply->type != XCB_ATOM_CARDINAL || reply->format != 32 || + xcb_get_property_value_length(reply) != sizeof(uint32_t)) { + wlr_log(WLR_DEBUG, "Invalid NET_WINDOW_OPACITY property type"); + return; + } + + uint32_t *val = xcb_get_property_value(reply); + xsurface->opacity = (double)*val / UINT32_MAX; + wl_signal_emit_mutable(&xsurface->events.set_opacity, NULL); } static void read_surface_role(struct wlr_xwm *xwm, @@ -715,6 +721,7 @@ static void read_surface_role(struct wlr_xwm *xwm, xcb_get_property_reply_t *reply) { if (reply->type != XCB_ATOM_STRING && reply->type != xwm->atoms[UTF8_STRING] && reply->type != XCB_ATOM_NONE) { + wlr_log(WLR_DEBUG, "Invalid WM_WINDOW_ROLE property type"); return; } @@ -736,6 +743,7 @@ static void read_surface_title(struct wlr_xwm *xwm, xcb_get_property_reply_t *reply) { if (reply->type != XCB_ATOM_STRING && reply->type != xwm->atoms[UTF8_STRING] && reply->type != XCB_ATOM_NONE) { + wlr_log(WLR_DEBUG, "Invalid WM_NAME/NET_WM_NAME property type"); return; } @@ -775,6 +783,7 @@ static void read_surface_parent(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, xcb_get_property_reply_t *reply) { if (reply->type != XCB_ATOM_WINDOW && reply->type != XCB_ATOM_NONE) { + wlr_log(WLR_DEBUG, "Invalid WM_TRANSIENT_FOR property type"); return; } @@ -806,6 +815,7 @@ static void read_surface_window_type(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, xcb_get_property_reply_t *reply) { if (reply->type != XCB_ATOM_ATOM && reply->type != XCB_ATOM_NONE) { + wlr_log(WLR_DEBUG, "Invalid NET_WM_WINDOW_TYPE property type"); return; } @@ -832,6 +842,7 @@ static void read_surface_protocols(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, xcb_get_property_reply_t *reply) { if (reply->type != XCB_ATOM_ATOM && reply->type != XCB_ATOM_NONE) { + wlr_log(WLR_DEBUG, "Invalid WM_PROTOCOLS property type"); return; } @@ -859,6 +870,7 @@ static void read_surface_hints(struct wlr_xwm *xwm, // In practice, reply->type == XCB_ATOM_ATOM if (reply->type != xwm->atoms[WM_HINTS] && reply->type != XCB_ATOM_ATOM && reply->type != XCB_ATOM_NONE) { + wlr_log(WLR_DEBUG, "Invalid WM_HINTS property type"); return; } @@ -886,6 +898,7 @@ static void read_surface_normal_hints(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, xcb_get_property_reply_t *reply) { if (reply->type != xwm->atoms[WM_SIZE_HINTS] && reply->type != XCB_ATOM_NONE) { + wlr_log(WLR_DEBUG, "Invalid WM_NORMAL_HINTS property type"); return; } @@ -944,6 +957,7 @@ static void read_surface_motif_hints(struct wlr_xwm *xwm, } if (reply->value_len < 5) { + wlr_log(WLR_DEBUG, "Invalid MOTIF_WM_HINTS property type"); return; } @@ -979,6 +993,7 @@ static void read_surface_strut_partial(struct wlr_xwm *xwm, if (reply->type != XCB_ATOM_CARDINAL || reply->format != 32 || xcb_get_property_value_length(reply) != sizeof(xcb_ewmh_wm_strut_partial_t)) { + wlr_log(WLR_DEBUG, "Invalid NET_WM_STRUT_PARTIAL property type"); return; } From ca1f9f86e67da124a377091bee1862e11617860d Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 6 Mar 2025 15:58:42 +0100 Subject: [PATCH 299/519] backend/drm: handle custom modes in connect_drm_connector() On startup, some connectors might be already lit up with a custom mode. We weren't correctly populating the current output state in this case. --- backend/drm/drm.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 6ae9dcbf3..38ed6c26d 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -1614,6 +1614,7 @@ static bool connect_drm_connector(struct wlr_drm_connector *wlr_conn, wlr_log(WLR_INFO, "Detected modes:"); + bool found_current_mode = false; for (int i = 0; i < drm_conn->count_modes; ++i) { if (drm_conn->modes[i].flags & DRM_MODE_FLAG_INTERLACE) { continue; @@ -1632,14 +1633,7 @@ static bool connect_drm_connector(struct wlr_drm_connector *wlr_conn, if (current_modeinfo != NULL && memcmp(&mode->drm_mode, current_modeinfo, sizeof(*current_modeinfo)) == 0) { wlr_output_state_set_mode(&state, &mode->wlr_mode); - - uint64_t mode_id = 0; - get_drm_prop(drm->fd, wlr_conn->crtc->id, - wlr_conn->crtc->props.mode_id, &mode_id); - - wlr_conn->crtc->own_mode_id = false; - wlr_conn->crtc->mode_id = mode_id; - wlr_conn->refresh = calculate_refresh_rate(current_modeinfo); + found_current_mode = true; } wlr_log(WLR_INFO, " %"PRId32"x%"PRId32" @ %.3f Hz %s", @@ -1650,6 +1644,23 @@ static bool connect_drm_connector(struct wlr_drm_connector *wlr_conn, wl_list_insert(modes.prev, &mode->wlr_mode.link); } + if (current_modeinfo != NULL) { + int32_t refresh = calculate_refresh_rate(current_modeinfo); + + if (!found_current_mode) { + wlr_output_state_set_custom_mode(&state, + current_modeinfo->hdisplay, current_modeinfo->vdisplay, refresh); + } + + uint64_t mode_id = 0; + get_drm_prop(drm->fd, wlr_conn->crtc->id, + wlr_conn->crtc->props.mode_id, &mode_id); + + wlr_conn->crtc->own_mode_id = false; + wlr_conn->crtc->mode_id = mode_id; + wlr_conn->refresh = refresh; + } + free(current_modeinfo); wlr_output_init(output, &drm->backend, &output_impl, drm->session->event_loop, &state); From 94cb8e2bc78f9e03e2811929c850810d0f2427d7 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 6 Mar 2025 16:04:49 +0100 Subject: [PATCH 300/519] backend/drm: fix enabling an output with a custom mode set Since 5567aefb1c56 ("backend/drm: Don't add pollute fixed modes list with custom modes"), when a custom mode is set on an output, current_mode will be NULL. Instead of checking current_mode, check width/height. Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3946 --- backend/drm/drm.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 38ed6c26d..d07d98afc 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -828,13 +828,12 @@ static bool drm_connector_prepare(struct wlr_drm_connector_state *conn_state, bo return false; } - if ((state->committed & WLR_OUTPUT_STATE_ENABLED) && state->enabled) { - if (output->current_mode == NULL && - !(state->committed & WLR_OUTPUT_STATE_MODE)) { - wlr_drm_conn_log(conn, WLR_DEBUG, - "Can't enable an output without a mode"); - return false; - } + if ((state->committed & WLR_OUTPUT_STATE_ENABLED) && state->enabled && + output->width == 0 && output->height == 0 && + !(state->committed & WLR_OUTPUT_STATE_MODE)) { + wlr_drm_conn_log(conn, WLR_DEBUG, + "Can't enable an output without a mode"); + return false; } if ((state->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) && From 31f9d6bb97b61bb9cc0e185293a6770b90a14b7b Mon Sep 17 00:00:00 2001 From: liupeng Date: Wed, 5 Mar 2025 14:02:58 +0800 Subject: [PATCH 301/519] screencopy-v1: drop output_enable listener --- include/wlr/types/wlr_screencopy_v1.h | 1 - types/wlr_screencopy_v1.c | 19 ++++--------------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/include/wlr/types/wlr_screencopy_v1.h b/include/wlr/types/wlr_screencopy_v1.h index acd0ce185..3db958ad1 100644 --- a/include/wlr/types/wlr_screencopy_v1.h +++ b/include/wlr/types/wlr_screencopy_v1.h @@ -58,7 +58,6 @@ struct wlr_screencopy_frame_v1 { struct { struct wl_listener output_commit; struct wl_listener output_destroy; - struct wl_listener output_enable; } WLR_PRIVATE; }; diff --git a/types/wlr_screencopy_v1.c b/types/wlr_screencopy_v1.c index d039659d4..c49ed6bdf 100644 --- a/types/wlr_screencopy_v1.c +++ b/types/wlr_screencopy_v1.c @@ -146,7 +146,6 @@ static void frame_destroy(struct wlr_screencopy_frame_v1 *frame) { wl_list_remove(&frame->link); wl_list_remove(&frame->output_commit.link); wl_list_remove(&frame->output_destroy.link); - wl_list_remove(&frame->output_enable.link); // Make the frame resource inert wl_resource_set_user_data(frame->resource, NULL); wlr_buffer_unlock(frame->buffer); @@ -288,6 +287,10 @@ static void frame_handle_output_commit(struct wl_listener *listener, struct wlr_output_event_commit *event = data; struct wlr_output *output = frame->output; + if (event->state->committed & WLR_OUTPUT_STATE_ENABLED && !output->enabled) { + goto err; + } + if (!(event->state->committed & WLR_OUTPUT_STATE_BUFFER)) { return; } @@ -340,16 +343,6 @@ err: frame_destroy(frame); } -static void frame_handle_output_enable(struct wl_listener *listener, - void *data) { - struct wlr_screencopy_frame_v1 *frame = - wl_container_of(listener, frame, output_enable); - if (!frame->output->enabled) { - zwlr_screencopy_frame_v1_send_failed(frame->resource); - frame_destroy(frame); - } -} - static void frame_handle_output_destroy(struct wl_listener *listener, void *data) { struct wlr_screencopy_frame_v1 *frame = @@ -441,9 +434,6 @@ static void frame_handle_copy(struct wl_client *wl_client, wl_signal_add(&output->events.commit, &frame->output_commit); frame->output_commit.notify = frame_handle_output_commit; - wl_signal_add(&output->events.destroy, &frame->output_enable); - frame->output_enable.notify = frame_handle_output_enable; - // Request a frame because we can't assume that the current front buffer is still usable. It may // have been released already, and we shouldn't lock it here because compositors want to render // into the least damaged buffer. @@ -528,7 +518,6 @@ static void capture_output(struct wl_client *wl_client, wl_list_insert(&client->manager->frames, &frame->link); wl_list_init(&frame->output_commit.link); - wl_list_init(&frame->output_enable.link); wl_signal_add(&output->events.destroy, &frame->output_destroy); frame->output_destroy.notify = frame_handle_output_destroy; From 50edd3a42dc5e6a7e225642ad192e5ed20bc7103 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 9 Mar 2025 00:52:26 +0100 Subject: [PATCH 302/519] Document config.h --- include/wlr/config.h.in | 56 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/include/wlr/config.h.in b/include/wlr/config.h.in index e687bfeec..ef186343e 100644 --- a/include/wlr/config.h.in +++ b/include/wlr/config.h.in @@ -1,20 +1,76 @@ #ifndef WLR_CONFIG_H #define WLR_CONFIG_H +/** + * Whether the DRM backend is compile-time enabled. Equivalent to the + * pkg-config "have_drm_backend" variable. + * + * Required for . + */ #mesondefine WLR_HAS_DRM_BACKEND +/** + * Whether the libinput backend is compile-time enabled. Equivalent to the + * pkg-config "have_libinput_backend" vartiable. + * + * Required for . + */ #mesondefine WLR_HAS_LIBINPUT_BACKEND +/** + * Whether the X11 backend is compile-time enabled. Equivalent to the + * pkg-config "have_x11_backend" variable. + * + * Required for . + */ #mesondefine WLR_HAS_X11_BACKEND +/** + * Whether the GLES2 renderer is compile-time enabled. Equivalent to the + * pkg-config "have_gles2_renderer" variable. + * + * Required for . + */ #mesondefine WLR_HAS_GLES2_RENDERER +/** + * Whether the Vulkan renderer is compile-time enabled. Equivalent to the + * pkg-config "have_vulkan_renderer" variable. + * + * Required for . + */ #mesondefine WLR_HAS_VULKAN_RENDERER +/** + * Whether the GBM allocator is compile-time enabled. Equivalent to the + * pkg-config "have_gbm_allocator" variable. + */ #mesondefine WLR_HAS_GBM_ALLOCATOR +/** + * Whether the udmabuf allocator is compile-time enabled. Equivalent to the + * pkg-config "have_udmabuf_allocator" variable. + */ #mesondefine WLR_HAS_UDMABUF_ALLOCATOR +/** + * Whether Xwayland support is compile-time enabled. Equivalent to the + * pkg-config "have_xwayland" variable. + * + * Required for . + */ #mesondefine WLR_HAS_XWAYLAND +/** + * Whether session support is compile-time enabled. Equivalent to the + * pkg-config "have_session" variable. + * + * Required for . + */ #mesondefine WLR_HAS_SESSION +/** + * Whether traditional color management support is compile-time enabled. + * Equivalent to the pkg-config "have_color_management" variable. + * + * Required for ICC profile support in . + */ #mesondefine WLR_HAS_COLOR_MANAGEMENT #endif From 954dba3968c2eea3bf2c21defe01bf540fd102f9 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 13 Mar 2025 01:32:32 +0100 Subject: [PATCH 303/519] xwayland: Reset signal mask and handlers before exec Certain signal-related properties, such as the signal mask and handlers that are set to ignore, are not reset by exec and therefore affect the new process image. In case of Xwayland, a compositor setting SIGCHLD to SIG_IGN causes keyboard compilation to fail as it expects waitpid to work by default. Reset the signal mask and the two signals that sway is known to set. --- xwayland/server.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/xwayland/server.c b/xwayland/server.c index 0e8ad44fe..3abeb8bb9 100644 --- a/xwayland/server.c +++ b/xwayland/server.c @@ -36,6 +36,13 @@ noreturn static void exec_xwayland(struct wlr_xwayland_server *server, _exit(EXIT_FAILURE); } + // The compositor may have messed with signal handling, try to clean it up + sigset_t set; + sigemptyset(&set); + sigprocmask(SIG_SETMASK, &set, NULL); + signal(SIGPIPE, SIG_DFL); + signal(SIGCHLD, SIG_DFL); + char *argv[64] = {0}; size_t i = 0; From ba7ac3efe52743fce261543eb34cf5d5dfed1661 Mon Sep 17 00:00:00 2001 From: Chris Ever Date: Fri, 14 Mar 2025 15:23:07 +0000 Subject: [PATCH 304/519] fix comment typo in wlr_text_input_v3.h fixes #3951 --- include/wlr/types/wlr_text_input_v3.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/wlr/types/wlr_text_input_v3.h b/include/wlr/types/wlr_text_input_v3.h index 77f7a672e..4bc4dbbd9 100644 --- a/include/wlr/types/wlr_text_input_v3.h +++ b/include/wlr/types/wlr_text_input_v3.h @@ -75,7 +75,7 @@ struct wlr_text_input_manager_v3 { struct { struct wl_signal text_input; // struct wlr_text_input_v3 - struct wl_signal destroy; // struct wlr_input_method_manager_v3 + struct wl_signal destroy; // struct wlr_text_input_manager_v3 } events; struct { From 128cd07e9156a80dcd79178c3e669ddae2c71430 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 13 Mar 2025 15:49:24 +0100 Subject: [PATCH 305/519] scene/surface: use source buffer to signal release timeline point We were signaling the release timeline point when the wlr_client_buffer was released. However, the wlr_client_buffer isn't necessarily released at the same time as the underlying source wlr_buffer. For instance, with wl_shm the source buffer is released before the wlr_client_buffer, and with linux-dmabuf-v1 the source buffer is released after the wlr_client_buffer. However, we want to signal the release timeline point exactly at the same time we send the wl_buffer.release event to the client. Use surface->buffer->source instead of &surface->buffer->base to fix this. linux-drm-syncobj-v1 can only be used with DMA-BUFs, and wlr_client_buffer.texture will keep the source locked, so surface->buffer->source is guaranteed to be non-NULL and unreleased. Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3940 Fixes: 9e71c88467fc ("scene: unwrap wlr_client_buffer for direct scan-out") --- types/scene/surface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/scene/surface.c b/types/scene/surface.c index e6d0fb8e2..1f954373d 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -187,7 +187,7 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { if (syncobj_surface_state != NULL && (surface->current.committed & WLR_SURFACE_STATE_BUFFER)) { wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer(syncobj_surface_state, - &surface->buffer->base); + surface->buffer->source); } } else { wlr_scene_buffer_set_buffer(scene_buffer, NULL); From 221bc5f6aa8f86ef9254433e0c2fab32fd1218fa Mon Sep 17 00:00:00 2001 From: Thomas Frans Date: Thu, 20 Mar 2025 16:47:04 +0100 Subject: [PATCH 306/519] git: fix incorrect subprojects ignore in `.gitignore` Ignoring the entire `/subprojects/` directory prevents the next rule from including just the Meson wrap files. Instead, ignore all the files in the directory which allows the intended behavior. --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 623384e78..02cf3c093 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -/subprojects/ +/subprojects/* !/subprojects/*.wrap From db2c907f934fcbff4859e6ea74ca12b9f30441ab Mon Sep 17 00:00:00 2001 From: xurui Date: Wed, 8 Jan 2025 15:32:26 +0800 Subject: [PATCH 307/519] xwayland/selection/dnd: always send finished event Signed-off-by: xurui --- xwayland/selection/dnd.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/xwayland/selection/dnd.c b/xwayland/selection/dnd.c index cdbcd1e9c..70792b5f4 100644 --- a/xwayland/selection/dnd.c +++ b/xwayland/selection/dnd.c @@ -222,10 +222,7 @@ int xwm_handle_selection_client_message(struct wlr_xwm *xwm, enum wl_data_device_manager_dnd_action action = data_device_manager_dnd_action_from_atom(xwm, action_atom); - if (performed) { - wlr_data_source_dnd_finish(source); - } - + wlr_data_source_dnd_finish(source); wlr_log(WLR_DEBUG, "DND_FINISH window=%" PRIu32 " performed=%d action=%d", target_window, performed, action); return 1; From 99da6ccc87c2439b19c9298df6b72b29bbab89bb Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 17 Jan 2025 13:58:33 +0100 Subject: [PATCH 308/519] backend/drm: pass DRM_MODE_ATOMIC_NONBLOCK for test commits The kernel performs some additional checks when DRM_MODE_ATOMIC_NONBLOCK is supplied: it requires that none of the planes are still busy with a previous page-flip. Pass the flag during test-only commits so that we don't end up performing a commit which will fail. --- backend/drm/atomic.c | 2 +- backend/drm/libliftoff.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index 22ce23821..91cafea60 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -508,7 +508,7 @@ static bool atomic_device_commit(struct wlr_drm_backend *drm, if (state->modeset) { flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; } - if (!test_only && state->nonblock) { + if (state->nonblock) { flags |= DRM_MODE_ATOMIC_NONBLOCK; } diff --git a/backend/drm/libliftoff.c b/backend/drm/libliftoff.c index 67b1ca527..a5bace015 100644 --- a/backend/drm/libliftoff.c +++ b/backend/drm/libliftoff.c @@ -426,7 +426,7 @@ static bool commit(struct wlr_drm_backend *drm, if (state->modeset) { flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; } - if (!test_only && state->nonblock) { + if (state->nonblock) { flags |= DRM_MODE_ATOMIC_NONBLOCK; } From 9dbf5b9f6b3ed486f88605ae0c93e191e528e42d Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Sun, 30 Mar 2025 01:15:09 +0300 Subject: [PATCH 309/519] fullscreen-shell: remove The protocol implementation has been marked as deprecated in the previous release. Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3846 --- examples/fullscreen-shell.c | 257 -------------------- examples/meson.build | 4 - include/wlr/types/wlr_fullscreen_shell_v1.h | 42 ---- protocol/meson.build | 1 - types/meson.build | 1 - types/wlr_fullscreen_shell_v1.c | 142 ----------- 6 files changed, 447 deletions(-) delete mode 100644 examples/fullscreen-shell.c delete mode 100644 include/wlr/types/wlr_fullscreen_shell_v1.h delete mode 100644 types/wlr_fullscreen_shell_v1.c diff --git a/examples/fullscreen-shell.c b/examples/fullscreen-shell.c deleted file mode 100644 index 956d851af..000000000 --- a/examples/fullscreen-shell.c +++ /dev/null @@ -1,257 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * A minimal fullscreen-shell server. It only supports rendering. - */ - -struct fullscreen_server { - struct wl_display *wl_display; - struct wlr_backend *backend; - struct wlr_renderer *renderer; - struct wlr_allocator *allocator; - - struct wlr_fullscreen_shell_v1 *fullscreen_shell; - struct wl_listener present_surface; - - struct wlr_output_layout *output_layout; - struct wl_list outputs; - struct wl_listener new_output; -}; - -struct fullscreen_output { - struct wl_list link; - struct fullscreen_server *server; - struct wlr_output *wlr_output; - struct wlr_surface *surface; - struct wl_listener surface_destroy; - - struct wl_listener frame; -}; - -struct render_data { - struct wlr_output *output; - struct wlr_render_pass *render_pass; - struct timespec *when; -}; - -static void render_surface(struct wlr_surface *surface, - int sx, int sy, void *data) { - struct render_data *rdata = data; - struct wlr_output *output = rdata->output; - - struct wlr_texture *texture = wlr_surface_get_texture(surface); - if (texture == NULL) { - return; - } - - struct wlr_box box = { - .x = sx * output->scale, - .y = sy * output->scale, - .width = surface->current.width * output->scale, - .height = surface->current.height * output->scale, - }; - - enum wl_output_transform transform = wlr_output_transform_invert(surface->current.transform); - transform = wlr_output_transform_compose(transform, output->transform); - - wlr_render_pass_add_texture(rdata->render_pass, &(struct wlr_render_texture_options){ - .texture = texture, - .dst_box = box, - .transform = transform, - }); - - wlr_surface_send_frame_done(surface, rdata->when); -} - -static void output_handle_frame(struct wl_listener *listener, void *data) { - struct fullscreen_output *output = - wl_container_of(listener, output, frame); - - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - int width, height; - wlr_output_effective_resolution(output->wlr_output, &width, &height); - - struct wlr_output_state state; - wlr_output_state_init(&state); - struct wlr_render_pass *pass = wlr_output_begin_render_pass(output->wlr_output, &state, NULL); - if (pass == NULL) { - return; - } - - wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){ - .color = { 0.3, 0.3, 0.3, 1.0 }, - .box = { .width = width, .height = height }, - }); - - if (output->surface != NULL) { - struct render_data rdata = { - .output = output->wlr_output, - .render_pass = pass, - .when = &now, - }; - wlr_surface_for_each_surface(output->surface, render_surface, &rdata); - } - - wlr_render_pass_submit(pass); - wlr_output_commit_state(output->wlr_output, &state); - wlr_output_state_finish(&state); -} - -static void output_set_surface(struct fullscreen_output *output, - struct wlr_surface *surface); - -static void output_handle_surface_destroy(struct wl_listener *listener, - void *data) { - struct fullscreen_output *output = - wl_container_of(listener, output, surface_destroy); - output_set_surface(output, NULL); -} - -static void output_set_surface(struct fullscreen_output *output, - struct wlr_surface *surface) { - if (output->surface == surface) { - return; - } - - if (output->surface != NULL) { - wl_list_remove(&output->surface_destroy.link); - output->surface = NULL; - } - - if (surface != NULL) { - output->surface_destroy.notify = output_handle_surface_destroy; - wl_signal_add(&surface->events.destroy, &output->surface_destroy); - output->surface = surface; - } - - wlr_log(WLR_DEBUG, "Presenting surface %p on output %s", - surface, output->wlr_output->name); -} - -static void server_handle_new_output(struct wl_listener *listener, void *data) { - struct fullscreen_server *server = - wl_container_of(listener, server, new_output); - struct wlr_output *wlr_output = data; - - wlr_output_init_render(wlr_output, server->allocator, server->renderer); - - struct fullscreen_output *output = calloc(1, sizeof(*output)); - output->wlr_output = wlr_output; - output->server = server; - output->frame.notify = output_handle_frame; - wl_signal_add(&wlr_output->events.frame, &output->frame); - wl_list_insert(&server->outputs, &output->link); - - wlr_output_layout_add_auto(server->output_layout, wlr_output); - wlr_output_create_global(wlr_output, server->wl_display); - - struct wlr_output_state state; - wlr_output_state_init(&state); - wlr_output_state_set_enabled(&state, true); - struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); - if (mode != NULL) { - wlr_output_state_set_mode(&state, mode); - } - wlr_output_commit_state(wlr_output, &state); - wlr_output_state_finish(&state); -} - -static void server_handle_present_surface(struct wl_listener *listener, - void *data) { - struct fullscreen_server *server = - wl_container_of(listener, server, present_surface); - struct wlr_fullscreen_shell_v1_present_surface_event *event = data; - - struct fullscreen_output *output; - wl_list_for_each(output, &server->outputs, link) { - if (event->output == NULL || event->output == output->wlr_output) { - output_set_surface(output, event->surface); - } - } -} - -int main(int argc, char *argv[]) { - wlr_log_init(WLR_DEBUG, NULL); - - char *startup_cmd = NULL; - - int c; - while ((c = getopt(argc, argv, "s:")) != -1) { - switch (c) { - case 's': - startup_cmd = optarg; - break; - default: - printf("usage: %s [-s startup-command]\n", argv[0]); - return EXIT_FAILURE; - } - } - if (optind < argc) { - printf("usage: %s [-s startup-command]\n", argv[0]); - return EXIT_FAILURE; - } - - struct fullscreen_server server = {0}; - server.wl_display = wl_display_create(); - server.backend = wlr_backend_autocreate(wl_display_get_event_loop(server.wl_display), NULL); - server.renderer = wlr_renderer_autocreate(server.backend); - wlr_renderer_init_wl_display(server.renderer, server.wl_display); - server.allocator = wlr_allocator_autocreate(server.backend, - server.renderer); - - wlr_compositor_create(server.wl_display, 5, server.renderer); - - server.output_layout = wlr_output_layout_create(server.wl_display); - - wl_list_init(&server.outputs); - server.new_output.notify = server_handle_new_output; - wl_signal_add(&server.backend->events.new_output, &server.new_output); - - server.fullscreen_shell = wlr_fullscreen_shell_v1_create(server.wl_display); - server.present_surface.notify = server_handle_present_surface; - wl_signal_add(&server.fullscreen_shell->events.present_surface, - &server.present_surface); - - const char *socket = wl_display_add_socket_auto(server.wl_display); - if (!socket) { - wl_display_destroy(server.wl_display); - return EXIT_FAILURE; - } - - if (!wlr_backend_start(server.backend)) { - wl_display_destroy(server.wl_display); - return EXIT_FAILURE; - } - - setenv("WAYLAND_DISPLAY", socket, true); - if (startup_cmd != NULL) { - if (fork() == 0) { - execl("/bin/sh", "/bin/sh", "-c", startup_cmd, (void *)NULL); - } - } - - wlr_log(WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s", - socket); - wl_display_run(server.wl_display); - - wl_display_destroy_clients(server.wl_display); - wl_display_destroy(server.wl_display); - return 0; -} diff --git a/examples/meson.build b/examples/meson.build index 3fe07df8e..28a83cccc 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -25,10 +25,6 @@ compositors = { 'output-layout': { 'src': ['output-layout.c', 'cat.c'], }, - 'fullscreen-shell': { - 'src': 'fullscreen-shell.c', - 'proto': ['fullscreen-shell-unstable-v1'], - }, 'scene-graph': { 'src': 'scene-graph.c', 'proto': ['xdg-shell'], diff --git a/include/wlr/types/wlr_fullscreen_shell_v1.h b/include/wlr/types/wlr_fullscreen_shell_v1.h deleted file mode 100644 index c4b1cbc7a..000000000 --- a/include/wlr/types/wlr_fullscreen_shell_v1.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This is a deprecated interface of wlroots. It will be removed in a future - * version. - */ - -#ifndef WLR_USE_UNSTABLE -#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" -#endif - -#ifndef WLR_TYPES_WLR_FULLSCREEN_SHELL_V1_H -#define WLR_TYPES_WLR_FULLSCREEN_SHELL_V1_H - -#include -#include "fullscreen-shell-unstable-v1-protocol.h" - -struct wlr_fullscreen_shell_v1 { - struct wl_global *global; - - struct { - struct wl_signal destroy; - // struct wlr_fullscreen_shell_v1_present_surface_event - struct wl_signal present_surface; - } events; - - void *data; - - struct { - struct wl_listener display_destroy; - } WLR_PRIVATE; -}; - -struct wlr_fullscreen_shell_v1_present_surface_event { - struct wl_client *client; - struct wlr_surface *surface; // can be NULL - enum zwp_fullscreen_shell_v1_present_method method; - struct wlr_output *output; // can be NULL -}; - -struct wlr_fullscreen_shell_v1 *wlr_fullscreen_shell_v1_create( - struct wl_display *display); - -#endif diff --git a/protocol/meson.build b/protocol/meson.build index a84f9f42a..f55539e93 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -45,7 +45,6 @@ protocols = { 'tearing-control-v1': wl_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml', # Unstable upstream protocols - 'fullscreen-shell-unstable-v1': wl_protocol_dir / 'unstable/fullscreen-shell/fullscreen-shell-unstable-v1.xml', 'idle-inhibit-unstable-v1': wl_protocol_dir / 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml', 'keyboard-shortcuts-inhibit-unstable-v1': wl_protocol_dir / 'unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml', 'pointer-constraints-unstable-v1': wl_protocol_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml', diff --git a/types/meson.build b/types/meson.build index 1d17fd647..5f44fd27e 100644 --- a/types/meson.build +++ b/types/meson.build @@ -49,7 +49,6 @@ wlr_files += files( 'wlr_ext_foreign_toplevel_list_v1.c', 'wlr_ext_data_control_v1.c', 'wlr_fractional_scale_v1.c', - 'wlr_fullscreen_shell_v1.c', 'wlr_gamma_control_v1.c', 'wlr_idle_inhibit_v1.c', 'wlr_idle_notify_v1.c', diff --git a/types/wlr_fullscreen_shell_v1.c b/types/wlr_fullscreen_shell_v1.c deleted file mode 100644 index 53d8c4e91..000000000 --- a/types/wlr_fullscreen_shell_v1.c +++ /dev/null @@ -1,142 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#define FULLSCREEN_SHELL_VERSION 1 - -static const struct zwp_fullscreen_shell_v1_interface shell_impl; - -static void shell_handle_release(struct wl_client *client, - struct wl_resource *resource) { - wl_resource_destroy(resource); -} - -static struct wlr_fullscreen_shell_v1 *shell_from_resource( - struct wl_resource *resource) { - assert(wl_resource_instance_of(resource, - &zwp_fullscreen_shell_v1_interface, &shell_impl)); - return wl_resource_get_user_data(resource); -} - -static void fullscreen_shell_surface_handle_commit(struct wlr_surface *surface) { - if (wlr_surface_has_buffer(surface)) { - wlr_surface_map(surface); - } -} - -static const struct wlr_surface_role fullscreen_shell_surface_role = { - .name = "zwp_fullscreen_shell_v1-surface", - .no_object = true, - .commit = fullscreen_shell_surface_handle_commit, -}; - -static void shell_handle_present_surface(struct wl_client *client, - struct wl_resource *shell_resource, - struct wl_resource *surface_resource, uint32_t method, - struct wl_resource *output_resource) { - struct wlr_fullscreen_shell_v1 *shell = shell_from_resource(shell_resource); - struct wlr_surface *surface = NULL; - if (surface_resource != NULL) { - surface = wlr_surface_from_resource(surface_resource); - } - struct wlr_output *output = NULL; - if (output_resource != NULL) { - output = wlr_output_from_resource(output_resource); - } - - if (!wlr_surface_set_role(surface, &fullscreen_shell_surface_role, - shell_resource, ZWP_FULLSCREEN_SHELL_V1_ERROR_ROLE)) { - return; - } - - struct wlr_fullscreen_shell_v1_present_surface_event event = { - .client = client, - .surface = surface, - .method = method, - .output = output, - }; - wl_signal_emit_mutable(&shell->events.present_surface, &event); -} - -static void shell_handle_present_surface_for_mode(struct wl_client *client, - struct wl_resource *shell_resource, - struct wl_resource *surface_resource, - struct wl_resource *output_resource, int32_t framerate, - uint32_t feedback_id) { - struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); - - if (!wlr_surface_set_role(surface, &fullscreen_shell_surface_role, - shell_resource, ZWP_FULLSCREEN_SHELL_V1_ERROR_ROLE)) { - return; - } - - uint32_t version = wl_resource_get_version(shell_resource); - struct wl_resource *feedback_resource = - wl_resource_create(client, NULL, version, feedback_id); - if (feedback_resource == NULL) { - wl_resource_post_no_memory(shell_resource); - return; - } - - // TODO: add support for mode switch - zwp_fullscreen_shell_mode_feedback_v1_send_mode_failed(feedback_resource); - wl_resource_destroy(feedback_resource); -} - -static const struct zwp_fullscreen_shell_v1_interface shell_impl = { - .release = shell_handle_release, - .present_surface = shell_handle_present_surface, - .present_surface_for_mode = shell_handle_present_surface_for_mode, -}; - -static void shell_bind(struct wl_client *client, void *data, uint32_t version, - uint32_t id) { - struct wlr_fullscreen_shell_v1 *shell = data; - - struct wl_resource *resource = wl_resource_create(client, - &zwp_fullscreen_shell_v1_interface, version, id); - if (resource == NULL) { - wl_client_post_no_memory(client); - return; - } - wl_resource_set_implementation(resource, &shell_impl, shell, NULL); -} - -static void handle_display_destroy(struct wl_listener *listener, void *data) { - struct wlr_fullscreen_shell_v1 *shell = - wl_container_of(listener, shell, display_destroy); - wl_signal_emit_mutable(&shell->events.destroy, shell); - - assert(wl_list_empty(&shell->events.destroy.listener_list)); - assert(wl_list_empty(&shell->events.present_surface.listener_list)); - - wl_list_remove(&shell->display_destroy.link); - wl_global_destroy(shell->global); - free(shell); -} - -struct wlr_fullscreen_shell_v1 *wlr_fullscreen_shell_v1_create( - struct wl_display *display) { - struct wlr_fullscreen_shell_v1 *shell = calloc(1, sizeof(*shell)); - if (shell == NULL) { - return NULL; - } - wl_signal_init(&shell->events.destroy); - wl_signal_init(&shell->events.present_surface); - - shell->global = wl_global_create(display, - &zwp_fullscreen_shell_v1_interface, FULLSCREEN_SHELL_VERSION, - shell, shell_bind); - if (shell->global == NULL) { - free(shell); - return NULL; - } - - shell->display_destroy.notify = handle_display_destroy; - wl_display_add_destroy_listener(display, &shell->display_destroy); - - return shell; -} From 420b60f203872d89e79be7f928733b35a5dee00d Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 27 Jan 2025 17:54:55 +0100 Subject: [PATCH 310/519] util/matrix: add matrix_invert() --- include/util/matrix.h | 7 +++++++ util/matrix.c | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/include/util/matrix.h b/include/util/matrix.h index 059a3bb1f..c45c99a06 100644 --- a/include/util/matrix.h +++ b/include/util/matrix.h @@ -39,4 +39,11 @@ void wlr_matrix_project_box(float mat[static 9], const struct wlr_box *box, void matrix_projection(float mat[static 9], int width, int height, enum wl_output_transform transform); +/** + * Compute the inverse of a matrix. + * + * The matrix needs to be inversible. + */ +void matrix_invert(float out[static 9], float m[static 9]); + #endif diff --git a/util/matrix.c b/util/matrix.c index 86b434c64..640787c95 100644 --- a/util/matrix.c +++ b/util/matrix.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -139,3 +140,26 @@ void wlr_matrix_project_box(float mat[static 9], const struct wlr_box *box, wlr_matrix_multiply(mat, projection, mat); } + +void matrix_invert(float out[static 9], float m[static 9]) { + float a = m[0], b = m[1], c = m[2], d = m[3], e = m[4], f = m[5], g = m[6], h = m[7], i = m[8]; + + // See: https://en.wikipedia.org/wiki/Determinant + float det = a*e*i + b*f*g + c*d*h - c*e*g - b*d*i - a*f*h; + assert(det != 0); + float inv_det = 1 / det; + + // See: https://en.wikipedia.org/wiki/Invertible_matrix#Inversion_of_3_%C3%97_3_matrices + float result[] = { + inv_det * (e*i - f*h), + inv_det * -(b*i - c*h), + inv_det * (b*f - c*e), + inv_det * -(d*i - f*g), + inv_det * (a*i - c*g), + inv_det * -(a*f - c*d), + inv_det * (d*h - e*g), + inv_det * -(a*h - b*g), + inv_det * (a*e - b*d), + }; + memcpy(out, result, sizeof(result)); +} From d8ad4809fcba81e62edf18beb0f744d595a19c30 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 27 Jan 2025 18:41:16 +0100 Subject: [PATCH 311/519] render/color: include public header from private one --- include/render/color.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/render/color.h b/include/render/color.h index 4ab587be1..000e4a44f 100644 --- a/include/render/color.h +++ b/include/render/color.h @@ -2,6 +2,7 @@ #define RENDER_COLOR_H #include +#include #include enum wlr_color_transform_type { From 156201fe71429335450cefa3dae7c5c704b2944b Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 27 Jan 2025 18:04:02 +0100 Subject: [PATCH 312/519] render/color: add wlr_color_primaries_to_xyz() --- include/render/color.h | 5 ++++ include/wlr/render/color.h | 14 +++++++++++ render/color.c | 49 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+) diff --git a/include/render/color.h b/include/render/color.h index 000e4a44f..932727001 100644 --- a/include/render/color.h +++ b/include/render/color.h @@ -45,4 +45,9 @@ struct wlr_color_transform_lut3d { struct wlr_color_transform_lut3d *wlr_color_transform_lut3d_from_base( struct wlr_color_transform *tr); +/** + * Compute the matrix to convert RGB color values to CIE 1931 XYZ. + */ +void wlr_color_primaries_to_xyz(const struct wlr_color_primaries *primaries, float matrix[static 9]); + #endif diff --git a/include/wlr/render/color.h b/include/wlr/render/color.h index 43d9154be..9a1688fe4 100644 --- a/include/wlr/render/color.h +++ b/include/wlr/render/color.h @@ -12,6 +12,20 @@ #include #include +/** + * CIE 1931 xy chromaticity coordinates. + */ +struct wlr_color_cie1931_xy { + float x, y; +}; + +/** + * Color primaries and white point describing a color volume. + */ +struct wlr_color_primaries { + struct wlr_color_cie1931_xy red, green, blue, white; +}; + /** * A color transformation formula, which maps a linear color space with * sRGB primaries to an output color space. diff --git a/render/color.c b/render/color.c index 9f8d76b06..b5463e4d8 100644 --- a/render/color.c +++ b/render/color.c @@ -1,7 +1,9 @@ #include #include +#include #include #include "render/color.h" +#include "util/matrix.h" struct wlr_color_transform *wlr_color_transform_init_srgb(void) { struct wlr_color_transform *tx = calloc(1, sizeof(struct wlr_color_transform)); @@ -50,3 +52,50 @@ struct wlr_color_transform_lut3d *wlr_color_transform_lut3d_from_base( struct wlr_color_transform_lut3d *lut3d = wl_container_of(tr, lut3d, base); return lut3d; } + +static void multiply_matrix_vector(float out[static 3], float m[static 9], float v[static 3]) { + float result[3] = { + m[0] * v[0] + m[1] * v[1] + m[2] * v[2], + m[3] * v[0] + m[4] * v[1] + m[5] * v[2], + m[6] * v[0] + m[7] * v[1] + m[8] * v[2], + }; + memcpy(out, result, sizeof(result)); +} + +static void xy_to_xyz(float out[static 3], struct wlr_color_cie1931_xy src) { + if (src.y == 0) { + out[0] = out[1] = out[2] = 0; + return; + } + + out[0] = src.x / src.y; + out[1] = 1; + out[2] = (1 - src.x - src.y) / src.y; +} + +void wlr_color_primaries_to_xyz(const struct wlr_color_primaries *primaries, float matrix[static 9]) { + // See: http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html + + float r[3], g[3], b[3], w[3]; + xy_to_xyz(r, primaries->red); + xy_to_xyz(g, primaries->green); + xy_to_xyz(b, primaries->blue); + xy_to_xyz(w, primaries->white); + + float xyz_matrix[9] = { + r[0], g[0], b[0], + r[1], g[1], b[1], + r[2], g[2], b[2], + }; + matrix_invert(xyz_matrix, xyz_matrix); + + float S[3]; + multiply_matrix_vector(S, xyz_matrix, w); + + float result[] = { + S[0] * r[0], S[1] * g[0], S[2] * b[0], + S[0] * r[1], S[1] * g[1], S[2] * b[1], + S[0] * r[2], S[1] * g[2], S[2] * b[2], + }; + memcpy(matrix, result, sizeof(result)); +} From 7d076d0bc9a5a1fe302a5ae0f71d76590ba5726f Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 27 Feb 2025 18:29:04 +0100 Subject: [PATCH 313/519] render/color: introduce wlr_color_named_primaries --- include/wlr/render/color.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/wlr/render/color.h b/include/wlr/render/color.h index 9a1688fe4..1502ee9e8 100644 --- a/include/wlr/render/color.h +++ b/include/wlr/render/color.h @@ -12,6 +12,14 @@ #include #include +/** + * Well-known color primaries. + */ +enum wlr_color_named_primaries { + WLR_COLOR_NAMED_PRIMARIES_SRGB = 1 << 0, + WLR_COLOR_NAMED_PRIMARIES_BT2020 = 1 << 1, +}; + /** * CIE 1931 xy chromaticity coordinates. */ From e11012a024225d8d14ce27355a83d3be454cd2b0 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 27 Feb 2025 18:29:22 +0100 Subject: [PATCH 314/519] render/color: introduce wlr_color_primaries_from_named() --- include/render/color.h | 6 ++++++ render/color.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/include/render/color.h b/include/render/color.h index 932727001..9da4f9a74 100644 --- a/include/render/color.h +++ b/include/render/color.h @@ -45,6 +45,12 @@ struct wlr_color_transform_lut3d { struct wlr_color_transform_lut3d *wlr_color_transform_lut3d_from_base( struct wlr_color_transform *tr); +/** + * Obtain primaries values from a well-known primaries name. + */ +void wlr_color_primaries_from_named(struct wlr_color_primaries *out, + enum wlr_color_named_primaries named); + /** * Compute the matrix to convert RGB color values to CIE 1931 XYZ. */ diff --git a/render/color.c b/render/color.c index b5463e4d8..ece6b38bd 100644 --- a/render/color.c +++ b/render/color.c @@ -5,6 +5,22 @@ #include "render/color.h" #include "util/matrix.h" +// See H.273 ColourPrimaries + +static const struct wlr_color_primaries COLOR_PRIMARIES_SRGB = { // code point 1 + .red = { 0.640, 0.330 }, + .green = { 0.300, 0.600 }, + .blue = { 0.150, 0.060 }, + .white = { 0.3127, 0.3290 }, +}; + +static const struct wlr_color_primaries COLOR_PRIMARIES_BT2020 = { // code point 9 + .red = { 0.708, 0.292 }, + .green = { 0.170, 0.797 }, + .blue = { 0.131, 0.046 }, + .white = { 0.3127, 0.3290 }, +}; + struct wlr_color_transform *wlr_color_transform_init_srgb(void) { struct wlr_color_transform *tx = calloc(1, sizeof(struct wlr_color_transform)); if (!tx) { @@ -53,6 +69,19 @@ struct wlr_color_transform_lut3d *wlr_color_transform_lut3d_from_base( return lut3d; } +void wlr_color_primaries_from_named(struct wlr_color_primaries *out, + enum wlr_color_named_primaries named) { + switch (named) { + case WLR_COLOR_NAMED_PRIMARIES_SRGB: + *out = COLOR_PRIMARIES_SRGB; + return; + case WLR_COLOR_NAMED_PRIMARIES_BT2020: + *out = COLOR_PRIMARIES_BT2020; + return; + } + abort(); +} + static void multiply_matrix_vector(float out[static 3], float m[static 9], float v[static 3]) { float result[3] = { m[0] * v[0] + m[1] * v[1] + m[2] * v[2], From 50537e2e6f54e33867ca9cae0915bf8ef1663110 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 28 Jan 2025 09:41:39 +0100 Subject: [PATCH 315/519] render/color: introduce enum wlr_color_transfer_function --- include/wlr/render/color.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/wlr/render/color.h b/include/wlr/render/color.h index 1502ee9e8..32d8e9540 100644 --- a/include/wlr/render/color.h +++ b/include/wlr/render/color.h @@ -20,6 +20,14 @@ enum wlr_color_named_primaries { WLR_COLOR_NAMED_PRIMARIES_BT2020 = 1 << 1, }; +/** + * Well-known color transfer functions. + */ +enum wlr_color_transfer_function { + WLR_COLOR_TRANSFER_FUNCTION_SRGB = 1 << 0, + WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ = 1 << 1, +}; + /** * CIE 1931 xy chromaticity coordinates. */ From 10b8880fc7344dae3c44ae87c536d3a0fbf7aabe Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 26 Feb 2025 08:58:15 +0100 Subject: [PATCH 316/519] render/color: add wlr_color_transfer_function_get_default_luminance() --- include/render/color.h | 6 ++++++ include/wlr/render/color.h | 7 +++++++ render/color.c | 20 ++++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/include/render/color.h b/include/render/color.h index 9da4f9a74..cddf1e219 100644 --- a/include/render/color.h +++ b/include/render/color.h @@ -56,4 +56,10 @@ void wlr_color_primaries_from_named(struct wlr_color_primaries *out, */ void wlr_color_primaries_to_xyz(const struct wlr_color_primaries *primaries, float matrix[static 9]); +/** + * Get default luminances for a transfer function. + */ +void wlr_color_transfer_function_get_default_luminance(enum wlr_color_transfer_function tf, + struct wlr_color_luminances *lum); + #endif diff --git a/include/wlr/render/color.h b/include/wlr/render/color.h index 32d8e9540..dc6f4097f 100644 --- a/include/wlr/render/color.h +++ b/include/wlr/render/color.h @@ -42,6 +42,13 @@ struct wlr_color_primaries { struct wlr_color_cie1931_xy red, green, blue, white; }; +/** + * Luminance range and reference white luminance level, in cd/m². + */ +struct wlr_color_luminances { + float min, max, reference; +}; + /** * A color transformation formula, which maps a linear color space with * sRGB primaries to an output color space. diff --git a/render/color.c b/render/color.c index ece6b38bd..c708938c7 100644 --- a/render/color.c +++ b/render/color.c @@ -128,3 +128,23 @@ void wlr_color_primaries_to_xyz(const struct wlr_color_primaries *primaries, flo }; memcpy(matrix, result, sizeof(result)); } + +void wlr_color_transfer_function_get_default_luminance(enum wlr_color_transfer_function tf, + struct wlr_color_luminances *lum) { + switch (tf) { + case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ: + *lum = (struct wlr_color_luminances){ + .min = 0.005, + .max = 10000, + .reference = 203, + }; + break; + default: + *lum = (struct wlr_color_luminances){ + .min = 0.2, + .max = 80, + .reference = 80, + }; + break; + } +} From 0ab3c1d06035d62c8a66e2ebb8e6ad33a57ee0f3 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 22 Jan 2025 08:52:43 +0100 Subject: [PATCH 317/519] color-management-v1: new protocol This implements the bare minimum to expose the protocol interfaces. References: https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/14 --- include/wlr/types/wlr_color_management_v1.h | 64 +++ protocol/meson.build | 3 +- types/meson.build | 1 + types/wlr_color_management_v1.c | 542 ++++++++++++++++++++ 4 files changed, 609 insertions(+), 1 deletion(-) create mode 100644 include/wlr/types/wlr_color_management_v1.h create mode 100644 types/wlr_color_management_v1.c diff --git a/include/wlr/types/wlr_color_management_v1.h b/include/wlr/types/wlr_color_management_v1.h new file mode 100644 index 000000000..4b5d18670 --- /dev/null +++ b/include/wlr/types/wlr_color_management_v1.h @@ -0,0 +1,64 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_TYPES_WLR_COLOR_MANAGEMENT_V1_H +#define WLR_TYPES_WLR_COLOR_MANAGEMENT_V1_H + +#include + +#include "color-management-v1-protocol.h" + +struct wlr_color_manager_v1_features { + bool icc_v2_v4; + bool parametric; + bool set_primaries; + bool set_tf_power; + bool set_luminances; + bool set_mastering_display_primaries; + bool extended_target_volume; + bool windows_scrgb; +}; + +struct wlr_color_manager_v1_options { + struct wlr_color_manager_v1_features features; + + const enum wp_color_manager_v1_render_intent *render_intents; + size_t render_intents_len; + + const enum wp_color_manager_v1_transfer_function *transfer_functions; + size_t transfer_functions_len; + + const enum wp_color_manager_v1_primaries *primaries; + size_t primaries_len; +}; + +struct wlr_color_manager_v1 { + struct wl_global *global; + + struct { + struct wlr_color_manager_v1_features features; + + enum wp_color_manager_v1_render_intent *render_intents; + size_t render_intents_len; + + enum wp_color_manager_v1_transfer_function *transfer_functions; + size_t transfer_functions_len; + + enum wp_color_manager_v1_primaries *primaries; + size_t primaries_len; + + struct wl_list outputs; // wlr_color_management_output_v1.link + + struct wl_listener display_destroy; + } WLR_PRIVATE; +}; + +struct wlr_color_manager_v1 *wlr_color_manager_v1_create(struct wl_display *display, + uint32_t version, const struct wlr_color_manager_v1_options *options); + +#endif diff --git a/protocol/meson.build b/protocol/meson.build index f55539e93..2a04a0377 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -1,5 +1,5 @@ wayland_protos = dependency('wayland-protocols', - version: '>=1.40', + version: '>=1.41', fallback: 'wayland-protocols', default_options: ['tests=false'], ) @@ -24,6 +24,7 @@ protocols = { # Staging upstream protocols 'alpha-modifier-v1': wl_protocol_dir / 'staging/alpha-modifier/alpha-modifier-v1.xml', + 'color-management-v1': wl_protocol_dir / 'staging/color-management/color-management-v1.xml', 'content-type-v1': wl_protocol_dir / 'staging/content-type/content-type-v1.xml', 'cursor-shape-v1': wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml', 'drm-lease-v1': wl_protocol_dir / 'staging/drm-lease/drm-lease-v1.xml', diff --git a/types/meson.build b/types/meson.build index 5f44fd27e..1995661e1 100644 --- a/types/meson.build +++ b/types/meson.build @@ -36,6 +36,7 @@ wlr_files += files( 'buffer/readonly_data.c', 'buffer/resource.c', 'wlr_alpha_modifier_v1.c', + 'wlr_color_management_v1.c', 'wlr_compositor.c', 'wlr_content_type_v1.c', 'wlr_cursor_shape_v1.c', diff --git a/types/wlr_color_management_v1.c b/types/wlr_color_management_v1.c new file mode 100644 index 000000000..b9b1c4b19 --- /dev/null +++ b/types/wlr_color_management_v1.c @@ -0,0 +1,542 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "render/color.h" + +#define COLOR_MANAGEMENT_V1_VERSION 1 + +struct wlr_color_management_output_v1 { + struct wl_resource *resource; + struct wlr_output *output; + struct wl_list link; + + struct wl_listener output_destroy; +}; + +struct wlr_color_management_surface_v1 { + struct wl_resource *resource; + struct wlr_surface *surface; + struct wlr_color_manager_v1 *manager; + + struct wlr_addon addon; +}; + +struct wlr_color_management_surface_feedback_v1 { + struct wl_resource *resource; + struct wlr_surface *surface; + + struct wl_listener surface_destroy; +}; + +static void resource_handle_destroy(struct wl_client *client, struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static enum wlr_color_named_primaries named_primaries_to_wlr( + enum wp_color_manager_v1_primaries primaries) { + switch (primaries) { + case WP_COLOR_MANAGER_V1_PRIMARIES_SRGB: + return WLR_COLOR_NAMED_PRIMARIES_SRGB; + case WP_COLOR_MANAGER_V1_PRIMARIES_BT2020: + return WLR_COLOR_NAMED_PRIMARIES_BT2020; + default: + abort(); + } +} + +static enum wlr_color_transfer_function transfer_function_to_wlr( + enum wp_color_manager_v1_transfer_function tf) { + switch (tf) { + case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB: + return WLR_COLOR_TRANSFER_FUNCTION_SRGB; + case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ: + return WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ; + default: + abort(); + } +} + +static int32_t encode_cie1931_coord(float value) { + return round(value * 1000 * 1000); +} + +static void image_desc_handle_get_information(struct wl_client *client, + struct wl_resource *image_desc_resource, uint32_t id) { + uint32_t version = wl_resource_get_version(image_desc_resource); + struct wl_resource *resource = wl_resource_create(client, + &wp_image_description_info_v1_interface, version, id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + + enum wp_color_manager_v1_primaries primaries_named = + WP_COLOR_MANAGER_V1_PRIMARIES_SRGB; + enum wp_color_manager_v1_transfer_function transfer_function = + WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB; + + struct wlr_color_primaries primaries; + wlr_color_primaries_from_named(&primaries, named_primaries_to_wlr(primaries_named)); + + struct wlr_color_luminances luminances; + wlr_color_transfer_function_get_default_luminance(transfer_function_to_wlr(transfer_function), &luminances); + + wp_image_description_info_v1_send_primaries_named(resource, primaries_named); + wp_image_description_info_v1_send_primaries(resource, + encode_cie1931_coord(primaries.red.x), encode_cie1931_coord(primaries.red.y), + encode_cie1931_coord(primaries.green.x), encode_cie1931_coord(primaries.green.y), + encode_cie1931_coord(primaries.blue.x), encode_cie1931_coord(primaries.blue.y), + encode_cie1931_coord(primaries.white.x), encode_cie1931_coord(primaries.white.y)); + wp_image_description_info_v1_send_tf_named(resource, transfer_function); + wp_image_description_info_v1_send_luminances(resource, + round(luminances.min * 10000), round(luminances.max), + round(luminances.reference)); + // TODO: send mastering display primaries and luminances here when we add + // support for features.set_mastering_display_primaries + wp_image_description_info_v1_send_target_primaries(resource, + encode_cie1931_coord(primaries.red.x), encode_cie1931_coord(primaries.red.y), + encode_cie1931_coord(primaries.green.x), encode_cie1931_coord(primaries.green.y), + encode_cie1931_coord(primaries.blue.x), encode_cie1931_coord(primaries.blue.y), + encode_cie1931_coord(primaries.white.x), encode_cie1931_coord(primaries.white.y)); + wp_image_description_info_v1_send_target_luminance(resource, + round(luminances.min * 10000), round(luminances.max)); + // TODO: send target_max_cll and target_max_fall + wp_image_description_info_v1_send_done(resource); + wl_resource_destroy(resource); +} + +static const struct wp_image_description_v1_interface image_desc_impl = { + .destroy = resource_handle_destroy, + .get_information = image_desc_handle_get_information, +}; + +static void image_desc_create_ready(struct wl_resource *parent_resource, uint32_t id) { + struct wl_client *client = wl_resource_get_client(parent_resource); + uint32_t version = wl_resource_get_version(parent_resource); + struct wl_resource *resource = wl_resource_create(client, + &wp_image_description_v1_interface, version, id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &image_desc_impl, NULL, NULL); + + wp_image_description_v1_send_ready(resource, 1); +} + +static const struct wp_color_management_output_v1_interface cm_output_impl; + +static struct wlr_color_management_output_v1 *cm_output_from_resource(struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &wp_color_management_output_v1_interface, &cm_output_impl)); + return wl_resource_get_user_data(resource); +} + +static void cm_output_handle_get_image_description(struct wl_client *client, + struct wl_resource *cm_output_resource, uint32_t id) { + image_desc_create_ready(cm_output_resource, id); +} + +static const struct wp_color_management_output_v1_interface cm_output_impl = { + .destroy = resource_handle_destroy, + .get_image_description = cm_output_handle_get_image_description, +}; + +static void cm_output_destroy(struct wlr_color_management_output_v1 *cm_output) { + if (cm_output == NULL) { + return; + } + wl_resource_set_user_data(cm_output->resource, NULL); // make inert + wl_list_remove(&cm_output->output_destroy.link); + wl_list_remove(&cm_output->link); + free(cm_output); +} + +static void cm_output_handle_output_destroy(struct wl_listener *listener, void *data) { + struct wlr_color_management_output_v1 *cm_output = wl_container_of(listener, cm_output, output_destroy); + cm_output_destroy(cm_output); +} + +static void cm_output_handle_resource_destroy(struct wl_resource *resource) { + struct wlr_color_management_output_v1 *cm_output = cm_output_from_resource(resource); + cm_output_destroy(cm_output); +} + +static void cm_surface_destroy(struct wlr_color_management_surface_v1 *cm_surface) { + if (cm_surface == NULL) { + return; + } + wl_resource_set_user_data(cm_surface->resource, NULL); // make inert + wlr_addon_finish(&cm_surface->addon); + free(cm_surface); +} + +static void cm_surface_handle_addon_destroy(struct wlr_addon *addon) { + struct wlr_color_management_surface_v1 *cm_surface = wl_container_of(addon, cm_surface, addon); + cm_surface_destroy(cm_surface); +} + +static const struct wlr_addon_interface cm_surface_addon_impl = { + .name = "wlr_color_management_surface_v1", + .destroy = cm_surface_handle_addon_destroy, +}; + +static const struct wp_color_management_surface_v1_interface cm_surface_impl; + +static struct wlr_color_management_surface_v1 *cm_surface_from_resource(struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &wp_color_management_surface_v1_interface, &cm_surface_impl)); + return wl_resource_get_user_data(resource); +} + +static void cm_surface_handle_set_image_description(struct wl_client *client, + struct wl_resource *cm_surface_resource, + struct wl_resource *image_desc_resource, uint32_t render_intent) { + struct wlr_color_management_surface_v1 *cm_surface = cm_surface_from_resource(cm_surface_resource); + if (cm_surface == NULL) { + wl_resource_post_error(cm_surface_resource, + WP_COLOR_MANAGEMENT_SURFACE_V1_ERROR_INERT, + "set_image_description cannot be sent on an inert object"); + return; + } + + bool found = false; + for (size_t i = 0; i < cm_surface->manager->render_intents_len; i++) { + if (cm_surface->manager->render_intents[i] == render_intent) { + found = true; + break; + } + } + if (!found) { + wl_resource_post_error(cm_surface_resource, + WP_COLOR_MANAGEMENT_SURFACE_V1_ERROR_RENDER_INTENT, + "invalid render intent"); + return; + } + + // TODO +} + +static void cm_surface_handle_unset_image_description(struct wl_client *client, + struct wl_resource *cm_surface_resource) { + struct wlr_color_management_surface_v1 *cm_surface = cm_surface_from_resource(cm_surface_resource); + if (cm_surface == NULL) { + wl_resource_post_error(cm_surface_resource, + WP_COLOR_MANAGEMENT_SURFACE_V1_ERROR_INERT, + "set_image_description cannot be sent on an inert object"); + return; + } + + // TODO +} + +static const struct wp_color_management_surface_v1_interface cm_surface_impl = { + .destroy = resource_handle_destroy, + .set_image_description = cm_surface_handle_set_image_description, + .unset_image_description = cm_surface_handle_unset_image_description, +}; + +static void cm_surface_handle_resource_destroy(struct wl_resource *resource) { + struct wlr_color_management_surface_v1 *cm_surface = cm_surface_from_resource(resource); + cm_surface_destroy(cm_surface); +} + +static const struct wp_color_management_surface_feedback_v1_interface surface_feedback_impl; + +static struct wlr_color_management_surface_feedback_v1 *surface_feedback_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &wp_color_management_surface_feedback_v1_interface, &surface_feedback_impl)); + return wl_resource_get_user_data(resource); +} + +static void surface_feedback_handle_get_preferred(struct wl_client *client, + struct wl_resource *surface_feedback_resource, uint32_t id) { + image_desc_create_ready(surface_feedback_resource, id); +} + +static const struct wp_color_management_surface_feedback_v1_interface surface_feedback_impl = { + .destroy = resource_handle_destroy, + .get_preferred = surface_feedback_handle_get_preferred, +}; + +static void surface_feedback_destroy(struct wlr_color_management_surface_feedback_v1 *surface_feedback) { + if (surface_feedback == NULL) { + return; + } + wl_resource_set_user_data(surface_feedback->resource, NULL); // make inert + wl_list_remove(&surface_feedback->surface_destroy.link); + free(surface_feedback); +} + +static void surface_feedback_handle_resource_destroy(struct wl_resource *resource) { + struct wlr_color_management_surface_feedback_v1 *surface_feedback = + surface_feedback_from_resource(resource); + surface_feedback_destroy(surface_feedback); +} + +static void surface_feedback_handle_surface_destroy(struct wl_listener *listener, void *data) { + struct wlr_color_management_surface_feedback_v1 *surface_feedback = + wl_container_of(listener, surface_feedback, surface_destroy); + surface_feedback_destroy(surface_feedback); +} + +static const struct wp_color_manager_v1_interface manager_impl; + +static struct wlr_color_manager_v1 *manager_from_resource(struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &wp_color_manager_v1_interface, &manager_impl)); + return wl_resource_get_user_data(resource); +} + +static void manager_handle_get_output(struct wl_client *client, + struct wl_resource *manager_resource, uint32_t id, + struct wl_resource *output_resource) { + struct wlr_color_manager_v1 *manager = manager_from_resource(manager_resource); + struct wlr_output *output = wlr_output_from_resource(output_resource); + + struct wlr_color_management_output_v1 *cm_output = calloc(1, sizeof(*cm_output)); + if (cm_output == NULL) { + wl_client_post_no_memory(client); + return; + } + + uint32_t version = wl_resource_get_version(manager_resource); + cm_output->resource = wl_resource_create(client, + &wp_color_management_output_v1_interface, version, id); + if (!cm_output->resource) { + wl_client_post_no_memory(client); + free(cm_output); + return; + } + wl_resource_set_implementation(cm_output->resource, &cm_output_impl, + cm_output, cm_output_handle_resource_destroy); + + cm_output->output_destroy.notify = cm_output_handle_output_destroy; + wl_signal_add(&output->events.destroy, &cm_output->output_destroy); + + wl_list_insert(&manager->outputs, &cm_output->link); +} + +static void manager_handle_get_surface(struct wl_client *client, + struct wl_resource *manager_resource, uint32_t id, + struct wl_resource *surface_resource) { + struct wlr_color_manager_v1 *manager = manager_from_resource(manager_resource); + struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); + + if (wlr_addon_find(&surface->addons, NULL, &cm_surface_addon_impl) != NULL) { + wl_resource_post_error(manager_resource, + WP_COLOR_MANAGER_V1_ERROR_SURFACE_EXISTS, + "wp_color_management_surface_v1 already constructed for this surface"); + return; + } + + struct wlr_color_management_surface_v1 *cm_surface = calloc(1, sizeof(*cm_surface)); + if (cm_surface == NULL) { + wl_client_post_no_memory(client); + return; + } + + uint32_t version = wl_resource_get_version(manager_resource); + cm_surface->resource = wl_resource_create(client, + &wp_color_management_surface_v1_interface, version, id); + if (!cm_surface->resource) { + wl_client_post_no_memory(client); + free(cm_surface); + return; + } + wl_resource_set_implementation(cm_surface->resource, &cm_surface_impl, cm_surface, cm_surface_handle_resource_destroy); + + cm_surface->manager = manager; + cm_surface->surface = surface; + + wlr_addon_init(&cm_surface->addon, &surface->addons, NULL, &cm_surface_addon_impl); +} + +static void manager_handle_get_surface_feedback(struct wl_client *client, + struct wl_resource *manager_resource, uint32_t id, + struct wl_resource *surface_resource) { + struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); + + struct wlr_color_management_surface_feedback_v1 *surface_feedback = + calloc(1, sizeof(*surface_feedback)); + if (surface_feedback == NULL) { + wl_client_post_no_memory(client); + return; + } + + uint32_t version = wl_resource_get_version(manager_resource); + surface_feedback->resource = wl_resource_create(client, + &wp_color_management_surface_feedback_v1_interface, version, id); + if (!surface_feedback->resource) { + wl_client_post_no_memory(client); + free(surface_feedback); + return; + } + wl_resource_set_implementation(surface_feedback->resource, &surface_feedback_impl, + surface_feedback, surface_feedback_handle_resource_destroy); + + surface_feedback->surface = surface; + + surface_feedback->surface_destroy.notify = surface_feedback_handle_surface_destroy; + wl_signal_add(&surface->events.destroy, &surface_feedback->surface_destroy); +} + +static void manager_handle_create_icc_creator(struct wl_client *client, + struct wl_resource *manager_resource, uint32_t id) { + wl_resource_post_error(manager_resource, + WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, + "new_icc_creator is not supported"); +} + +static void manager_handle_create_parametric_creator(struct wl_client *client, + struct wl_resource *manager_resource, uint32_t id) { + wl_resource_post_error(manager_resource, + WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, + "new_parametric_creator is not supported"); +} + +static void manager_handle_create_windows_scrgb(struct wl_client *client, + struct wl_resource *manager_resource, uint32_t id) { + wl_resource_post_error(manager_resource, + WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, + "get_windows_scrgb is not supported"); +} + +static const struct wp_color_manager_v1_interface manager_impl = { + .destroy = resource_handle_destroy, + .get_output = manager_handle_get_output, + .get_surface = manager_handle_get_surface, + .get_surface_feedback = manager_handle_get_surface_feedback, + .create_icc_creator = manager_handle_create_icc_creator, + .create_parametric_creator = manager_handle_create_parametric_creator, + .create_windows_scrgb = manager_handle_create_windows_scrgb, +}; + +static void manager_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id) { + struct wlr_color_manager_v1 *manager = data; + + struct wl_resource *resource = wl_resource_create(client, + &wp_color_manager_v1_interface, version, id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &manager_impl, manager, NULL); + + const bool features[] = { + [WP_COLOR_MANAGER_V1_FEATURE_ICC_V2_V4] = manager->features.icc_v2_v4, + [WP_COLOR_MANAGER_V1_FEATURE_PARAMETRIC] = manager->features.parametric, + [WP_COLOR_MANAGER_V1_FEATURE_SET_PRIMARIES] = manager->features.set_primaries, + [WP_COLOR_MANAGER_V1_FEATURE_SET_TF_POWER] = manager->features.set_tf_power, + [WP_COLOR_MANAGER_V1_FEATURE_SET_LUMINANCES] = manager->features.set_luminances, + [WP_COLOR_MANAGER_V1_FEATURE_SET_MASTERING_DISPLAY_PRIMARIES] = manager->features.set_mastering_display_primaries, + [WP_COLOR_MANAGER_V1_FEATURE_EXTENDED_TARGET_VOLUME] = manager->features.extended_target_volume, + [WP_COLOR_MANAGER_V1_FEATURE_WINDOWS_SCRGB] = manager->features.windows_scrgb, + }; + + for (uint32_t i = 0; i < sizeof(features) / sizeof(features[0]); i++) { + if (features[i]) { + wp_color_manager_v1_send_supported_feature(resource, i); + } + } + for (size_t i = 0; i < manager->render_intents_len; i++) { + wp_color_manager_v1_send_supported_intent(resource, + manager->render_intents[i]); + } + for (size_t i = 0; i < manager->transfer_functions_len; i++) { + wp_color_manager_v1_send_supported_tf_named(resource, + manager->transfer_functions[i]); + } + for (size_t i = 0; i < manager->primaries_len; i++) { + wp_color_manager_v1_send_supported_primaries_named(resource, + manager->primaries[i]); + } + + wp_color_manager_v1_send_done(resource); +} + +static void manager_handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_color_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); + wl_list_remove(&manager->display_destroy.link); + wl_global_destroy(manager->global); + free(manager->render_intents); + free(manager->transfer_functions); + free(manager->primaries); + free(manager); +} + +static bool memdup(void *out, const void *src, size_t size) { + void *dst = malloc(size); + if (dst == NULL) { + return false; + } + memcpy(dst, src, size); + void **dst_ptr = out; + *dst_ptr = dst; + return true; +} + +struct wlr_color_manager_v1 *wlr_color_manager_v1_create(struct wl_display *display, + uint32_t version, const struct wlr_color_manager_v1_options *options) { + assert(version <= COLOR_MANAGEMENT_V1_VERSION); + + bool has_perceptual_render_intent = false; + for (size_t i = 0; i < options->render_intents_len; i++) { + if (options->render_intents[i] == WP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL) { + has_perceptual_render_intent = true; + } + } + assert(has_perceptual_render_intent); + + // TODO: add support for all of these features + assert(!options->features.icc_v2_v4); + assert(!options->features.parametric); + assert(!options->features.set_primaries); + assert(!options->features.set_tf_power); + assert(!options->features.set_luminances); + assert(!options->features.set_mastering_display_primaries); + assert(!options->features.extended_target_volume); + assert(!options->features.windows_scrgb); + + struct wlr_color_manager_v1 *manager = calloc(1, sizeof(*manager)); + if (manager == NULL) { + return NULL; + } + + manager->features = options->features; + + bool ok = + memdup(&manager->render_intents, options->render_intents, sizeof(options->render_intents[0]) * options->render_intents_len) && + memdup(&manager->transfer_functions, options->transfer_functions, sizeof(options->transfer_functions[0]) * options->transfer_functions_len) && + memdup(&manager->primaries, options->primaries, sizeof(options->primaries[0]) * options->primaries_len); + if (!ok) { + goto err_options; + } + + manager->render_intents_len = options->render_intents_len; + manager->transfer_functions_len = options->transfer_functions_len; + manager->primaries_len = options->primaries_len; + + wl_list_init(&manager->outputs); + + manager->global = wl_global_create(display, &wp_color_manager_v1_interface, + version, manager, manager_bind); + if (manager->global == NULL) { + goto err_options; + } + + manager->display_destroy.notify = manager_handle_display_destroy; + wl_display_add_destroy_listener(display, &manager->display_destroy); + + return manager; + +err_options: + free(manager->render_intents); + free(manager->transfer_functions); + free(manager->primaries); + free(manager); + return NULL; +} From 6d4737a7f604c2f837f180ff783e0dfb66611cc1 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 22 Jan 2025 20:45:35 +0100 Subject: [PATCH 318/519] color-management-v1: add struct wlr_image_description_v1 --- include/wlr/types/wlr_color_management_v1.h | 9 ++ types/wlr_color_management_v1.c | 118 +++++++++++++++++--- 2 files changed, 110 insertions(+), 17 deletions(-) diff --git a/include/wlr/types/wlr_color_management_v1.h b/include/wlr/types/wlr_color_management_v1.h index 4b5d18670..16c3fc6a8 100644 --- a/include/wlr/types/wlr_color_management_v1.h +++ b/include/wlr/types/wlr_color_management_v1.h @@ -13,6 +13,13 @@ #include "color-management-v1-protocol.h" +struct wlr_image_description_v1_data { + uint32_t tf_named; // enum wp_color_manager_v1_transfer_function, zero if unset + uint32_t primaries_named; // enum wp_color_manager_v1_primaries, zero if unset + + uint32_t max_cll, max_fall; // cd/m², zero if unset +}; + struct wlr_color_manager_v1_features { bool icc_v2_v4; bool parametric; @@ -54,6 +61,8 @@ struct wlr_color_manager_v1 { struct wl_list outputs; // wlr_color_management_output_v1.link + uint32_t last_image_desc_identity; + struct wl_listener display_destroy; } WLR_PRIVATE; }; diff --git a/types/wlr_color_management_v1.c b/types/wlr_color_management_v1.c index b9b1c4b19..278789e67 100644 --- a/types/wlr_color_management_v1.c +++ b/types/wlr_color_management_v1.c @@ -13,6 +13,7 @@ struct wlr_color_management_output_v1 { struct wl_resource *resource; struct wlr_output *output; + struct wlr_color_manager_v1 *manager; struct wl_list link; struct wl_listener output_destroy; @@ -29,10 +30,16 @@ struct wlr_color_management_surface_v1 { struct wlr_color_management_surface_feedback_v1 { struct wl_resource *resource; struct wlr_surface *surface; + struct wlr_color_manager_v1 *manager; struct wl_listener surface_destroy; }; +struct wlr_image_description_v1 { + struct wl_resource *resource; + struct wlr_image_description_v1_data data; // immutable +}; + static void resource_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } @@ -65,8 +72,23 @@ static int32_t encode_cie1931_coord(float value) { return round(value * 1000 * 1000); } +static const struct wp_image_description_v1_interface image_desc_impl; + +static struct wlr_image_description_v1 *image_desc_from_resource(struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &wp_image_description_v1_interface, &image_desc_impl)); + return wl_resource_get_user_data(resource); +} + static void image_desc_handle_get_information(struct wl_client *client, struct wl_resource *image_desc_resource, uint32_t id) { + struct wlr_image_description_v1 *image_desc = image_desc_from_resource(image_desc_resource); + if (image_desc == NULL) { + wl_resource_post_error(image_desc_resource, + WP_IMAGE_DESCRIPTION_V1_ERROR_NOT_READY, + "image description is in failed state"); + return; + } + uint32_t version = wl_resource_get_version(image_desc_resource); struct wl_resource *resource = wl_resource_create(client, &wp_image_description_info_v1_interface, version, id); @@ -75,24 +97,19 @@ static void image_desc_handle_get_information(struct wl_client *client, return; } - enum wp_color_manager_v1_primaries primaries_named = - WP_COLOR_MANAGER_V1_PRIMARIES_SRGB; - enum wp_color_manager_v1_transfer_function transfer_function = - WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB; - struct wlr_color_primaries primaries; - wlr_color_primaries_from_named(&primaries, named_primaries_to_wlr(primaries_named)); + wlr_color_primaries_from_named(&primaries, named_primaries_to_wlr(image_desc->data.primaries_named)); struct wlr_color_luminances luminances; - wlr_color_transfer_function_get_default_luminance(transfer_function_to_wlr(transfer_function), &luminances); + wlr_color_transfer_function_get_default_luminance(transfer_function_to_wlr(image_desc->data.tf_named), &luminances); - wp_image_description_info_v1_send_primaries_named(resource, primaries_named); + wp_image_description_info_v1_send_primaries_named(resource, image_desc->data.primaries_named); wp_image_description_info_v1_send_primaries(resource, encode_cie1931_coord(primaries.red.x), encode_cie1931_coord(primaries.red.y), encode_cie1931_coord(primaries.green.x), encode_cie1931_coord(primaries.green.y), encode_cie1931_coord(primaries.blue.x), encode_cie1931_coord(primaries.blue.y), encode_cie1931_coord(primaries.white.x), encode_cie1931_coord(primaries.white.y)); - wp_image_description_info_v1_send_tf_named(resource, transfer_function); + wp_image_description_info_v1_send_tf_named(resource, image_desc->data.tf_named); wp_image_description_info_v1_send_luminances(resource, round(luminances.min * 10000), round(luminances.max), round(luminances.reference)); @@ -115,18 +132,54 @@ static const struct wp_image_description_v1_interface image_desc_impl = { .get_information = image_desc_handle_get_information, }; -static void image_desc_create_ready(struct wl_resource *parent_resource, uint32_t id) { +static void image_desc_handle_resource_destroy(struct wl_resource *resource) { + struct wlr_image_description_v1 *image_desc = image_desc_from_resource(resource); + free(image_desc); +} + +static struct wl_resource *image_desc_create_resource( + struct wl_resource *parent_resource, uint32_t id) { struct wl_client *client = wl_resource_get_client(parent_resource); uint32_t version = wl_resource_get_version(parent_resource); - struct wl_resource *resource = wl_resource_create(client, - &wp_image_description_v1_interface, version, id); - if (!resource) { - wl_client_post_no_memory(client); + return wl_resource_create(client, &wp_image_description_v1_interface, + version, id); +} + +static void image_desc_create_ready(struct wlr_color_manager_v1 *manager, + struct wl_resource *parent_resource, uint32_t id, + const struct wlr_image_description_v1_data *data) { + struct wlr_image_description_v1 *image_desc = calloc(1, sizeof(*image_desc)); + if (image_desc == NULL) { + wl_resource_post_no_memory(parent_resource); + return; + } + + image_desc->data = *data; + + image_desc->resource = image_desc_create_resource(parent_resource, id); + if (!image_desc->resource) { + wl_resource_post_no_memory(parent_resource); + free(image_desc); + return; + } + wl_resource_set_implementation(image_desc->resource, &image_desc_impl, + image_desc, image_desc_handle_resource_destroy); + + // TODO: de-duplicate identity + uint32_t identity = ++manager->last_image_desc_identity; + wp_image_description_v1_send_ready(image_desc->resource, identity); +} + +static void image_desc_create_failed(struct wl_resource *parent_resource, uint32_t id, + enum wp_image_description_v1_cause cause, const char *msg) { + struct wl_resource *resource = image_desc_create_resource(parent_resource, id); + if (resource == NULL) { + wl_resource_post_no_memory(parent_resource); return; } wl_resource_set_implementation(resource, &image_desc_impl, NULL, NULL); - wp_image_description_v1_send_ready(resource, 1); + wp_image_description_v1_send_failed(resource, cause, msg); } static const struct wp_color_management_output_v1_interface cm_output_impl; @@ -138,7 +191,19 @@ static struct wlr_color_management_output_v1 *cm_output_from_resource(struct wl_ static void cm_output_handle_get_image_description(struct wl_client *client, struct wl_resource *cm_output_resource, uint32_t id) { - image_desc_create_ready(cm_output_resource, id); + struct wlr_color_management_output_v1 *cm_output = cm_output_from_resource(cm_output_resource); + if (cm_output == NULL) { + image_desc_create_failed(cm_output_resource, id, + WP_IMAGE_DESCRIPTION_V1_CAUSE_NO_OUTPUT, + "output has been destroyed"); + return; + } + + struct wlr_image_description_v1_data data = { + .tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB, + .primaries_named = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB, + }; + image_desc_create_ready(cm_output->manager, cm_output_resource, id, &data); } static const struct wp_color_management_output_v1_interface cm_output_impl = { @@ -254,7 +319,21 @@ static struct wlr_color_management_surface_feedback_v1 *surface_feedback_from_re static void surface_feedback_handle_get_preferred(struct wl_client *client, struct wl_resource *surface_feedback_resource, uint32_t id) { - image_desc_create_ready(surface_feedback_resource, id); + struct wlr_color_management_surface_feedback_v1 *surface_feedback = + surface_feedback_from_resource(surface_feedback_resource); + if (surface_feedback == NULL) { + wl_resource_post_error(surface_feedback_resource, + WP_COLOR_MANAGEMENT_SURFACE_FEEDBACK_V1_ERROR_INERT, + "get_preferred sent on inert feedback surface"); + return; + } + + struct wlr_image_description_v1_data data = { + .tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB, + .primaries_named = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB, + }; + image_desc_create_ready(surface_feedback->manager, + surface_feedback_resource, id, &data); } static const struct wp_color_management_surface_feedback_v1_interface surface_feedback_impl = { @@ -302,6 +381,8 @@ static void manager_handle_get_output(struct wl_client *client, return; } + cm_output->manager = manager; + uint32_t version = wl_resource_get_version(manager_resource); cm_output->resource = wl_resource_create(client, &wp_color_management_output_v1_interface, version, id); @@ -357,6 +438,7 @@ static void manager_handle_get_surface(struct wl_client *client, static void manager_handle_get_surface_feedback(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id, struct wl_resource *surface_resource) { + struct wlr_color_manager_v1 *manager = manager_from_resource(manager_resource); struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); struct wlr_color_management_surface_feedback_v1 *surface_feedback = @@ -366,6 +448,8 @@ static void manager_handle_get_surface_feedback(struct wl_client *client, return; } + surface_feedback->manager = manager; + uint32_t version = wl_resource_get_version(manager_resource); surface_feedback->resource = wl_resource_create(client, &wp_color_management_surface_feedback_v1_interface, version, id); From ab4ed32c06aab35ffa8891c9bb43cc130cb0281c Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 22 Jan 2025 20:47:54 +0100 Subject: [PATCH 319/519] color-management-v1: add support for parametric image desc creator --- types/wlr_color_management_v1.c | 206 +++++++++++++++++++++++++++++++- 1 file changed, 202 insertions(+), 4 deletions(-) diff --git a/types/wlr_color_management_v1.c b/types/wlr_color_management_v1.c index 278789e67..100b0d921 100644 --- a/types/wlr_color_management_v1.c +++ b/types/wlr_color_management_v1.c @@ -40,6 +40,12 @@ struct wlr_image_description_v1 { struct wlr_image_description_v1_data data; // immutable }; +struct wlr_image_description_creator_params_v1 { + struct wl_resource *resource; + struct wlr_color_manager_v1 *manager; + struct wlr_image_description_v1_data data; +}; + static void resource_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } @@ -362,6 +368,176 @@ static void surface_feedback_handle_surface_destroy(struct wl_listener *listener surface_feedback_destroy(surface_feedback); } +static const struct wp_image_description_creator_params_v1_interface image_desc_creator_params_impl; + +static struct wlr_image_description_creator_params_v1 * +image_desc_creator_params_from_resource(struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &wp_image_description_creator_params_v1_interface, + &image_desc_creator_params_impl)); + return wl_resource_get_user_data(resource); +} + +static void image_desc_creator_params_handle_create(struct wl_client *client, + struct wl_resource *params_resource, uint32_t id) { + struct wlr_image_description_creator_params_v1 *params = + image_desc_creator_params_from_resource(params_resource); + + if (params->data.tf_named == 0) { + wl_resource_post_error(params_resource, + WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INCOMPLETE_SET, + "missing transfer function"); + return; + } + if (params->data.primaries_named == 0) { + wl_resource_post_error(params_resource, + WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INCOMPLETE_SET, + "missing primaries"); + return; + } + + if (params->data.max_cll != 0 && params->data.max_fall != 0 && + params->data.max_fall > params->data.max_cll) { + wl_resource_post_error(params_resource, + WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_LUMINANCE, + "max_fall must be less or equal to max_cll"); + return; + } + + image_desc_create_ready(params->manager, params_resource, id, ¶ms->data); +} + +static void image_desc_creator_params_handle_set_tf_named(struct wl_client *client, + struct wl_resource *params_resource, uint32_t tf) { + struct wlr_image_description_creator_params_v1 *params = + image_desc_creator_params_from_resource(params_resource); + + if (params->data.tf_named != 0) { + wl_resource_post_error(params_resource, + WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, + "transfer function already set"); + return; + } + + bool found = false; + for (size_t i = 0; i < params->manager->transfer_functions_len; i++) { + if (params->manager->transfer_functions[i] == tf) { + found = true; + break; + } + } + if (!found) { + wl_resource_post_error(params_resource, + WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_TF, + "invalid transfer function"); + return; + } + + params->data.tf_named = tf; +} + +static void image_desc_creator_params_handle_set_tf_power(struct wl_client *client, + struct wl_resource *params_resource, uint32_t eexp) { + wl_resource_post_error(params_resource, + WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_UNSUPPORTED_FEATURE, + "set_tf_power is not supported"); +} + +static void image_desc_creator_params_handle_set_primaries_named(struct wl_client *client, + struct wl_resource *params_resource, uint32_t primaries) { + struct wlr_image_description_creator_params_v1 *params = + image_desc_creator_params_from_resource(params_resource); + + if (params->data.primaries_named != 0) { + wl_resource_post_error(params_resource, + WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, + "primaries already set"); + return; + } + + bool found = false; + for (size_t i = 0; i < params->manager->primaries_len; i++) { + if (params->manager->primaries[i] == primaries) { + found = true; + break; + } + } + if (!found) { + wl_resource_post_error(params_resource, + WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_PRIMARIES_NAMED, + "invalid primaries"); + return; + } + + params->data.primaries_named = primaries; +} + +static void image_desc_creator_params_handle_set_primaries(struct wl_client *client, + struct wl_resource *params_resource, int32_t r_x, int32_t r_y, + int32_t g_x, int32_t g_y, int32_t b_x, int32_t b_y, + int32_t w_x, int32_t w_y) { + wl_resource_post_error(params_resource, + WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_UNSUPPORTED_FEATURE, + "set_primaries is not supported"); +} + +static void image_desc_creator_params_handle_set_luminances(struct wl_client *client, + struct wl_resource *params_resource, uint32_t min_lum, + uint32_t max_lum, uint32_t reference_lum) { + wl_resource_post_error(params_resource, + WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_UNSUPPORTED_FEATURE, + "set_luminances is not supported"); +} + +static void image_desc_creator_params_handle_set_mastering_display_primaries( + struct wl_client *client, struct wl_resource *params_resource, + int32_t r_x, int32_t r_y, int32_t g_x, int32_t g_y, + int32_t b_x, int32_t b_y, int32_t w_x, int32_t w_y) { + wl_resource_post_error(params_resource, + WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_UNSUPPORTED_FEATURE, + "set_mastering_display_primaries is not supported"); +} + +static void image_desc_creator_params_handle_set_mastering_luminance(struct wl_client *client, + struct wl_resource *params_resource, uint32_t min_lum, uint32_t max_lum) { + wl_resource_post_error(params_resource, + WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_UNSUPPORTED_FEATURE, + "set_mastering_luminance is not supported"); +} + +static void image_desc_creator_params_handle_set_max_cll(struct wl_client *client, + struct wl_resource *params_resource, uint32_t max_cll) { + struct wlr_image_description_creator_params_v1 *params = + image_desc_creator_params_from_resource(params_resource); + params->data.max_cll = max_cll; +} + +static void image_desc_creator_params_handle_set_max_fall(struct wl_client *client, + struct wl_resource *params_resource, uint32_t max_fall) { + struct wlr_image_description_creator_params_v1 *params = + image_desc_creator_params_from_resource(params_resource); + params->data.max_fall = max_fall; +} + +static const struct wp_image_description_creator_params_v1_interface image_desc_creator_params_impl = { + .create = image_desc_creator_params_handle_create, + .set_tf_named = image_desc_creator_params_handle_set_tf_named, + .set_tf_power = image_desc_creator_params_handle_set_tf_power, + .set_primaries_named = image_desc_creator_params_handle_set_primaries_named, + .set_primaries = image_desc_creator_params_handle_set_primaries, + .set_luminances = image_desc_creator_params_handle_set_luminances, + .set_mastering_display_primaries = image_desc_creator_params_handle_set_mastering_display_primaries, + .set_mastering_luminance = image_desc_creator_params_handle_set_mastering_luminance, + .set_max_cll = image_desc_creator_params_handle_set_max_cll, + .set_max_fall = image_desc_creator_params_handle_set_max_fall, +}; + +static void image_desc_creator_params_handle_resource_destroy(struct wl_resource *resource) { + struct wlr_image_description_creator_params_v1 *params = + image_desc_creator_params_from_resource(resource); + free(params); +} + static const struct wp_color_manager_v1_interface manager_impl; static struct wlr_color_manager_v1 *manager_from_resource(struct wl_resource *resource) { @@ -476,9 +652,32 @@ static void manager_handle_create_icc_creator(struct wl_client *client, static void manager_handle_create_parametric_creator(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id) { - wl_resource_post_error(manager_resource, - WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, - "new_parametric_creator is not supported"); + struct wlr_color_manager_v1 *manager = manager_from_resource(manager_resource); + if (!manager->features.parametric) { + wl_resource_post_error(manager_resource, + WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, + "new_parametric_creator is not supported"); + return; + } + + struct wlr_image_description_creator_params_v1 *params = calloc(1, sizeof(*params)); + if (params == NULL) { + wl_client_post_no_memory(client); + return; + } + + params->manager = manager; + + uint32_t version = wl_resource_get_version(manager_resource); + params->resource = wl_resource_create(client, + &wp_image_description_creator_params_v1_interface, version, id); + if (!params->resource) { + wl_client_post_no_memory(client); + free(params); + return; + } + wl_resource_set_implementation(params->resource, &image_desc_creator_params_impl, + params, image_desc_creator_params_handle_resource_destroy); } static void manager_handle_create_windows_scrgb(struct wl_client *client, @@ -577,7 +776,6 @@ struct wlr_color_manager_v1 *wlr_color_manager_v1_create(struct wl_display *disp // TODO: add support for all of these features assert(!options->features.icc_v2_v4); - assert(!options->features.parametric); assert(!options->features.set_primaries); assert(!options->features.set_tf_power); assert(!options->features.set_luminances); From dcf38e3ea96ce91a1e3162e8e865181cf2f3cffc Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 22 Jan 2025 21:32:21 +0100 Subject: [PATCH 320/519] color-management-v1: add wlr_surface_get_image_description_v1_data() --- include/wlr/types/wlr_color_management_v1.h | 5 ++ types/wlr_color_management_v1.c | 66 ++++++++++++++++++--- 2 files changed, 64 insertions(+), 7 deletions(-) diff --git a/include/wlr/types/wlr_color_management_v1.h b/include/wlr/types/wlr_color_management_v1.h index 16c3fc6a8..1be842eb6 100644 --- a/include/wlr/types/wlr_color_management_v1.h +++ b/include/wlr/types/wlr_color_management_v1.h @@ -13,6 +13,8 @@ #include "color-management-v1-protocol.h" +struct wlr_surface; + struct wlr_image_description_v1_data { uint32_t tf_named; // enum wp_color_manager_v1_transfer_function, zero if unset uint32_t primaries_named; // enum wp_color_manager_v1_primaries, zero if unset @@ -70,4 +72,7 @@ struct wlr_color_manager_v1 { struct wlr_color_manager_v1 *wlr_color_manager_v1_create(struct wl_display *display, uint32_t version, const struct wlr_color_manager_v1_options *options); +const struct wlr_image_description_v1_data * +wlr_surface_get_image_description_v1_data(struct wlr_surface *surface); + #endif diff --git a/types/wlr_color_management_v1.c b/types/wlr_color_management_v1.c index 100b0d921..d9892d3b5 100644 --- a/types/wlr_color_management_v1.c +++ b/types/wlr_color_management_v1.c @@ -19,12 +19,20 @@ struct wlr_color_management_output_v1 { struct wl_listener output_destroy; }; +struct wlr_color_management_surface_v1_state { + bool has_image_desc_data; + struct wlr_image_description_v1_data image_desc_data; +}; + struct wlr_color_management_surface_v1 { struct wl_resource *resource; struct wlr_surface *surface; struct wlr_color_manager_v1 *manager; struct wlr_addon addon; + struct wlr_surface_synced synced; + + struct wlr_color_management_surface_v1_state current, pending; }; struct wlr_color_management_surface_feedback_v1 { @@ -37,6 +45,7 @@ struct wlr_color_management_surface_feedback_v1 { struct wlr_image_description_v1 { struct wl_resource *resource; + bool get_info_allowed; struct wlr_image_description_v1_data data; // immutable }; @@ -95,6 +104,13 @@ static void image_desc_handle_get_information(struct wl_client *client, return; } + if (!image_desc->get_info_allowed) { + wl_resource_post_error(image_desc_resource, + WP_IMAGE_DESCRIPTION_V1_ERROR_NO_INFORMATION, + "get_information not allowed"); + return; + } + uint32_t version = wl_resource_get_version(image_desc_resource); struct wl_resource *resource = wl_resource_create(client, &wp_image_description_info_v1_interface, version, id); @@ -153,7 +169,8 @@ static struct wl_resource *image_desc_create_resource( static void image_desc_create_ready(struct wlr_color_manager_v1 *manager, struct wl_resource *parent_resource, uint32_t id, - const struct wlr_image_description_v1_data *data) { + const struct wlr_image_description_v1_data *data, + bool get_info_allowed) { struct wlr_image_description_v1 *image_desc = calloc(1, sizeof(*image_desc)); if (image_desc == NULL) { wl_resource_post_no_memory(parent_resource); @@ -161,6 +178,7 @@ static void image_desc_create_ready(struct wlr_color_manager_v1 *manager, } image_desc->data = *data; + image_desc->get_info_allowed = get_info_allowed; image_desc->resource = image_desc_create_resource(parent_resource, id); if (!image_desc->resource) { @@ -209,7 +227,7 @@ static void cm_output_handle_get_image_description(struct wl_client *client, .tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB, .primaries_named = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB, }; - image_desc_create_ready(cm_output->manager, cm_output_resource, id, &data); + image_desc_create_ready(cm_output->manager, cm_output_resource, id, &data, true); } static const struct wp_color_management_output_v1_interface cm_output_impl = { @@ -242,10 +260,15 @@ static void cm_surface_destroy(struct wlr_color_management_surface_v1 *cm_surfac return; } wl_resource_set_user_data(cm_surface->resource, NULL); // make inert + wlr_surface_synced_finish(&cm_surface->synced); wlr_addon_finish(&cm_surface->addon); free(cm_surface); } +static const struct wlr_surface_synced_impl cm_surface_synced_impl = { + .state_size = sizeof(struct wlr_color_management_surface_v1_state), +}; + static void cm_surface_handle_addon_destroy(struct wlr_addon *addon) { struct wlr_color_management_surface_v1 *cm_surface = wl_container_of(addon, cm_surface, addon); cm_surface_destroy(cm_surface); @@ -274,6 +297,8 @@ static void cm_surface_handle_set_image_description(struct wl_client *client, return; } + struct wlr_image_description_v1 *image_desc = image_desc_from_resource(image_desc_resource); + bool found = false; for (size_t i = 0; i < cm_surface->manager->render_intents_len; i++) { if (cm_surface->manager->render_intents[i] == render_intent) { @@ -288,7 +313,8 @@ static void cm_surface_handle_set_image_description(struct wl_client *client, return; } - // TODO + cm_surface->pending.has_image_desc_data = true; + cm_surface->pending.image_desc_data = image_desc->data; } static void cm_surface_handle_unset_image_description(struct wl_client *client, @@ -301,7 +327,7 @@ static void cm_surface_handle_unset_image_description(struct wl_client *client, return; } - // TODO + cm_surface->pending.has_image_desc_data = false; } static const struct wp_color_management_surface_v1_interface cm_surface_impl = { @@ -339,7 +365,7 @@ static void surface_feedback_handle_get_preferred(struct wl_client *client, .primaries_named = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB, }; image_desc_create_ready(surface_feedback->manager, - surface_feedback_resource, id, &data); + surface_feedback_resource, id, &data, true); } static const struct wp_color_management_surface_feedback_v1_interface surface_feedback_impl = { @@ -404,7 +430,7 @@ static void image_desc_creator_params_handle_create(struct wl_client *client, return; } - image_desc_create_ready(params->manager, params_resource, id, ¶ms->data); + image_desc_create_ready(params->manager, params_resource, id, ¶ms->data, false); } static void image_desc_creator_params_handle_set_tf_named(struct wl_client *client, @@ -576,13 +602,22 @@ static void manager_handle_get_output(struct wl_client *client, wl_list_insert(&manager->outputs, &cm_output->link); } +static struct wlr_color_management_surface_v1 *cm_surface_from_surface(struct wlr_surface *surface) { + struct wlr_addon *addon = wlr_addon_find(&surface->addons, NULL, &cm_surface_addon_impl); + if (addon == NULL) { + return NULL; + } + struct wlr_color_management_surface_v1 *cm_surface = wl_container_of(addon, cm_surface, addon); + return cm_surface; +} + static void manager_handle_get_surface(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id, struct wl_resource *surface_resource) { struct wlr_color_manager_v1 *manager = manager_from_resource(manager_resource); struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); - if (wlr_addon_find(&surface->addons, NULL, &cm_surface_addon_impl) != NULL) { + if (cm_surface_from_surface(surface) != NULL) { wl_resource_post_error(manager_resource, WP_COLOR_MANAGER_V1_ERROR_SURFACE_EXISTS, "wp_color_management_surface_v1 already constructed for this surface"); @@ -595,11 +630,19 @@ static void manager_handle_get_surface(struct wl_client *client, return; } + if (!wlr_surface_synced_init(&cm_surface->synced, surface, &cm_surface_synced_impl, + &cm_surface->pending, &cm_surface->current)) { + wl_client_post_no_memory(client); + free(cm_surface); + return; + } + uint32_t version = wl_resource_get_version(manager_resource); cm_surface->resource = wl_resource_create(client, &wp_color_management_surface_v1_interface, version, id); if (!cm_surface->resource) { wl_client_post_no_memory(client); + wlr_surface_synced_finish(&cm_surface->synced); free(cm_surface); return; } @@ -822,3 +865,12 @@ err_options: free(manager); return NULL; } + +const struct wlr_image_description_v1_data * +wlr_surface_get_image_description_v1_data(struct wlr_surface *surface) { + struct wlr_color_management_surface_v1 *cm_surface = cm_surface_from_surface(surface); + if (cm_surface == NULL || !cm_surface->current.has_image_desc_data) { + return NULL; + } + return &cm_surface->current.image_desc_data; +} From 95c85af87c157bbc86149a9375f6933798ae0318 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 24 Jan 2025 17:31:50 +0100 Subject: [PATCH 321/519] color-management-v1: add support for mastering display metadata --- include/wlr/types/wlr_color_management_v1.h | 9 ++ types/wlr_color_management_v1.c | 93 +++++++++++++++++++-- 2 files changed, 95 insertions(+), 7 deletions(-) diff --git a/include/wlr/types/wlr_color_management_v1.h b/include/wlr/types/wlr_color_management_v1.h index 1be842eb6..68b1d317d 100644 --- a/include/wlr/types/wlr_color_management_v1.h +++ b/include/wlr/types/wlr_color_management_v1.h @@ -10,6 +10,7 @@ #define WLR_TYPES_WLR_COLOR_MANAGEMENT_V1_H #include +#include #include "color-management-v1-protocol.h" @@ -19,6 +20,14 @@ struct wlr_image_description_v1_data { uint32_t tf_named; // enum wp_color_manager_v1_transfer_function, zero if unset uint32_t primaries_named; // enum wp_color_manager_v1_primaries, zero if unset + bool has_mastering_display_primaries; + struct wlr_color_primaries mastering_display_primaries; + + bool has_mastering_luminance; + struct { + float min, max; // cd/m² + } mastering_luminance; + uint32_t max_cll, max_fall; // cd/m², zero if unset }; diff --git a/types/wlr_color_management_v1.c b/types/wlr_color_management_v1.c index d9892d3b5..5c210df26 100644 --- a/types/wlr_color_management_v1.c +++ b/types/wlr_color_management_v1.c @@ -87,6 +87,10 @@ static int32_t encode_cie1931_coord(float value) { return round(value * 1000 * 1000); } +static float decode_cie1931_coord(int32_t raw) { + return (float)raw / (1000 * 1000); +} + static const struct wp_image_description_v1_interface image_desc_impl; static struct wlr_image_description_v1 *image_desc_from_resource(struct wl_resource *resource) { @@ -404,6 +408,31 @@ image_desc_creator_params_from_resource(struct wl_resource *resource) { return wl_resource_get_user_data(resource); } +static bool check_mastering_luminance_range(struct wl_resource *params_resource, + const struct wlr_image_description_v1_data *data, + float value, const char *name) { + if (!data->has_mastering_luminance || value == 0) { + return true; + } + + if (value <= data->mastering_luminance.min) { + wl_resource_post_error(params_resource, + WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_LUMINANCE, + "%s must be greater than min L of the mastering luminance range", + name); + return false; + } + if (value > data->mastering_luminance.max) { + wl_resource_post_error(params_resource, + WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_LUMINANCE, + "%s must be less or equal to max L of the mastering luminance range", + name); + return false; + } + + return true; +} + static void image_desc_creator_params_handle_create(struct wl_client *client, struct wl_resource *params_resource, uint32_t id) { struct wlr_image_description_creator_params_v1 *params = @@ -430,6 +459,14 @@ static void image_desc_creator_params_handle_create(struct wl_client *client, return; } + if (!check_mastering_luminance_range(params_resource, ¶ms->data, params->data.max_cll, "max_cll") || + !check_mastering_luminance_range(params_resource, ¶ms->data, params->data.max_fall, "max_fall")) { + return; + } + + // TODO: check that the target color volume is contained within the + // primary color volume + image_desc_create_ready(params->manager, params_resource, id, ¶ms->data, false); } @@ -519,16 +556,59 @@ static void image_desc_creator_params_handle_set_mastering_display_primaries( struct wl_client *client, struct wl_resource *params_resource, int32_t r_x, int32_t r_y, int32_t g_x, int32_t g_y, int32_t b_x, int32_t b_y, int32_t w_x, int32_t w_y) { - wl_resource_post_error(params_resource, - WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_UNSUPPORTED_FEATURE, - "set_mastering_display_primaries is not supported"); + struct wlr_image_description_creator_params_v1 *params = + image_desc_creator_params_from_resource(params_resource); + if (!params->manager->features.set_mastering_display_primaries) { + wl_resource_post_error(params_resource, + WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_UNSUPPORTED_FEATURE, + "set_mastering_display_primaries is not supported"); + return; + } + + if (params->data.has_mastering_display_primaries) { + wl_resource_post_error(params_resource, + WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, + "mastering display primaries already set"); + return; + } + + params->data.has_mastering_display_primaries = true; + params->data.mastering_display_primaries = (struct wlr_color_primaries){ + .red = { decode_cie1931_coord(r_x), decode_cie1931_coord(r_y) }, + .green = { decode_cie1931_coord(g_x), decode_cie1931_coord(g_y) }, + .blue = { decode_cie1931_coord(b_x), decode_cie1931_coord(b_y) }, + .white = { decode_cie1931_coord(w_x), decode_cie1931_coord(w_y) }, + }; } static void image_desc_creator_params_handle_set_mastering_luminance(struct wl_client *client, struct wl_resource *params_resource, uint32_t min_lum, uint32_t max_lum) { - wl_resource_post_error(params_resource, - WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_UNSUPPORTED_FEATURE, - "set_mastering_luminance is not supported"); + struct wlr_image_description_creator_params_v1 *params = + image_desc_creator_params_from_resource(params_resource); + if (!params->manager->features.set_mastering_display_primaries) { + wl_resource_post_error(params_resource, + WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_UNSUPPORTED_FEATURE, + "set_mastering_luminance is not supported"); + return; + } + + if (params->data.has_mastering_luminance) { + wl_resource_post_error(params_resource, + WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, + "mastering luminance already set"); + return; + } + + params->data.has_mastering_luminance = true; + params->data.mastering_luminance.min = (float)min_lum / 10000; + params->data.mastering_luminance.max = max_lum; + + if (params->data.mastering_luminance.max <= params->data.mastering_luminance.min) { + wl_resource_post_error(params_resource, + WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_LUMINANCE, + "max luminance must be greater than min luminance"); + return; + } } static void image_desc_creator_params_handle_set_max_cll(struct wl_client *client, @@ -822,7 +902,6 @@ struct wlr_color_manager_v1 *wlr_color_manager_v1_create(struct wl_display *disp assert(!options->features.set_primaries); assert(!options->features.set_tf_power); assert(!options->features.set_luminances); - assert(!options->features.set_mastering_display_primaries); assert(!options->features.extended_target_volume); assert(!options->features.windows_scrgb); From fa6cd856e347a1557149cb31f860c92c790b1dd0 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 24 Jan 2025 18:21:56 +0100 Subject: [PATCH 322/519] color-management-v1: add setter for surface feedback --- include/wlr/types/wlr_color_management_v1.h | 5 ++++ types/wlr_color_management_v1.c | 33 +++++++++++++++++---- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/include/wlr/types/wlr_color_management_v1.h b/include/wlr/types/wlr_color_management_v1.h index 68b1d317d..a369806bc 100644 --- a/include/wlr/types/wlr_color_management_v1.h +++ b/include/wlr/types/wlr_color_management_v1.h @@ -71,6 +71,7 @@ struct wlr_color_manager_v1 { size_t primaries_len; struct wl_list outputs; // wlr_color_management_output_v1.link + struct wl_list surface_feedbacks; // wlr_color_management_surface_feedback_v1.link uint32_t last_image_desc_identity; @@ -84,4 +85,8 @@ struct wlr_color_manager_v1 *wlr_color_manager_v1_create(struct wl_display *disp const struct wlr_image_description_v1_data * wlr_surface_get_image_description_v1_data(struct wlr_surface *surface); +void wlr_color_manager_v1_set_surface_preferred_image_description( + struct wlr_color_manager_v1 *manager, struct wlr_surface *surface, + const struct wlr_image_description_v1_data *data); + #endif diff --git a/types/wlr_color_management_v1.c b/types/wlr_color_management_v1.c index 5c210df26..432f75bca 100644 --- a/types/wlr_color_management_v1.c +++ b/types/wlr_color_management_v1.c @@ -39,6 +39,9 @@ struct wlr_color_management_surface_feedback_v1 { struct wl_resource *resource; struct wlr_surface *surface; struct wlr_color_manager_v1 *manager; + struct wl_list link; + + struct wlr_image_description_v1_data data; struct wl_listener surface_destroy; }; @@ -364,12 +367,8 @@ static void surface_feedback_handle_get_preferred(struct wl_client *client, return; } - struct wlr_image_description_v1_data data = { - .tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB, - .primaries_named = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB, - }; image_desc_create_ready(surface_feedback->manager, - surface_feedback_resource, id, &data, true); + surface_feedback_resource, id, &surface_feedback->data, true); } static const struct wp_color_management_surface_feedback_v1_interface surface_feedback_impl = { @@ -383,6 +382,7 @@ static void surface_feedback_destroy(struct wlr_color_management_surface_feedbac } wl_resource_set_user_data(surface_feedback->resource, NULL); // make inert wl_list_remove(&surface_feedback->surface_destroy.link); + wl_list_remove(&surface_feedback->link); free(surface_feedback); } @@ -761,9 +761,15 @@ static void manager_handle_get_surface_feedback(struct wl_client *client, surface_feedback, surface_feedback_handle_resource_destroy); surface_feedback->surface = surface; + surface_feedback->data = (struct wlr_image_description_v1_data){ + .tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB, + .primaries_named = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB, + }; surface_feedback->surface_destroy.notify = surface_feedback_handle_surface_destroy; wl_signal_add(&surface->events.destroy, &surface_feedback->surface_destroy); + + wl_list_insert(&manager->surface_feedbacks, &surface_feedback->link); } static void manager_handle_create_icc_creator(struct wl_client *client, @@ -925,6 +931,7 @@ struct wlr_color_manager_v1 *wlr_color_manager_v1_create(struct wl_display *disp manager->primaries_len = options->primaries_len; wl_list_init(&manager->outputs); + wl_list_init(&manager->surface_feedbacks); manager->global = wl_global_create(display, &wp_color_manager_v1_interface, version, manager, manager_bind); @@ -953,3 +960,19 @@ wlr_surface_get_image_description_v1_data(struct wlr_surface *surface) { } return &cm_surface->current.image_desc_data; } + +void wlr_color_manager_v1_set_surface_preferred_image_description( + struct wlr_color_manager_v1 *manager, struct wlr_surface *surface, + const struct wlr_image_description_v1_data *data) { + // TODO: de-duplicate identity + uint32_t identity = ++manager->last_image_desc_identity; + + struct wlr_color_management_surface_feedback_v1 *surface_feedback; + wl_list_for_each(surface_feedback, &manager->surface_feedbacks, link) { + if (surface_feedback->surface == surface) { + surface_feedback->data = *data; + wp_color_management_surface_feedback_v1_send_preferred_changed( + surface_feedback->resource, identity); + } + } +} From 5f65b1194ca6400eeb6077c81fe0282a391fdc2d Mon Sep 17 00:00:00 2001 From: YaoBing Xiao Date: Mon, 31 Mar 2025 17:42:00 +0800 Subject: [PATCH 323/519] ext_image_capture_source_v1: remove unused wlr_renderer include --- types/ext_image_capture_source_v1/output.c | 1 - 1 file changed, 1 deletion(-) diff --git a/types/ext_image_capture_source_v1/output.c b/types/ext_image_capture_source_v1/output.c index 1bb0dcc9e..e5ef78f93 100644 --- a/types/ext_image_capture_source_v1/output.c +++ b/types/ext_image_capture_source_v1/output.c @@ -7,7 +7,6 @@ #include #include #include -#include "render/wlr_renderer.h" #include "ext-image-capture-source-v1-protocol.h" #define OUTPUT_IMAGE_SOURCE_MANAGER_V1_VERSION 1 From 582f487b223ef35049f53e6a4f52aa2d7b122969 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Mon, 31 Mar 2025 14:27:54 +0300 Subject: [PATCH 324/519] meson: add a missing src file --- types/meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/types/meson.build b/types/meson.build index 1995661e1..831698eb9 100644 --- a/types/meson.build +++ b/types/meson.build @@ -99,6 +99,7 @@ wlr_files += files( 'wlr_xdg_foreign_v2.c', 'wlr_xdg_foreign_registry.c', 'wlr_xdg_output_v1.c', + 'wlr_xdg_system_bell_v1.c', 'wlr_xdg_toplevel_icon_v1.c', ) From 84fc6aaf5a869da385dc8cf26cc31f4e43f5098c Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Tue, 1 Apr 2025 15:39:02 +0300 Subject: [PATCH 325/519] Drop region.h This header has been deprecated a while ago. --- include/types/wlr_region.h | 2 -- include/wlr/types/wlr_region.h | 20 -------------------- 2 files changed, 22 deletions(-) delete mode 100644 include/wlr/types/wlr_region.h diff --git a/include/types/wlr_region.h b/include/types/wlr_region.h index b00f8802d..485632555 100644 --- a/include/types/wlr_region.h +++ b/include/types/wlr_region.h @@ -1,8 +1,6 @@ #ifndef TYPES_WLR_REGION_H #define TYPES_WLR_REGION_H -#include - struct wl_client; /* diff --git a/include/wlr/types/wlr_region.h b/include/wlr/types/wlr_region.h deleted file mode 100644 index 483ac9687..000000000 --- a/include/wlr/types/wlr_region.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * This is a deprecated interface of wlroots. It will be removed in a future - * version. wlr/types/wlr_compositor.h should be used instead. - */ - -#ifndef WLR_TYPES_WLR_REGION_H -#define WLR_TYPES_WLR_REGION_H - -#include - -struct wl_resource; - -/** - * Obtain a Pixman region from a wl_region resource. - * - * To allow clients to create wl_region objects, call wlr_compositor_create(). - */ -const pixman_region32_t *wlr_region_from_resource(struct wl_resource *resource); - -#endif From 9c51424f8d3436f28baf1e16381856b6f06582af Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Thu, 13 Mar 2025 12:03:02 +0300 Subject: [PATCH 326/519] ext-data-control: add missing listener list assertion --- types/wlr_ext_data_control_v1.c | 1 + 1 file changed, 1 insertion(+) diff --git a/types/wlr_ext_data_control_v1.c b/types/wlr_ext_data_control_v1.c index 80869f1c5..0c487e674 100644 --- a/types/wlr_ext_data_control_v1.c +++ b/types/wlr_ext_data_control_v1.c @@ -669,6 +669,7 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { wl_signal_emit_mutable(&manager->events.destroy, manager); assert(wl_list_empty(&manager->events.destroy.listener_list)); + assert(wl_list_empty(&manager->events.new_device.listener_list)); wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); From 709fc8fd8eb1561afda8c7844fe0d1221b5b30fa Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 6 Apr 2025 12:06:01 +0200 Subject: [PATCH 327/519] ext-data-control-v1: fix types in wl_list/wl_signal comments These refer to the wlr protocol types. --- include/wlr/types/wlr_ext_data_control_v1.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/wlr/types/wlr_ext_data_control_v1.h b/include/wlr/types/wlr_ext_data_control_v1.h index de5ad8d52..e49dfb3f5 100644 --- a/include/wlr/types/wlr_ext_data_control_v1.h +++ b/include/wlr/types/wlr_ext_data_control_v1.h @@ -14,11 +14,11 @@ struct wlr_ext_data_control_manager_v1 { struct wl_global *global; - struct wl_list devices; // wlr_data_control_device_v1.link + struct wl_list devices; // wlr_ext_data_control_device_v1.link struct { struct wl_signal destroy; - struct wl_signal new_device; // wlr_data_control_device_v1 + struct wl_signal new_device; // wlr_ext_data_control_device_v1 } events; struct wl_listener display_destroy; @@ -27,7 +27,7 @@ struct wlr_ext_data_control_manager_v1 { struct wlr_ext_data_control_device_v1 { struct wl_resource *resource; struct wlr_ext_data_control_manager_v1 *manager; - struct wl_list link; // wlr_data_control_manager_v1.devices + struct wl_list link; // wlr_ext_data_control_manager_v1.devices struct wlr_seat *seat; struct wl_resource *selection_offer_resource; // current selection offer From 5563d23b81d3149bf7cd0c3000536c08b9a9cd26 Mon Sep 17 00:00:00 2001 From: David Turner Date: Mon, 7 Apr 2025 13:25:42 +0100 Subject: [PATCH 328/519] single-pixel-buffer: Add try_from_buffer() function Add wlr_single_pixel_buffer_v1_try_from_buffer() and move `struct wlr_single_pixel_buffer_v1` to wlr_buffer.h. This allows other code to find out if a wlr_buffer is a single-pixel buffer and, if so, find out what color it is. --- include/wlr/types/wlr_buffer.h | 25 +++++++++++++++++++++++++ types/wlr_single_pixel_buffer_v1.c | 20 +++++++++++--------- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/include/wlr/types/wlr_buffer.h b/include/wlr/types/wlr_buffer.h index 19a8b73b6..8fd38905e 100644 --- a/include/wlr/types/wlr_buffer.h +++ b/include/wlr/types/wlr_buffer.h @@ -181,4 +181,29 @@ struct wlr_client_buffer { */ struct wlr_client_buffer *wlr_client_buffer_get(struct wlr_buffer *buffer); +/** + * A single-pixel buffer. Used by clients to draw solid-color rectangles. + */ +struct wlr_single_pixel_buffer_v1 { + struct wlr_buffer base; + + // Full-scale for each component is UINT32_MAX + uint32_t r, g, b, a; + + struct { + struct wl_resource *resource; + struct wl_listener release; + + // Packed little-endian DRM_FORMAT_ARGB8888. Used for data_ptr_access + uint8_t argb8888[4]; + } WLR_PRIVATE; +}; + +/** + * If the wlr_buffer is a wlr_single_pixel_buffer_v1 then unwrap it. + * Otherwise, returns NULL. + */ +struct wlr_single_pixel_buffer_v1 *wlr_single_pixel_buffer_v1_try_from_buffer( + struct wlr_buffer *buffer); + #endif diff --git a/types/wlr_single_pixel_buffer_v1.c b/types/wlr_single_pixel_buffer_v1.c index 949588ce4..9b82717fa 100644 --- a/types/wlr_single_pixel_buffer_v1.c +++ b/types/wlr_single_pixel_buffer_v1.c @@ -8,15 +8,6 @@ #define SINGLE_PIXEL_MANAGER_VERSION 1 -struct wlr_single_pixel_buffer_v1 { - struct wlr_buffer base; - struct wl_resource *resource; - uint32_t r, g, b, a; - uint8_t argb8888[4]; // packed little-endian DRM_FORMAT_ARGB8888 - - struct wl_listener release; -}; - static void destroy_resource(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); @@ -180,3 +171,14 @@ struct wlr_single_pixel_buffer_manager_v1 *wlr_single_pixel_buffer_manager_v1_cr return manager; } + +struct wlr_single_pixel_buffer_v1 *wlr_single_pixel_buffer_v1_try_from_buffer( + struct wlr_buffer *buffer) { + + if (buffer->impl != &buffer_impl) { + return NULL; + } + + return wl_container_of(buffer, + (struct wlr_single_pixel_buffer_v1 *)NULL, base); +} From 792bee96579cf6d9aa16d68d3f9a33a947fdafcd Mon Sep 17 00:00:00 2001 From: David Turner Date: Mon, 7 Apr 2025 13:28:51 +0100 Subject: [PATCH 329/519] scene: Optimize rendering of single-pixel buffers The single-pixel buffer protocol is used to allow wayland clients to easily draw solid-color rectangles by presenting a 1x1-pixel buffer and scaling it to the desired size. This patch improves how these buffers are then handled in the scene-tree renderer. We already ignore opaque black rectangles at the very bottom (and anything under them) because we assume we'll be rendering on a black background. This patch detects black opaque single-pixel buffers and handles them in the same way as black opaque rectangles. It also renders single-pixel buffers as rectangles rather than buffers because this is probably more efficient in the underlying renderer. In wlr_scene_surface we cache whether the attached buffer is a single-pixel buffer. This is done because the wlr_single_pixel_buffer_v1 will be destroyed after texture upload, after which it becomes much more annoying to check if the buffer is a single-pixel buffer. --- include/wlr/types/wlr_scene.h | 6 ++++++ types/scene/surface.c | 19 +++++++++++++++++ types/scene/wlr_scene.c | 40 +++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 0e3e7750a..b18f3e84e 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -132,6 +132,12 @@ struct wlr_scene_surface { struct wl_listener frame_done; struct wl_listener surface_destroy; struct wl_listener surface_commit; + + // True if the underlying buffer is a wlr_single_pixel_buffer_v1 + bool is_single_pixel_buffer; + // If is_single_pixel_buffer is set, contains the color of the buffer + // as {R, G, B, A} where the max value of each component is UINT32_MAX + uint32_t single_pixel_buffer_color[4]; } WLR_PRIVATE; }; diff --git a/types/scene/surface.c b/types/scene/surface.c index 1f954373d..079b82946 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include "types/wlr_scene.h" @@ -164,6 +165,24 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { scene_buffer_unmark_client_buffer(scene_buffer); if (surface->buffer) { + // If this is a buffer change, check if it's a single pixel buffer. + // Cache that so we can still apply rendering optimisations even when + // the original buffer has been freed after texture upload. + if (&surface->buffer->base != scene_buffer->buffer) { + scene_surface->is_single_pixel_buffer = false; + if (surface->buffer->source != NULL) { + struct wlr_single_pixel_buffer_v1 *single_pixel_buffer = + wlr_single_pixel_buffer_v1_try_from_buffer(surface->buffer->source); + if (single_pixel_buffer != NULL) { + scene_surface->is_single_pixel_buffer = true; + scene_surface->single_pixel_buffer_color[0] = single_pixel_buffer->r; + scene_surface->single_pixel_buffer_color[1] = single_pixel_buffer->g; + scene_surface->single_pixel_buffer_color[2] = single_pixel_buffer->b; + scene_surface->single_pixel_buffer_color[3] = single_pixel_buffer->a; + } + } + } + client_buffer_mark_next_can_damage(surface->buffer); struct wlr_linux_drm_syncobj_surface_v1_state *syncobj_surface_state = diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index d64ee4ada..481b2404f 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1386,6 +1386,24 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren case WLR_SCENE_NODE_BUFFER:; struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); + struct wlr_scene_surface *scene_surface = + wlr_scene_surface_try_from_buffer(scene_buffer); + if (scene_surface != NULL && scene_surface->is_single_pixel_buffer) { + // Render the buffer as a rect, this is likely to be more efficient + wlr_render_pass_add_rect(data->render_pass, &(struct wlr_render_rect_options){ + .box = dst_box, + .color = { + .r = (float)scene_surface->single_pixel_buffer_color[0] / (float)UINT32_MAX, + .g = (float)scene_surface->single_pixel_buffer_color[1] / (float)UINT32_MAX, + .b = (float)scene_surface->single_pixel_buffer_color[2] / (float)UINT32_MAX, + .a = (float)scene_surface->single_pixel_buffer_color[3] / + (float)UINT32_MAX * scene_buffer->opacity, + }, + .clip = &render_region, + }); + break; + } + struct wlr_texture *texture = scene_buffer_get_texture(scene_buffer, data->output->output->renderer); if (texture == NULL) { @@ -1736,6 +1754,18 @@ struct render_list_constructor_data { bool fractional_scale; }; +static bool scene_buffer_is_black_opaque(struct wlr_scene_buffer *scene_buffer) { + struct wlr_scene_surface *scene_surface = + wlr_scene_surface_try_from_buffer(scene_buffer); + return scene_surface != NULL && + scene_surface->is_single_pixel_buffer && + scene_surface->single_pixel_buffer_color[0] == 0 && + scene_surface->single_pixel_buffer_color[1] == 0 && + scene_surface->single_pixel_buffer_color[2] == 0 && + scene_surface->single_pixel_buffer_color[3] == UINT32_MAX && + scene_buffer->opacity == 1.0; +} + static bool construct_render_list_iterator(struct wlr_scene_node *node, int lx, int ly, void *_data) { struct render_list_constructor_data *data = _data; @@ -1758,6 +1788,16 @@ static bool construct_render_list_iterator(struct wlr_scene_node *node, } } + // Apply the same special-case to black opaque single-pixel buffers + if (node->type == WLR_SCENE_NODE_BUFFER && data->calculate_visibility && + (!data->fractional_scale || data->render_list->size == 0)) { + struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); + + if (scene_buffer_is_black_opaque(scene_buffer)) { + return false; + } + } + pixman_region32_t intersection; pixman_region32_init(&intersection); pixman_region32_intersect_rect(&intersection, &node->visible, From c450991c4b1cc47d434360f2c45c97cde047d100 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sun, 30 Mar 2025 19:06:42 +0200 Subject: [PATCH 330/519] wlr_scene: Debounce dmabuf feedback on scanout Direct scanout can be enabled and disabled on a frame-by-frame basis, and so we could end up sending different feedback to a surface on every other frame. Reacting to new feedback is expensive, as the client may need to reallocate their swapchain. Debounce the state change a number of frames, for now set to 30, to avoid immediate reaction to scanout (or composition) that only lasts a few frames. A timer could be used instead, but it did not seem worth the complexity. What just want to know that the state has been stable across a reasonable number of samples, and a counter seems sufficient for that. --- include/wlr/types/wlr_scene.h | 8 ++++ types/scene/wlr_scene.c | 76 +++++++++++++++++++++++++---------- 2 files changed, 63 insertions(+), 21 deletions(-) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index b18f3e84e..5010a8a44 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -227,6 +227,14 @@ struct wlr_scene_output { pixman_region32_t pending_commit_damage; uint8_t index; + + /** + * When scanout is applicable, we increment this every time a frame is rendered until + * DMABUF_FEEDBACK_DEBOUNCE_FRAMES is hit to debounce the scanout dmabuf feedback. Likewise, + * when scanout is no longer applicable, we decrement this until zero is hit to debounce + * composition dmabuf feedback. + */ + uint8_t dmabuf_feedback_debounce; bool prev_scanout; bool gamma_lut_changed; diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 481b2404f..9afa78701 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -26,7 +26,8 @@ #include #endif -#define HIGHLIGHT_DAMAGE_FADEOUT_TIME 250 +#define DMABUF_FEEDBACK_DEBOUNCE_FRAMES 30 +#define HIGHLIGHT_DAMAGE_FADEOUT_TIME 250 struct wlr_scene_tree *wlr_scene_tree_from_node(struct wlr_scene_node *node) { assert(node->type == WLR_SCENE_NODE_TREE); @@ -1330,7 +1331,6 @@ struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node, struct render_list_entry { struct wlr_scene_node *node; - bool sent_dmabuf_feedback; bool highlight_transparent_region; int x, y; }; @@ -1856,33 +1856,47 @@ static void scene_buffer_send_dmabuf_feedback(const struct wlr_scene *scene, wlr_linux_dmabuf_feedback_v1_finish(&feedback); } -static bool scene_entry_try_direct_scanout(struct render_list_entry *entry, - struct wlr_output_state *state, const struct render_data *data) { +enum scene_direct_scanout_result { + // This scene node is not a candidate for scanout + SCANOUT_INELIGIBLE, + + // This scene node is a candidate for scanout, but is currently + // incompatible due to e.g. buffer mismatch, and if possible we'd like to + // resolve this incompatibility. + SCANOUT_CANDIDATE, + + // Scanout is successful. + SCANOUT_SUCCESS, +}; + +static enum scene_direct_scanout_result scene_entry_try_direct_scanout( + struct render_list_entry *entry, struct wlr_output_state *state, + const struct render_data *data) { struct wlr_scene_output *scene_output = data->output; struct wlr_scene_node *node = entry->node; if (!scene_output->scene->direct_scanout) { - return false; + return SCANOUT_INELIGIBLE; } if (node->type != WLR_SCENE_NODE_BUFFER) { - return false; + return SCANOUT_INELIGIBLE; } if (state->committed & (WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_ENABLED | WLR_OUTPUT_STATE_RENDER_FORMAT)) { // Legacy DRM will explode if we try to modeset with a direct scanout buffer - return false; + return SCANOUT_INELIGIBLE; } if (!wlr_output_is_direct_scanout_allowed(scene_output->output)) { - return false; + return SCANOUT_INELIGIBLE; } struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); if (buffer->buffer == NULL) { - return false; + return SCANOUT_INELIGIBLE; } // The native size of the buffer after any transform is applied @@ -1895,23 +1909,26 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry, }; if (buffer->transform != data->transform) { - return false; + return SCANOUT_INELIGIBLE; } - if (buffer->primary_output == scene_output) { + // We want to ensure optimal buffer selection, but as direct-scanout can be enabled and disabled + // on a frame-by-frame basis, we wait for a few frames to send the new format recommendations. + // Maybe we should only send feedback in this case if tests fail. + if (scene_output->dmabuf_feedback_debounce >= DMABUF_FEEDBACK_DEBOUNCE_FRAMES + && buffer->primary_output == scene_output) { struct wlr_linux_dmabuf_feedback_v1_init_options options = { .main_renderer = scene_output->output->renderer, .scanout_primary_output = scene_output->output, }; scene_buffer_send_dmabuf_feedback(scene_output->scene, buffer, &options); - entry->sent_dmabuf_feedback = true; } struct wlr_output_state pending; wlr_output_state_init(&pending); if (!wlr_output_state_copy(&pending, state)) { - return false; + return SCANOUT_CANDIDATE; } if (!wlr_fbox_empty(&buffer->src_box) && @@ -1936,10 +1953,9 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry, if (buffer->wait_timeline != NULL) { wlr_output_state_set_wait_timeline(&pending, buffer->wait_timeline, buffer->wait_point); } - if (!wlr_output_test_state(scene_output->output, &pending)) { wlr_output_state_finish(&pending); - return false; + return SCANOUT_CANDIDATE; } wlr_output_state_copy(state, &pending); @@ -1950,7 +1966,7 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry, .direct_scanout = true, }; wl_signal_emit_mutable(&buffer->events.output_sample, &sample_event); - return true; + return SCANOUT_SUCCESS; } bool wlr_scene_output_needs_frame(struct wlr_scene_output *scene_output) { @@ -2133,17 +2149,31 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, // - There is only one entry in the render list // - There are no color transforms that need to be applied // - Damage highlight debugging is not enabled - bool scanout = options->color_transform == NULL && - list_len == 1 && debug_damage != WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT && - scene_entry_try_direct_scanout(&list_data[0], state, &render_data); + enum scene_direct_scanout_result scanout = SCANOUT_INELIGIBLE; + if (options->color_transform == NULL && list_len == 1 + && debug_damage != WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) { + scanout = scene_entry_try_direct_scanout(&list_data[0], state, &render_data); + } + if (scanout == SCANOUT_INELIGIBLE) { + if (scene_output->dmabuf_feedback_debounce > 0) { + // We cannot scan out, so count down towards sending composition dmabuf feedback + scene_output->dmabuf_feedback_debounce--; + } + } else if (scene_output->dmabuf_feedback_debounce < DMABUF_FEEDBACK_DEBOUNCE_FRAMES) { + // We either want to scan out or successfully scanned out, so count up towards sending + // scanout dmabuf feedback + scene_output->dmabuf_feedback_debounce++; + } + + bool scanout_success = scanout == SCANOUT_SUCCESS; if (scene_output->prev_scanout != scanout) { scene_output->prev_scanout = scanout; wlr_log(WLR_DEBUG, "Direct scan-out %s", scanout ? "enabled" : "disabled"); } - if (scanout) { + if (scanout_success) { scene_output_state_attempt_gamma(scene_output, state); if (timer) { @@ -2249,7 +2279,11 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, if (entry->node->type == WLR_SCENE_NODE_BUFFER) { struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(entry->node); - if (buffer->primary_output == scene_output && !entry->sent_dmabuf_feedback) { + // Direct scanout counts up to DMABUF_FEEDBACK_DEBOUNCE_FRAMES before sending new dmabuf + // feedback, and on composition we wait until it hits zero again. If we knew that an + // entry could never be a scanout candidate, we could send feedback to it + // unconditionally without debounce, but for now it is all or nothing + if (scene_output->dmabuf_feedback_debounce == 0 && buffer->primary_output == scene_output) { struct wlr_linux_dmabuf_feedback_v1_init_options options = { .main_renderer = output->renderer, .scanout_primary_output = NULL, From 0bf0c55ad2db7d457cf4c921c3713d97077b5e91 Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 10 Apr 2025 14:36:08 +0100 Subject: [PATCH 331/519] scene: Move single-pixel buffer things to scene_buffer Move single-pixel buffer status cache from wlr_scene_surface to wlr_scene_buffer, it makes more sense there and means the optimisations will still work if wlr_scene_buffer is used without wlr_scene_surface. --- include/wlr/types/wlr_scene.h | 12 ++++----- types/scene/surface.c | 18 -------------- types/scene/wlr_scene.c | 47 ++++++++++++++++++++++++----------- 3 files changed, 38 insertions(+), 39 deletions(-) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 5010a8a44..2292f1fc8 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -132,12 +132,6 @@ struct wlr_scene_surface { struct wl_listener frame_done; struct wl_listener surface_destroy; struct wl_listener surface_commit; - - // True if the underlying buffer is a wlr_single_pixel_buffer_v1 - bool is_single_pixel_buffer; - // If is_single_pixel_buffer is set, contains the color of the buffer - // as {R, G, B, A} where the max value of each component is UINT32_MAX - uint32_t single_pixel_buffer_color[4]; } WLR_PRIVATE; }; @@ -205,6 +199,12 @@ struct wlr_scene_buffer { struct wl_listener buffer_release; struct wl_listener renderer_destroy; + + // True if the underlying buffer is a wlr_single_pixel_buffer_v1 + bool is_single_pixel_buffer; + // If is_single_pixel_buffer is set, contains the color of the buffer + // as {R, G, B, A} where the max value of each component is UINT32_MAX + uint32_t single_pixel_buffer_color[4]; } WLR_PRIVATE; }; diff --git a/types/scene/surface.c b/types/scene/surface.c index 079b82946..e5b3b0f7d 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -165,24 +165,6 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { scene_buffer_unmark_client_buffer(scene_buffer); if (surface->buffer) { - // If this is a buffer change, check if it's a single pixel buffer. - // Cache that so we can still apply rendering optimisations even when - // the original buffer has been freed after texture upload. - if (&surface->buffer->base != scene_buffer->buffer) { - scene_surface->is_single_pixel_buffer = false; - if (surface->buffer->source != NULL) { - struct wlr_single_pixel_buffer_v1 *single_pixel_buffer = - wlr_single_pixel_buffer_v1_try_from_buffer(surface->buffer->source); - if (single_pixel_buffer != NULL) { - scene_surface->is_single_pixel_buffer = true; - scene_surface->single_pixel_buffer_color[0] = single_pixel_buffer->r; - scene_surface->single_pixel_buffer_color[1] = single_pixel_buffer->g; - scene_surface->single_pixel_buffer_color[2] = single_pixel_buffer->b; - scene_surface->single_pixel_buffer_color[3] = single_pixel_buffer->a; - } - } - } - client_buffer_mark_next_can_damage(surface->buffer); struct wlr_linux_drm_syncobj_surface_v1_state *syncobj_surface_state = diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 9afa78701..c7ce8176f 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -875,6 +875,28 @@ void wlr_scene_buffer_set_buffer_with_options(struct wlr_scene_buffer *scene_buf scene_buffer->buffer_height != buffer->height; } + // If this is a buffer change, check if it's a single pixel buffer. + // Cache that so we can still apply rendering optimisations even when + // the original buffer has been freed after texture upload. + if (buffer != scene_buffer->buffer) { + scene_buffer->is_single_pixel_buffer = false; + struct wlr_client_buffer *client_buffer = NULL; + if (buffer != NULL) { + client_buffer = wlr_client_buffer_get(buffer); + } + if (client_buffer != NULL && client_buffer->source != NULL) { + struct wlr_single_pixel_buffer_v1 *single_pixel_buffer = + wlr_single_pixel_buffer_v1_try_from_buffer(client_buffer->source); + if (single_pixel_buffer != NULL) { + scene_buffer->is_single_pixel_buffer = true; + scene_buffer->single_pixel_buffer_color[0] = single_pixel_buffer->r; + scene_buffer->single_pixel_buffer_color[1] = single_pixel_buffer->g; + scene_buffer->single_pixel_buffer_color[2] = single_pixel_buffer->b; + scene_buffer->single_pixel_buffer_color[3] = single_pixel_buffer->a; + } + } + } + scene_buffer_set_buffer(scene_buffer, buffer); scene_buffer_set_texture(scene_buffer, NULL); scene_buffer_set_wait_timeline(scene_buffer, @@ -1386,17 +1408,15 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren case WLR_SCENE_NODE_BUFFER:; struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); - struct wlr_scene_surface *scene_surface = - wlr_scene_surface_try_from_buffer(scene_buffer); - if (scene_surface != NULL && scene_surface->is_single_pixel_buffer) { + if (scene_buffer->is_single_pixel_buffer) { // Render the buffer as a rect, this is likely to be more efficient wlr_render_pass_add_rect(data->render_pass, &(struct wlr_render_rect_options){ .box = dst_box, .color = { - .r = (float)scene_surface->single_pixel_buffer_color[0] / (float)UINT32_MAX, - .g = (float)scene_surface->single_pixel_buffer_color[1] / (float)UINT32_MAX, - .b = (float)scene_surface->single_pixel_buffer_color[2] / (float)UINT32_MAX, - .a = (float)scene_surface->single_pixel_buffer_color[3] / + .r = (float)scene_buffer->single_pixel_buffer_color[0] / (float)UINT32_MAX, + .g = (float)scene_buffer->single_pixel_buffer_color[1] / (float)UINT32_MAX, + .b = (float)scene_buffer->single_pixel_buffer_color[2] / (float)UINT32_MAX, + .a = (float)scene_buffer->single_pixel_buffer_color[3] / (float)UINT32_MAX * scene_buffer->opacity, }, .clip = &render_region, @@ -1755,14 +1775,11 @@ struct render_list_constructor_data { }; static bool scene_buffer_is_black_opaque(struct wlr_scene_buffer *scene_buffer) { - struct wlr_scene_surface *scene_surface = - wlr_scene_surface_try_from_buffer(scene_buffer); - return scene_surface != NULL && - scene_surface->is_single_pixel_buffer && - scene_surface->single_pixel_buffer_color[0] == 0 && - scene_surface->single_pixel_buffer_color[1] == 0 && - scene_surface->single_pixel_buffer_color[2] == 0 && - scene_surface->single_pixel_buffer_color[3] == UINT32_MAX && + return scene_buffer->is_single_pixel_buffer && + scene_buffer->single_pixel_buffer_color[0] == 0 && + scene_buffer->single_pixel_buffer_color[1] == 0 && + scene_buffer->single_pixel_buffer_color[2] == 0 && + scene_buffer->single_pixel_buffer_color[3] == UINT32_MAX && scene_buffer->opacity == 1.0; } From 9c9bf2efeee576977ac1e44e9ef91bd1c7f7b1dd Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Thu, 10 Apr 2025 14:45:24 +0300 Subject: [PATCH 332/519] xdg-system-bell-v1: set proper global/resource user data See https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3963. --- types/wlr_xdg_system_bell_v1.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/types/wlr_xdg_system_bell_v1.c b/types/wlr_xdg_system_bell_v1.c index 3cabf6559..5ab9b5a19 100644 --- a/types/wlr_xdg_system_bell_v1.c +++ b/types/wlr_xdg_system_bell_v1.c @@ -40,13 +40,15 @@ static const struct xdg_system_bell_v1_interface bell_impl = { static void bell_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { + struct wlr_xdg_system_bell_v1 *bell = data; + struct wl_resource *resource = wl_resource_create(client, &xdg_system_bell_v1_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } - wl_resource_set_implementation(resource, &bell_impl, NULL, NULL); + wl_resource_set_implementation(resource, &bell_impl, bell, NULL); } static void handle_display_destroy(struct wl_listener *listener, void *data) { @@ -71,7 +73,7 @@ struct wlr_xdg_system_bell_v1 *wlr_xdg_system_bell_v1_create(struct wl_display * } bell->global = wl_global_create(display, &xdg_system_bell_v1_interface, - version, NULL, bell_bind); + version, bell, bell_bind); if (bell->global == NULL) { free(bell); return NULL; From 1e7baefe96606bc7beb7d60f6c69eb209ece832b Mon Sep 17 00:00:00 2001 From: Olivier Tilloy Date: Thu, 3 Apr 2025 14:15:58 +0200 Subject: [PATCH 333/519] seat/keyboard: optimize wlr_seat_set_keyboard to send the keymap only if it has changed --- types/seat/wlr_seat_keyboard.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/types/seat/wlr_seat_keyboard.c b/types/seat/wlr_seat_keyboard.c index 56f83b7f4..3227035e2 100644 --- a/types/seat/wlr_seat_keyboard.c +++ b/types/seat/wlr_seat_keyboard.c @@ -122,6 +122,11 @@ void wlr_seat_set_keyboard(struct wlr_seat *seat, return; } + // send the keymap only if it has changed + bool needs_keymap_update = + !seat->keyboard_state.keyboard || !keyboard || + seat->keyboard_state.keyboard->keymap != keyboard->keymap; + if (seat->keyboard_state.keyboard) { wl_list_remove(&seat->keyboard_state.keyboard_destroy.link); wl_list_remove(&seat->keyboard_state.keyboard_keymap.link); @@ -145,7 +150,9 @@ void wlr_seat_set_keyboard(struct wlr_seat *seat, struct wlr_seat_client *client; wl_list_for_each(client, &seat->clients, link) { - seat_client_send_keymap(client, keyboard); + if (needs_keymap_update) { + seat_client_send_keymap(client, keyboard); + } seat_client_send_repeat_info(client, keyboard); } From 7161bcfabc7ef85ad5865ac8fe29ab964507e596 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 12 Apr 2025 22:50:55 +0200 Subject: [PATCH 334/519] build: bump version to 0.19.0-rc1 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index fb1ea12ba..a40cb6bdf 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'wlroots', 'c', - version: '0.19.0-dev', + version: '0.19.0-rc1', license: 'MIT', meson_version: '>=1.3', default_options: [ From 5dc73937ff96e17a7b4e136a80dfec087604878d Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Sun, 13 Apr 2025 13:25:14 +0300 Subject: [PATCH 335/519] xdg-surface: ensure that the effective geom is not empty Fixes: 5c98d1a04a1439bf40c6e516086cfaff2d67f135 --- types/xdg_shell/wlr_xdg_surface.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/types/xdg_shell/wlr_xdg_surface.c b/types/xdg_shell/wlr_xdg_surface.c index 5652fc26c..3d3299f93 100644 --- a/types/xdg_shell/wlr_xdg_surface.c +++ b/types/xdg_shell/wlr_xdg_surface.c @@ -262,6 +262,10 @@ static void update_geometry(struct wlr_xdg_surface *surface) { wlr_surface_get_extents(surface->surface, &surface->geometry); wlr_box_intersection(&surface->geometry, &surface->current.geometry, &surface->geometry); + if (wlr_box_empty(&surface->geometry)) { + wl_resource_post_error(surface->resource, XDG_SURFACE_ERROR_INVALID_SIZE, + "the effective window geometry is empty"); + } } } else { wlr_surface_get_extents(surface->surface, &surface->geometry); @@ -332,10 +336,10 @@ static void xdg_surface_role_commit(struct wlr_surface *wlr_surface) { break; } - if (wlr_surface->mapped) { - update_geometry(surface); - } else if (wlr_surface_has_buffer(wlr_surface)) { + if (!wlr_surface->mapped && wlr_surface_has_buffer(wlr_surface)) { wlr_surface_map(wlr_surface); + } else { + update_geometry(surface); } } From d7527778bb0545e47f51777637ca627077a2dd71 Mon Sep 17 00:00:00 2001 From: Yixue Wang Date: Mon, 4 Nov 2024 16:53:02 +0800 Subject: [PATCH 336/519] wlr_client_buffer: add get_shm implementation --- types/buffer/client.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/types/buffer/client.c b/types/buffer/client.c index 6aa55386d..5bc670334 100644 --- a/types/buffer/client.c +++ b/types/buffer/client.c @@ -44,9 +44,44 @@ static bool client_buffer_get_dmabuf(struct wlr_buffer *buffer, return wlr_buffer_get_dmabuf(client_buffer->source, attribs); } +static bool client_buffer_get_shm(struct wlr_buffer *buffer, + struct wlr_shm_attributes *attribs) { + struct wlr_client_buffer *client_buffer = client_buffer_from_buffer(buffer); + + if (client_buffer->source == NULL) { + return false; + } + + return wlr_buffer_get_shm(client_buffer->source, attribs); +} + +static bool client_buffer_begin_data_ptr_access(struct wlr_buffer *buffer, uint32_t flags, + void **data, uint32_t *format, size_t *stride) { + struct wlr_client_buffer *client_buffer = client_buffer_from_buffer(buffer); + + if (client_buffer->source == NULL) { + return false; + } + + return wlr_buffer_begin_data_ptr_access(client_buffer->source, flags, data, format, stride); +} + +static void client_buffer_end_data_ptr_access(struct wlr_buffer *buffer) { + struct wlr_client_buffer *client_buffer = client_buffer_from_buffer(buffer); + + if (client_buffer->source == NULL) { + return; + } + + wlr_buffer_end_data_ptr_access(client_buffer->source); +} + static const struct wlr_buffer_impl client_buffer_impl = { .destroy = client_buffer_destroy, .get_dmabuf = client_buffer_get_dmabuf, + .get_shm = client_buffer_get_shm, + .begin_data_ptr_access = client_buffer_begin_data_ptr_access, + .end_data_ptr_access = client_buffer_end_data_ptr_access, }; static void client_buffer_handle_source_destroy(struct wl_listener *listener, From 867960d6f40cb9d5901746f653afb8261d4ea4bf Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 14 Apr 2025 12:42:13 +0200 Subject: [PATCH 337/519] compositor: Clean up surface current_outputs last During surface resource cleanup, several signals will be emitted. If any of these end up calling wlr_surface_send_enter, a new output could be added to the current_outputs list. This would result in a leaked surface_output and a dangling wlr_surface pointer. Clean up current_outputs last. References: https://github.com/swaywm/sway/issues/8650 --- types/wlr_compositor.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/types/wlr_compositor.c b/types/wlr_compositor.c index b956d8dbc..d2c881e2a 100644 --- a/types/wlr_compositor.c +++ b/types/wlr_compositor.c @@ -725,12 +725,6 @@ static void surface_destroy_role_object(struct wlr_surface *surface); static void surface_handle_resource_destroy(struct wl_resource *resource) { struct wlr_surface *surface = wlr_surface_from_resource(resource); - struct wlr_surface_output *surface_output, *surface_output_tmp; - wl_list_for_each_safe(surface_output, surface_output_tmp, - &surface->current_outputs, link) { - surface_output_destroy(surface_output); - } - surface_destroy_role_object(surface); wl_signal_emit_mutable(&surface->events.destroy, surface); @@ -762,6 +756,13 @@ static void surface_handle_resource_destroy(struct wl_resource *resource) { if (surface->buffer != NULL) { wlr_buffer_unlock(&surface->buffer->base); } + + struct wlr_surface_output *surface_output, *surface_output_tmp; + wl_list_for_each_safe(surface_output, surface_output_tmp, + &surface->current_outputs, link) { + surface_output_destroy(surface_output); + } + free(surface); } From 4277d8cfdc2a9e0e72395c99980d6227da7e42c2 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 14 Apr 2025 16:52:00 +0200 Subject: [PATCH 338/519] scene: Fix scanout log to respect SCANOUT_SUCCESS scene_entry_try_direct_scanout returns a tristate value, but the log message was not updated to account for this. Compare whether or not the state is specifically SCANOUT_SUCCESS for logging purposes. Fixes: c450991c4b1cc47d434360f2c45c97cde047d100 --- types/scene/wlr_scene.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index c7ce8176f..e823d62ae 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -2166,13 +2166,13 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, // - There is only one entry in the render list // - There are no color transforms that need to be applied // - Damage highlight debugging is not enabled - enum scene_direct_scanout_result scanout = SCANOUT_INELIGIBLE; + enum scene_direct_scanout_result scanout_result = SCANOUT_INELIGIBLE; if (options->color_transform == NULL && list_len == 1 && debug_damage != WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) { - scanout = scene_entry_try_direct_scanout(&list_data[0], state, &render_data); + scanout_result = scene_entry_try_direct_scanout(&list_data[0], state, &render_data); } - if (scanout == SCANOUT_INELIGIBLE) { + if (scanout_result == SCANOUT_INELIGIBLE) { if (scene_output->dmabuf_feedback_debounce > 0) { // We cannot scan out, so count down towards sending composition dmabuf feedback scene_output->dmabuf_feedback_debounce--; @@ -2183,14 +2183,14 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, scene_output->dmabuf_feedback_debounce++; } - bool scanout_success = scanout == SCANOUT_SUCCESS; + bool scanout = scanout_result == SCANOUT_SUCCESS; if (scene_output->prev_scanout != scanout) { scene_output->prev_scanout = scanout; wlr_log(WLR_DEBUG, "Direct scan-out %s", scanout ? "enabled" : "disabled"); } - if (scanout_success) { + if (scanout) { scene_output_state_attempt_gamma(scene_output, state); if (timer) { From 5fd43add1cbd286ff13489048c4809b6ad915b2b Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Tue, 15 Apr 2025 20:13:29 +0300 Subject: [PATCH 339/519] Fix/cleanup includes a bit --- backend/backend.c | 2 -- backend/drm/atomic.c | 2 -- backend/drm/backend.c | 2 -- backend/drm/drm.c | 4 ---- backend/libinput/tablet_tool.c | 1 + include/backend/drm/fb.h | 4 ++++ include/backend/libinput.h | 2 -- include/render/gles2.h | 1 - include/types/wlr_region.h | 2 ++ include/wlr/types/wlr_ext_image_copy_capture_v1.h | 3 +++ include/wlr/types/wlr_input_method_v2.h | 1 - include/wlr/types/wlr_output_swapchain_manager.h | 1 + include/wlr/types/wlr_presentation_time.h | 1 - render/gles2/texture.c | 1 - render/wlr_renderer.c | 1 - types/wlr_drm_lease_v1.c | 1 - types/wlr_ext_foreign_toplevel_list_v1.c | 1 - types/wlr_input_method_v2.c | 2 +- types/wlr_tearing_control_v1.c | 1 - types/wlr_xdg_foreign_v2.c | 1 - xwayland/xwayland.c | 3 --- 21 files changed, 12 insertions(+), 25 deletions(-) diff --git a/backend/backend.c b/backend/backend.c index b8a9d71cc..a130d9045 100644 --- a/backend/backend.c +++ b/backend/backend.c @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -12,7 +11,6 @@ #include #include #include -#include "backend/multi.h" #include "types/wlr_output.h" #include "util/env.h" #include "util/time.h" diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index 91cafea60..037c3c23a 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -5,13 +5,11 @@ #include #include #include -#include #include #include "backend/drm/drm.h" #include "backend/drm/fb.h" #include "backend/drm/iface.h" #include "backend/drm/util.h" -#include "types/wlr_output.h" static char *atomic_commit_flags_str(uint32_t flags) { const char *const l[] = { diff --git a/backend/drm/backend.c b/backend/drm/backend.c index 9d9d5c229..5a545b192 100644 --- a/backend/drm/backend.c +++ b/backend/drm/backend.c @@ -1,10 +1,8 @@ #include -#include #include #include #include #include -#include #include #include #include diff --git a/backend/drm/drm.c b/backend/drm/drm.c index d07d98afc..e106ea8c1 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -1,7 +1,6 @@ #include #include #include -#include #include #include #include @@ -25,9 +24,6 @@ #include "backend/drm/fb.h" #include "backend/drm/iface.h" #include "backend/drm/util.h" -#include "render/pixel_format.h" -#include "render/drm_format_set.h" -#include "render/wlr_renderer.h" #include "types/wlr_output.h" #include "util/env.h" #include "config.h" diff --git a/backend/libinput/tablet_tool.c b/backend/libinput/tablet_tool.c index cc9e60968..eec697c49 100644 --- a/backend/libinput/tablet_tool.c +++ b/backend/libinput/tablet_tool.c @@ -6,6 +6,7 @@ #include #include #include "backend/libinput.h" +#include "config.h" struct tablet_tool { struct wlr_tablet_tool wlr_tool; diff --git a/include/backend/drm/fb.h b/include/backend/drm/fb.h index 91d0fffcc..e8d328e97 100644 --- a/include/backend/drm/fb.h +++ b/include/backend/drm/fb.h @@ -2,6 +2,10 @@ #define BACKEND_DRM_FB_H #include +#include +#include + +struct wlr_drm_format_set; struct wlr_drm_fb { struct wlr_buffer *wlr_buf; diff --git a/include/backend/libinput.h b/include/backend/libinput.h index cf685f0ef..874e9aa1f 100644 --- a/include/backend/libinput.h +++ b/include/backend/libinput.h @@ -12,8 +12,6 @@ #include #include -#include "config.h" - struct wlr_libinput_backend { struct wlr_backend backend; diff --git a/include/render/gles2.h b/include/render/gles2.h index 0f24ae38e..6b852dcb7 100644 --- a/include/render/gles2.h +++ b/include/render/gles2.h @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include diff --git a/include/types/wlr_region.h b/include/types/wlr_region.h index 485632555..a44a9f09a 100644 --- a/include/types/wlr_region.h +++ b/include/types/wlr_region.h @@ -1,6 +1,8 @@ #ifndef TYPES_WLR_REGION_H #define TYPES_WLR_REGION_H +#include + struct wl_client; /* diff --git a/include/wlr/types/wlr_ext_image_copy_capture_v1.h b/include/wlr/types/wlr_ext_image_copy_capture_v1.h index 897005710..51c4af8c4 100644 --- a/include/wlr/types/wlr_ext_image_copy_capture_v1.h +++ b/include/wlr/types/wlr_ext_image_copy_capture_v1.h @@ -9,10 +9,13 @@ #ifndef WLR_TYPES_WLR_EXT_IMAGE_COPY_CAPTURE_V1_H #define WLR_TYPES_WLR_EXT_IMAGE_COPY_CAPTURE_V1_H +#include #include #include #include "ext-image-copy-capture-v1-protocol.h" +struct wlr_renderer; + struct wlr_ext_image_copy_capture_manager_v1 { struct wl_global *global; diff --git a/include/wlr/types/wlr_input_method_v2.h b/include/wlr/types/wlr_input_method_v2.h index 02216ddad..77460b571 100644 --- a/include/wlr/types/wlr_input_method_v2.h +++ b/include/wlr/types/wlr_input_method_v2.h @@ -9,7 +9,6 @@ #ifndef WLR_TYPES_WLR_INPUT_METHOD_V2_H #define WLR_TYPES_WLR_INPUT_METHOD_V2_H #include -#include #include #include #include diff --git a/include/wlr/types/wlr_output_swapchain_manager.h b/include/wlr/types/wlr_output_swapchain_manager.h index 3ea9c49b9..26b3e5636 100644 --- a/include/wlr/types/wlr_output_swapchain_manager.h +++ b/include/wlr/types/wlr_output_swapchain_manager.h @@ -13,6 +13,7 @@ struct wlr_backend; struct wlr_backend_output_state; +struct wlr_output; /** * Helper to allocate swapchains for mode-setting. diff --git a/include/wlr/types/wlr_presentation_time.h b/include/wlr/types/wlr_presentation_time.h index 9becffeee..22d538ed7 100644 --- a/include/wlr/types/wlr_presentation_time.h +++ b/include/wlr/types/wlr_presentation_time.h @@ -11,7 +11,6 @@ #include #include -#include #include struct wlr_surface; diff --git a/render/gles2/texture.c b/render/gles2/texture.c index f9ad4cc56..9a967ebdb 100644 --- a/render/gles2/texture.c +++ b/render/gles2/texture.c @@ -13,7 +13,6 @@ #include "render/egl.h" #include "render/gles2.h" #include "render/pixel_format.h" -#include "types/wlr_buffer.h" static const struct wlr_texture_impl texture_impl; diff --git a/render/wlr_renderer.c b/render/wlr_renderer.c index eb6b9e97f..e65314ccc 100644 --- a/render/wlr_renderer.c +++ b/render/wlr_renderer.c @@ -25,7 +25,6 @@ #include #endif // WLR_HAS_VULKAN_RENDERER -#include "render/pixel_format.h" #include "render/wlr_renderer.h" #include "util/env.h" diff --git a/types/wlr_drm_lease_v1.c b/types/wlr_drm_lease_v1.c index 50097ae75..c64bb0896 100644 --- a/types/wlr_drm_lease_v1.c +++ b/types/wlr_drm_lease_v1.c @@ -1,5 +1,4 @@ #include -#include #include #include #include diff --git a/types/wlr_ext_foreign_toplevel_list_v1.c b/types/wlr_ext_foreign_toplevel_list_v1.c index 432b27fc2..f20ddc877 100644 --- a/types/wlr_ext_foreign_toplevel_list_v1.c +++ b/types/wlr_ext_foreign_toplevel_list_v1.c @@ -1,4 +1,3 @@ - #include #include #include diff --git a/types/wlr_input_method_v2.c b/types/wlr_input_method_v2.c index 3804a113a..ae50d784c 100644 --- a/types/wlr_input_method_v2.c +++ b/types/wlr_input_method_v2.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -9,7 +10,6 @@ #include #include #include "input-method-unstable-v2-protocol.h" -#include "util/shm.h" // Note: zwp_input_popup_surface_v2 and zwp_input_method_keyboard_grab_v2 objects // become inert when the corresponding zwp_input_method_v2 is destroyed diff --git a/types/wlr_tearing_control_v1.c b/types/wlr_tearing_control_v1.c index 6ba0b2740..826cf5471 100644 --- a/types/wlr_tearing_control_v1.c +++ b/types/wlr_tearing_control_v1.c @@ -1,4 +1,3 @@ - #include #include #include diff --git a/types/wlr_xdg_foreign_v2.c b/types/wlr_xdg_foreign_v2.c index 8ea2af2a8..58071f4a1 100644 --- a/types/wlr_xdg_foreign_v2.c +++ b/types/wlr_xdg_foreign_v2.c @@ -1,4 +1,3 @@ - #include #include #include diff --git a/xwayland/xwayland.c b/xwayland/xwayland.c index 7cef4cc8f..d3b2a6c84 100644 --- a/xwayland/xwayland.c +++ b/xwayland/xwayland.c @@ -1,7 +1,5 @@ #include -#include #include -#include #include #include #include @@ -14,7 +12,6 @@ #include #include #include -#include "sockets.h" #include "xwayland/xwm.h" struct wlr_xwayland_cursor { From f8c639f19a40b33ab8f4a061ec49f657439781ee Mon Sep 17 00:00:00 2001 From: Philipp Kaeser Date: Wed, 16 Apr 2025 19:57:33 +0200 Subject: [PATCH 340/519] output_layout: Fixes docstring for wlr_output_layout_add[_auto]. --- include/wlr/types/wlr_output_layout.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/wlr/types/wlr_output_layout.h b/include/wlr/types/wlr_output_layout.h index f2cbc6374..b2665b068 100644 --- a/include/wlr/types/wlr_output_layout.h +++ b/include/wlr/types/wlr_output_layout.h @@ -85,7 +85,7 @@ struct wlr_output *wlr_output_layout_output_at( * already a part of the output layout, it will become manually configured and * will be moved to the specified coordinates. * - * Returns true on success, false on a memory allocation error. + * Returns the output's output layout, or NULL on error. */ struct wlr_output_layout_output *wlr_output_layout_add(struct wlr_output_layout *layout, struct wlr_output *output, int lx, int ly); @@ -97,7 +97,7 @@ struct wlr_output_layout_output *wlr_output_layout_add(struct wlr_output_layout * changes. If the output is already a part of the layout, it will become * automatically configured. * - * Returns true on success, false on a memory allocation error. + * Returns the output's output layout, or NULL on error. */ struct wlr_output_layout_output *wlr_output_layout_add_auto(struct wlr_output_layout *layout, struct wlr_output *output); From ed2406621a5e815e0eb5123a35af722e3cfbf72b Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Tue, 15 Apr 2025 12:01:16 +0300 Subject: [PATCH 341/519] xdg-surface: don't disconnect clients for bad effective geom yet Preferably, the geometry computation would've been done at the client commit time, but this requires correct subsurface state management which we don't have at the moment. The next best solution, which is computing the geometry on server commit time, doesn't currently have a way to prevent user commit handlers from firing, meaning that compositors might get an invalid surface state. Additionally, Chromium and gtk-layer-shell turned out to violate the protocol in this regard, so client disconnection leads to really bad UX. As such, complain via a log message instead, and ignore invalid geometry, falling back to the bounding rectangle. --- types/xdg_shell/wlr_xdg_surface.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/types/xdg_shell/wlr_xdg_surface.c b/types/xdg_shell/wlr_xdg_surface.c index 3d3299f93..e492405d3 100644 --- a/types/xdg_shell/wlr_xdg_surface.c +++ b/types/xdg_shell/wlr_xdg_surface.c @@ -257,17 +257,22 @@ static const struct xdg_surface_interface xdg_surface_implementation = { // the surface, in which case it's updated on map, so that subsurfaces are // mapped and surface extents are computed correctly. static void update_geometry(struct wlr_xdg_surface *surface) { - if (!wlr_box_empty(&surface->current.geometry)) { - if ((surface->current.committed & WLR_XDG_SURFACE_STATE_WINDOW_GEOMETRY) != 0) { - wlr_surface_get_extents(surface->surface, &surface->geometry); - wlr_box_intersection(&surface->geometry, - &surface->current.geometry, &surface->geometry); - if (wlr_box_empty(&surface->geometry)) { - wl_resource_post_error(surface->resource, XDG_SURFACE_ERROR_INVALID_SIZE, - "the effective window geometry is empty"); - } + if ((surface->current.committed & WLR_XDG_SURFACE_STATE_WINDOW_GEOMETRY) != 0) { + wlr_surface_get_extents(surface->surface, &surface->geometry); + + struct wlr_box effective; + wlr_box_intersection(&effective, &surface->geometry, &surface->current.geometry); + + if (wlr_box_empty(&effective)) { + wlr_log(WLR_INFO, + "A client has committed an invalid effective window geometry (%d,%d %dx%d); " + "this will result in client disconnection in the future", + effective.x, effective.y, effective.width, effective.height); + // The extents are used instead + } else { + surface->geometry = effective; } - } else { + } else if (wlr_box_empty(&surface->geometry)) { wlr_surface_get_extents(surface->surface, &surface->geometry); } } From 3ad4374a54b307f6d4473b04031860bf88798d00 Mon Sep 17 00:00:00 2001 From: YaoBing Xiao Date: Fri, 18 Apr 2025 17:10:17 +0800 Subject: [PATCH 342/519] backend/drm: add DRM object and property IDs to error --- backend/drm/properties.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/backend/drm/properties.c b/backend/drm/properties.c index 05af16738..0104304e5 100644 --- a/backend/drm/properties.c +++ b/backend/drm/properties.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -79,14 +80,14 @@ static bool scan_properties(int fd, uint32_t id, uint32_t type, uint32_t *result const struct prop_info *info, size_t info_len) { drmModeObjectProperties *props = drmModeObjectGetProperties(fd, id, type); if (!props) { - wlr_log_errno(WLR_ERROR, "Failed to get DRM object properties"); + wlr_log_errno(WLR_ERROR, "Failed to get DRM object %" PRIu32 " properties", id); return false; } for (uint32_t i = 0; i < props->count_props; ++i) { drmModePropertyRes *prop = drmModeGetProperty(fd, props->props[i]); if (!prop) { - wlr_log_errno(WLR_ERROR, "Failed to get DRM object property"); + wlr_log_errno(WLR_ERROR, "Failed to get property %" PRIu32 " of DRM object %" PRIu32, props->props[i], id); continue; } From 8dab5f838d32ff601bbb52b435717508322f7d61 Mon Sep 17 00:00:00 2001 From: M Stoeckl Date: Sat, 19 Apr 2025 08:15:04 -0400 Subject: [PATCH 343/519] color-management-v1: implement surface_feedback_v1::get_preferred_parametric --- types/wlr_color_management_v1.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/types/wlr_color_management_v1.c b/types/wlr_color_management_v1.c index 432f75bca..bdf13768d 100644 --- a/types/wlr_color_management_v1.c +++ b/types/wlr_color_management_v1.c @@ -371,9 +371,25 @@ static void surface_feedback_handle_get_preferred(struct wl_client *client, surface_feedback_resource, id, &surface_feedback->data, true); } +static void surface_feedback_handle_get_preferred_parametric(struct wl_client *client, + struct wl_resource *surface_feedback_resource, uint32_t id) { + struct wlr_color_management_surface_feedback_v1 *surface_feedback = + surface_feedback_from_resource(surface_feedback_resource); + if (surface_feedback == NULL) { + wl_resource_post_error(surface_feedback_resource, + WP_COLOR_MANAGEMENT_SURFACE_FEEDBACK_V1_ERROR_INERT, + "get_preferred_parametric sent on inert feedback surface"); + return; + } + + image_desc_create_ready(surface_feedback->manager, + surface_feedback_resource, id, &surface_feedback->data, true); +} + static const struct wp_color_management_surface_feedback_v1_interface surface_feedback_impl = { .destroy = resource_handle_destroy, .get_preferred = surface_feedback_handle_get_preferred, + .get_preferred_parametric = surface_feedback_handle_get_preferred_parametric, }; static void surface_feedback_destroy(struct wlr_color_management_surface_feedback_v1 *surface_feedback) { From 7bf669fb5ca3429c5cbd7f45e567244b1682db10 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Sat, 19 Apr 2025 00:58:03 +0300 Subject: [PATCH 344/519] xdg-surface: fix geom for surfaces that don't set it explicitly If a surface which relies on the default window geometry (e.g. wlroots' Wayland backend output) gets resized, the geometry doesn't get updated. This commit fixes that. Additionally, the fallback is the explicitly set window geometry now, not the extents; this works better for Chromium. --- types/xdg_shell/wlr_xdg_surface.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/types/xdg_shell/wlr_xdg_surface.c b/types/xdg_shell/wlr_xdg_surface.c index e492405d3..8a252d2f8 100644 --- a/types/xdg_shell/wlr_xdg_surface.c +++ b/types/xdg_shell/wlr_xdg_surface.c @@ -257,22 +257,24 @@ static const struct xdg_surface_interface xdg_surface_implementation = { // the surface, in which case it's updated on map, so that subsurfaces are // mapped and surface extents are computed correctly. static void update_geometry(struct wlr_xdg_surface *surface) { - if ((surface->current.committed & WLR_XDG_SURFACE_STATE_WINDOW_GEOMETRY) != 0) { - wlr_surface_get_extents(surface->surface, &surface->geometry); + if (!wlr_box_empty(&surface->current.geometry)) { + if ((surface->current.committed & WLR_XDG_SURFACE_STATE_WINDOW_GEOMETRY) != 0) { + struct wlr_box *geom = &surface->geometry; + wlr_surface_get_extents(surface->surface, geom); - struct wlr_box effective; - wlr_box_intersection(&effective, &surface->geometry, &surface->current.geometry); + wlr_box_intersection(geom, geom, &surface->current.geometry); + if (wlr_box_empty(geom)) { + wlr_log(WLR_INFO, + "A client has committed an invalid effective window geometry (%d,%d %dx%d); " + "this will result in client disconnection in the future", + geom->x, geom->y, geom->width, geom->height); - if (wlr_box_empty(&effective)) { - wlr_log(WLR_INFO, - "A client has committed an invalid effective window geometry (%d,%d %dx%d); " - "this will result in client disconnection in the future", - effective.x, effective.y, effective.width, effective.height); - // The extents are used instead - } else { - surface->geometry = effective; + // Fall back to the explicitly set window geometry as extents could be empty which + // would result in strange state when the client commits a buffer later + *geom = surface->current.geometry; + } } - } else if (wlr_box_empty(&surface->geometry)) { + } else { wlr_surface_get_extents(surface->surface, &surface->geometry); } } From b9d3ee9a2c91e46e720a39ca1b4e43e9fd1191fa Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 20 Apr 2025 13:21:10 +0200 Subject: [PATCH 345/519] build: bump version to 0.19.0-rc2 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index a40cb6bdf..75c4e266d 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'wlroots', 'c', - version: '0.19.0-rc1', + version: '0.19.0-rc2', license: 'MIT', meson_version: '>=1.3', default_options: [ From 78aaaaf7b62334fae673dec7b0ff2d5942b78041 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 20 Apr 2025 21:12:33 +0200 Subject: [PATCH 346/519] Revert "xwayland: Reset signal mask and handlers before exec" This reverts commit 954dba3968c2eea3bf2c21defe01bf540fd102f9. Motivations: - This only resets some state, but other global state such as other signal handlers, process limits (e.g. NOFILE) or system-specific settings are left as-is. The chunk of state which does get reset is opinionated. - Compositors have other ways to do this, e.g. with pthread_atfork() or with empty signal handler callbacks. --- xwayland/server.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/xwayland/server.c b/xwayland/server.c index 3abeb8bb9..0e8ad44fe 100644 --- a/xwayland/server.c +++ b/xwayland/server.c @@ -36,13 +36,6 @@ noreturn static void exec_xwayland(struct wlr_xwayland_server *server, _exit(EXIT_FAILURE); } - // The compositor may have messed with signal handling, try to clean it up - sigset_t set; - sigemptyset(&set); - sigprocmask(SIG_SETMASK, &set, NULL); - signal(SIGPIPE, SIG_DFL); - signal(SIGCHLD, SIG_DFL); - char *argv[64] = {0}; size_t i = 0; From e9450a994764a89f47e3dbb7b06c754140047e43 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Thu, 24 Apr 2025 13:19:35 +0200 Subject: [PATCH 347/519] xdg-activation: make wl_global public This is currently inconsistent with everything else in wlroots. --- include/wlr/types/wlr_xdg_activation_v1.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/wlr/types/wlr_xdg_activation_v1.h b/include/wlr/types/wlr_xdg_activation_v1.h index 6c1d5b8ad..c9eda5be2 100644 --- a/include/wlr/types/wlr_xdg_activation_v1.h +++ b/include/wlr/types/wlr_xdg_activation_v1.h @@ -39,6 +39,7 @@ struct wlr_xdg_activation_token_v1 { }; struct wlr_xdg_activation_v1 { + struct wl_global *global; uint32_t token_timeout_msec; // token timeout in milliseconds (0 to disable) struct wl_list tokens; // wlr_xdg_activation_token_v1.link @@ -51,7 +52,6 @@ struct wlr_xdg_activation_v1 { struct { struct wl_display *display; - struct wl_global *global; struct wl_listener display_destroy; } WLR_PRIVATE; From bd8454d3bc0ff8753b278e031ebf865a38ad0f7c Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sat, 15 Feb 2025 01:13:15 +0900 Subject: [PATCH 348/519] Revert "wlr_keyboard: don't emit key event for duplicated keycodes" This reverts commit 86eaa44a3a9f5ee315aa1a6a676aeeb911942fd8. That commit caused a regression for IME users in many compositors: when a input_method is activated while a key is pressed, and a virtual keyboard is created by IME, the following key-release event via the virtual keyboard is missed since the key in the virtual keyboard haven't been pressed. For example, pressing and releasing Ctrl+F in Firefox with fcitx5 running triggered repeated keys (ffffff...) in the opened input box. --- include/types/wlr_keyboard.h | 2 +- types/wlr_keyboard.c | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/include/types/wlr_keyboard.h b/include/types/wlr_keyboard.h index bdae09bdd..7cd26e8a5 100644 --- a/include/types/wlr_keyboard.h +++ b/include/types/wlr_keyboard.h @@ -1,6 +1,6 @@ #include -bool keyboard_key_update(struct wlr_keyboard *keyboard, +void keyboard_key_update(struct wlr_keyboard *keyboard, struct wlr_keyboard_key_event *event); bool keyboard_modifier_update(struct wlr_keyboard *keyboard); diff --git a/types/wlr_keyboard.c b/types/wlr_keyboard.c index edf0d3ecb..613b4e841 100644 --- a/types/wlr_keyboard.c +++ b/types/wlr_keyboard.c @@ -66,9 +66,8 @@ bool keyboard_modifier_update(struct wlr_keyboard *keyboard) { return true; } -bool keyboard_key_update(struct wlr_keyboard *keyboard, +void keyboard_key_update(struct wlr_keyboard *keyboard, struct wlr_keyboard_key_event *event) { - size_t old_num_keycodes = keyboard->num_keycodes; if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { set_add(keyboard->keycodes, &keyboard->num_keycodes, WLR_KEYBOARD_KEYS_CAP, event->keycode); @@ -79,8 +78,6 @@ bool keyboard_key_update(struct wlr_keyboard *keyboard, } assert(keyboard->num_keycodes <= WLR_KEYBOARD_KEYS_CAP); - - return old_num_keycodes != keyboard->num_keycodes; } void wlr_keyboard_notify_modifiers(struct wlr_keyboard *keyboard, @@ -102,9 +99,8 @@ void wlr_keyboard_notify_modifiers(struct wlr_keyboard *keyboard, void wlr_keyboard_notify_key(struct wlr_keyboard *keyboard, struct wlr_keyboard_key_event *event) { - if (keyboard_key_update(keyboard, event)) { - wl_signal_emit_mutable(&keyboard->events.key, event); - } + keyboard_key_update(keyboard, event); + wl_signal_emit_mutable(&keyboard->events.key, event); if (keyboard->xkb_state == NULL) { return; From 86976870bdf785efa028adcc691ab37f3d38eb42 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sat, 15 Feb 2025 16:47:52 +0900 Subject: [PATCH 349/519] wlr_keyboard_group: fix leak of wlr_keyboard_group->keys If the underlying wlr_keyboard emits duplicated key-presses, wlr_keyboard_group->keys might not be empty even after calling wlr_keyboard_group_remove_keyboard() for all of the keyboards. --- types/wlr_keyboard_group.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/types/wlr_keyboard_group.c b/types/wlr_keyboard_group.c index 734e0b2f3..0ff6d93bb 100644 --- a/types/wlr_keyboard_group.c +++ b/types/wlr_keyboard_group.c @@ -306,10 +306,19 @@ void wlr_keyboard_group_remove_keyboard(struct wlr_keyboard_group *group, } void wlr_keyboard_group_destroy(struct wlr_keyboard_group *group) { - struct keyboard_group_device *device, *tmp; - wl_list_for_each_safe(device, tmp, &group->devices, link) { + struct keyboard_group_device *device, *tmp_device; + wl_list_for_each_safe(device, tmp_device, &group->devices, link) { wlr_keyboard_group_remove_keyboard(group, device->keyboard); } + + // Now group->keys might not be empty if a wlr_keyboard has emitted + // duplicated key presses + struct keyboard_group_key *key, *tmp_key; + wl_list_for_each_safe(key, tmp_key, &group->keys, link) { + wl_list_remove(&key->link); + free(key); + } + wlr_keyboard_finish(&group->keyboard); assert(wl_list_empty(&group->events.enter.listener_list)); From 648aee65ad7bf45bde397df393f0eb7e8221d06b Mon Sep 17 00:00:00 2001 From: YaoBing Xiao Date: Tue, 22 Apr 2025 15:10:34 +0800 Subject: [PATCH 350/519] backend/drm: rename conn_name to conn_type_name for clarity --- backend/drm/drm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index e106ea8c1..bca4541ea 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -1531,14 +1531,14 @@ static struct wlr_drm_connector *create_drm_connector(struct wlr_drm_backend *dr return NULL; } - const char *conn_name = + const char *conn_type_name = drmModeGetConnectorTypeName(drm_conn->connector_type); - if (conn_name == NULL) { - conn_name = "Unknown"; + if (conn_type_name == NULL) { + conn_type_name = "Unknown"; } snprintf(wlr_conn->name, sizeof(wlr_conn->name), - "%s-%"PRIu32, conn_name, drm_conn->connector_type_id); + "%s-%"PRIu32, conn_type_name, drm_conn->connector_type_id); wlr_conn->possible_crtcs = drmModeConnectorGetPossibleCrtcs(drm->fd, drm_conn); From 7dd8fdf76c4e58e99c768a1f025777ddc8585a1a Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sun, 27 Apr 2025 11:25:23 +0200 Subject: [PATCH 351/519] tablet-v2: cleanup focused surface on tool destroy Currently the surface_destroy listener may not get removed if destroy_tablet_tool_v2() is called while the tool is in proximity to a surface. Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3969 Reported-by: Hooman Ise --- types/tablet_v2/wlr_tablet_v2_tool.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/types/tablet_v2/wlr_tablet_v2_tool.c b/types/tablet_v2/wlr_tablet_v2_tool.c index 8574a9a2f..e73aa767b 100644 --- a/types/tablet_v2/wlr_tablet_v2_tool.c +++ b/types/tablet_v2/wlr_tablet_v2_tool.c @@ -103,7 +103,10 @@ void destroy_tablet_tool_v2(struct wl_resource *resource) { } if (client->tool && client->tool->current_client == client) { + wl_list_remove(&client->tool->surface_destroy.link); + wl_list_init(&client->tool->surface_destroy.link); client->tool->current_client = NULL; + client->tool->focused_surface = NULL; } wl_list_remove(&client->seat_link); From 80f33cd350783824bd86c671660b0701964e4c38 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Thu, 24 Apr 2025 12:50:41 +0200 Subject: [PATCH 352/519] presentation-time: make version a uint32_t This is consistent with wlr_xdg_shell_create(), wlr_shm_create(), wlr_layer_shell_create(), etc. --- include/wlr/types/wlr_presentation_time.h | 2 +- types/wlr_presentation_time.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/wlr/types/wlr_presentation_time.h b/include/wlr/types/wlr_presentation_time.h index 22d538ed7..c635208e4 100644 --- a/include/wlr/types/wlr_presentation_time.h +++ b/include/wlr/types/wlr_presentation_time.h @@ -59,7 +59,7 @@ struct wlr_presentation_event { struct wlr_backend; struct wlr_presentation *wlr_presentation_create(struct wl_display *display, - struct wlr_backend *backend, int version); + struct wlr_backend *backend, uint32_t version); /** * Mark the current surface's buffer as sampled. * diff --git a/types/wlr_presentation_time.c b/types/wlr_presentation_time.c index 763cae953..c4366f0fe 100644 --- a/types/wlr_presentation_time.c +++ b/types/wlr_presentation_time.c @@ -175,7 +175,7 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { } struct wlr_presentation *wlr_presentation_create(struct wl_display *display, - struct wlr_backend *backend, int version) { + struct wlr_backend *backend, uint32_t version) { assert(version <= PRESENTATION_VERSION); struct wlr_presentation *presentation = calloc(1, sizeof(*presentation)); From 156d47c8665e7508623e3e96db0b0300742db937 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 27 Apr 2025 22:48:44 +0200 Subject: [PATCH 353/519] build: bump version to 0.19.0-rc3 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 75c4e266d..3cc564b04 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'wlroots', 'c', - version: '0.19.0-rc2', + version: '0.19.0-rc3', license: 'MIT', meson_version: '>=1.3', default_options: [ From 22db307e4cd63f64228aaf3c763764470f112982 Mon Sep 17 00:00:00 2001 From: YaoBing Xiao Date: Wed, 30 Apr 2025 17:10:45 +0800 Subject: [PATCH 354/519] backend/drm: assign plane_id from function parameter instead of drm_plane --- backend/drm/drm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index bca4541ea..e9e4c6db2 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -170,7 +170,7 @@ static bool init_plane(struct wlr_drm_backend *drm, } p->type = type; - p->id = drm_plane->plane_id; + p->id = id; p->props = props; p->initial_crtc_id = drm_plane->crtc_id; From d4e4c9f64b388cd2698294fbfcc37fd68104e24a Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 5 May 2025 22:14:40 +0200 Subject: [PATCH 355/519] render/vulkan: create binary semaphore when signal timeline is supplied We were only creating the binary semaphore when implicit sync interop was available. We also use the binary semaphore when explicit sync is enabled. Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3973 --- render/vulkan/pass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 032fcc7b2..3f662b203 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -480,7 +480,7 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { .semaphore = renderer->timeline_semaphore, .value = render_timeline_point, }; - if (renderer->dev->implicit_sync_interop) { + if (renderer->dev->implicit_sync_interop || pass->signal_timeline != NULL) { if (render_cb->binary_semaphore == VK_NULL_HANDLE) { VkExportSemaphoreCreateInfo export_info = { .sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO, From e57dd9c5ef0b9cffeb56f0d87e0e520f1e300de4 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 5 May 2025 22:16:26 +0200 Subject: [PATCH 356/519] render/vulkan: check binary semaphore in vulkan_sync_render_buffer() Ensure the binary semaphore is available when we need it. --- render/vulkan/renderer.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index 4051dbf05..b2feca013 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -989,6 +989,8 @@ bool vulkan_sync_render_buffer(struct wlr_vk_renderer *renderer, return vulkan_wait_command_buffer(cb, renderer); } + assert(cb->binary_semaphore != VK_NULL_HANDLE); + // Note: vkGetSemaphoreFdKHR implicitly resets the semaphore const VkSemaphoreGetFdInfoKHR get_fence_fd_info = { .sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR, From aef4de2ced66ff45a2cba4788b1951af945da663 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 6 May 2025 00:39:05 +0200 Subject: [PATCH 357/519] wlr-foreign-toplevel-management-v1: Simply leave and destroy code We send the output_leave event and destroy a toplevel_output both if our output_leave listener is called, or if the underlying wlr_output is destroyed. We somewhat clumsily reused the output_leave listener, which meant that even though we had the toplevel output, we went out of our way to let the output_leave handler find said toplevel_output again. Simplify both pathways by adding a toplevel_output_leave function. Should have no functional changes. --- types/wlr_foreign_toplevel_management_v1.c | 40 +++++++++++----------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/types/wlr_foreign_toplevel_management_v1.c b/types/wlr_foreign_toplevel_management_v1.c index f06fdb7b7..15b18000c 100644 --- a/types/wlr_foreign_toplevel_management_v1.c +++ b/types/wlr_foreign_toplevel_management_v1.c @@ -273,12 +273,27 @@ static void toplevel_handle_output_bind(struct wl_listener *listener, toplevel_update_idle_source(toplevel_output->toplevel); } +static void toplevel_output_destroy( + struct wlr_foreign_toplevel_handle_v1_output *toplevel_output) { + wl_list_remove(&toplevel_output->link); + wl_list_remove(&toplevel_output->output_bind.link); + wl_list_remove(&toplevel_output->output_destroy.link); + free(toplevel_output); +} + +static void toplevel_output_leave( + struct wlr_foreign_toplevel_handle_v1_output *toplevel_output) { + struct wlr_foreign_toplevel_handle_v1 *toplevel = toplevel_output->toplevel; + struct wlr_output *output = toplevel_output->output; + toplevel_send_output(toplevel, output, false); + toplevel_output_destroy(toplevel_output); +} + static void toplevel_handle_output_destroy(struct wl_listener *listener, void *data) { struct wlr_foreign_toplevel_handle_v1_output *toplevel_output = wl_container_of(listener, toplevel_output, output_destroy); - wlr_foreign_toplevel_handle_v1_output_leave(toplevel_output->toplevel, - toplevel_output->output); + toplevel_output_leave(toplevel_output); } void wlr_foreign_toplevel_handle_v1_output_enter( @@ -310,33 +325,18 @@ void wlr_foreign_toplevel_handle_v1_output_enter( toplevel_send_output(toplevel, output, true); } -static void toplevel_output_destroy( - struct wlr_foreign_toplevel_handle_v1_output *toplevel_output) { - wl_list_remove(&toplevel_output->link); - wl_list_remove(&toplevel_output->output_bind.link); - wl_list_remove(&toplevel_output->output_destroy.link); - free(toplevel_output); -} - void wlr_foreign_toplevel_handle_v1_output_leave( struct wlr_foreign_toplevel_handle_v1 *toplevel, struct wlr_output *output) { struct wlr_foreign_toplevel_handle_v1_output *toplevel_output_iterator; - struct wlr_foreign_toplevel_handle_v1_output *toplevel_output = NULL; - wl_list_for_each(toplevel_output_iterator, &toplevel->outputs, link) { if (toplevel_output_iterator->output == output) { - toplevel_output = toplevel_output_iterator; - break; + toplevel_output_leave(toplevel_output_iterator); + return; } } - if (toplevel_output) { - toplevel_send_output(toplevel, output, false); - toplevel_output_destroy(toplevel_output); - } else { - // XXX: log an error? crash? - } + // XXX: log an error? crash? } static void fill_array_from_toplevel_state(struct wl_array *states, From f36f856cdb2266cf7773e3d1b5bd2ebba2ceecf5 Mon Sep 17 00:00:00 2001 From: YaoBing Xiao Date: Mon, 5 May 2025 21:54:48 +0800 Subject: [PATCH 358/519] render/drm_syncobj: fix return type mismatch --- render/drm_syncobj.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/render/drm_syncobj.c b/render/drm_syncobj.c index 7f479f570..912a2b6e5 100644 --- a/render/drm_syncobj.c +++ b/render/drm_syncobj.c @@ -136,7 +136,7 @@ bool wlr_drm_syncobj_timeline_import_sync_file(struct wlr_drm_syncobj_timeline * uint32_t syncobj_handle; if (drmSyncobjCreate(timeline->drm_fd, 0, &syncobj_handle) != 0) { wlr_log_errno(WLR_ERROR, "drmSyncobjCreate failed"); - return -1; + return false; } if (drmSyncobjImportSyncFile(timeline->drm_fd, syncobj_handle, From 70add22e74df8f7591ea2f8871306e8192200a27 Mon Sep 17 00:00:00 2001 From: YaoBing Xiao Date: Wed, 7 May 2025 12:44:06 +0800 Subject: [PATCH 359/519] render/pixman: null check on newly created image --- render/pixman/renderer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/render/pixman/renderer.c b/render/pixman/renderer.c index a5b83e38e..4631a33ab 100644 --- a/render/pixman/renderer.c +++ b/render/pixman/renderer.c @@ -43,7 +43,7 @@ bool begin_pixman_data_ptr_access(struct wlr_buffer *wlr_buffer, pixman_image_t pixman_image_t *new_image = pixman_image_create_bits_no_clear(format, wlr_buffer->width, wlr_buffer->height, data, stride); - if (image == NULL) { + if (new_image == NULL) { wlr_buffer_end_data_ptr_access(wlr_buffer); return false; } From 2420bfef0b104d9fa1152b63661e1fb0cf05bb41 Mon Sep 17 00:00:00 2001 From: David Turner Date: Tue, 6 May 2025 16:01:00 +0100 Subject: [PATCH 360/519] backend/drm: Fix segfault in libliftoff startup With labwc and WLR_DRM_FORCE_LIBLIFTOFF=1, a segfault is seen on startup because we call output_state_get_buffer_src_box() when there is no buffer set in the output state. Fix this by getting the src/dst box from state->primary_viewport instead. --- backend/drm/libliftoff.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/backend/drm/libliftoff.c b/backend/drm/libliftoff.c index a5bace015..12761afd4 100644 --- a/backend/drm/libliftoff.c +++ b/backend/drm/libliftoff.c @@ -331,17 +331,16 @@ static bool add_connector(drmModeAtomicReq *req, if (crtc->props.vrr_enabled != 0) { ok = ok && add_prop(req, crtc->id, crtc->props.vrr_enabled, state->vrr_enabled); } - struct wlr_fbox src_box; - struct wlr_box dst_box; - output_state_get_buffer_src_box(state->base, &src_box); - output_state_get_buffer_dst_box(state->base, &dst_box); - ok = ok && - set_plane_props(crtc->primary, crtc->primary->liftoff_layer, - state->primary_fb, 0, &dst_box, &src_box); - ok = ok && - set_plane_props(crtc->primary, crtc->liftoff_composition_layer, - state->primary_fb, 0, &dst_box, &src_box); + ok = ok && set_plane_props(crtc->primary, + crtc->primary->liftoff_layer, state->primary_fb, 0, + &state->primary_viewport.dst_box, + &state->primary_viewport.src_box); + ok = ok && set_plane_props(crtc->primary, + crtc->liftoff_composition_layer, state->primary_fb, 0, + &state->primary_viewport.dst_box, + &state->primary_viewport.src_box); + liftoff_layer_set_property(crtc->primary->liftoff_layer, "FB_DAMAGE_CLIPS", state->fb_damage_clips); liftoff_layer_set_property(crtc->liftoff_composition_layer, From af34aaad53770b340e57b7a801cd98d5d341eee5 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 1 May 2025 15:11:36 +0200 Subject: [PATCH 361/519] xwayland: handle unset _NET_WM_NAME The spec says [1]: > If set, the Window Manager should use this in preference to WM_NAME. However we overwrite WM_NAME with NULL when _NET_WM_NAME is unset. Fix this by storing both WM_NAME and _NET_WM_NAME, so that we handle properly all combinations of events (e.g. a client setting both and later clearing one). [1]: https://specifications.freedesktop.org/wm-spec/1.3/ar01s05.html#id-1.6.2 --- include/wlr/xwayland/xwayland.h | 2 ++ xwayland/xwm.c | 38 ++++++++++++++++++++++----------- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/include/wlr/xwayland/xwayland.h b/include/wlr/xwayland/xwayland.h index b1b2247e6..0eb466d96 100644 --- a/include/wlr/xwayland/xwayland.h +++ b/include/wlr/xwayland/xwayland.h @@ -232,6 +232,8 @@ struct wlr_xwayland_surface { void *data; struct { + char *wm_name, *net_wm_name; + struct wl_listener surface_commit; struct wl_listener surface_map; struct wl_listener surface_unmap; diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 8e3ea3b4e..8cd1d41ab 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -629,7 +629,8 @@ static void xwayland_surface_destroy(struct wlr_xwayland_surface *xsurface) { wl_event_source_remove(xsurface->ping_timer); - free(xsurface->title); + free(xsurface->wm_name); + free(xsurface->net_wm_name); free(xsurface->class); free(xsurface->instance); free(xsurface->role); @@ -739,7 +740,7 @@ static void read_surface_role(struct wlr_xwm *xwm, } static void read_surface_title(struct wlr_xwm *xwm, - struct wlr_xwayland_surface *xsurface, + struct wlr_xwayland_surface *xsurface, xcb_atom_t property, xcb_get_property_reply_t *reply) { if (reply->type != XCB_ATOM_STRING && reply->type != xwm->atoms[UTF8_STRING] && reply->type != XCB_ATOM_NONE) { @@ -747,21 +748,34 @@ static void read_surface_title(struct wlr_xwm *xwm, return; } - bool is_utf8 = reply->type == xwm->atoms[UTF8_STRING]; - if (!is_utf8 && xsurface->has_utf8_title && reply->type != XCB_ATOM_NONE) { - return; + size_t len = xcb_get_property_value_length(reply); + const char *title_buffer = xcb_get_property_value(reply); + char *title = NULL; + if (len > 0) { + title = strndup(title_buffer, len); } - size_t len = xcb_get_property_value_length(reply); - char *title = xcb_get_property_value(reply); + if (property == XCB_ATOM_WM_NAME) { + free(xsurface->wm_name); + xsurface->wm_name = title; + } else if (property == xwm->atoms[NET_WM_NAME]) { + free(xsurface->net_wm_name); + xsurface->net_wm_name = title; + } else { + abort(); // unreachable + } - free(xsurface->title); - if (len > 0) { - xsurface->title = strndup(title, len); + // Prefer _NET_WM_NAME over WM_NAME if available + if (xsurface->net_wm_name != NULL) { + xsurface->title = xsurface->net_wm_name; + } else if (xsurface->wm_name != NULL) { + xsurface->title = xsurface->wm_name; } else { xsurface->title = NULL; } - xsurface->has_utf8_title = is_utf8; + + // TODO: drop this field + xsurface->has_utf8_title = reply->type == xwm->atoms[UTF8_STRING]; wl_signal_emit_mutable(&xsurface->events.set_title, NULL); } @@ -1061,7 +1075,7 @@ static void read_surface_property(struct wlr_xwm *xwm, read_surface_class(xwm, xsurface, reply); } else if (property == XCB_ATOM_WM_NAME || property == xwm->atoms[NET_WM_NAME]) { - read_surface_title(xwm, xsurface, reply); + read_surface_title(xwm, xsurface, property, reply); } else if (property == XCB_ATOM_WM_TRANSIENT_FOR) { read_surface_parent(xwm, xsurface, reply); } else if (property == xwm->atoms[NET_WM_PID]) { From 13a62a23a258d96f902c740310d5c7c59784a4d1 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 15 May 2025 10:47:57 +0200 Subject: [PATCH 362/519] build: bump version to 0.19.0 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 3cc564b04..016e59842 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'wlroots', 'c', - version: '0.19.0-rc3', + version: '0.19.0', license: 'MIT', meson_version: '>=1.3', default_options: [ From f04ef79f619983bfb4a7c9908bdae62e0d0d5ba7 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 15 May 2025 10:54:23 +0200 Subject: [PATCH 363/519] build: bump version to 0.20.0-dev --- meson.build | 2 +- tinywl/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index 016e59842..fc7c179a6 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'wlroots', 'c', - version: '0.19.0', + version: '0.20.0-dev', license: 'MIT', meson_version: '>=1.3', default_options: [ diff --git a/tinywl/Makefile b/tinywl/Makefile index a5cedfcc8..70dc671ca 100644 --- a/tinywl/Makefile +++ b/tinywl/Makefile @@ -2,7 +2,7 @@ PKG_CONFIG?=pkg-config WAYLAND_PROTOCOLS!=$(PKG_CONFIG) --variable=pkgdatadir wayland-protocols WAYLAND_SCANNER!=$(PKG_CONFIG) --variable=wayland_scanner wayland-scanner -PKGS="wlroots-0.19" wayland-server xkbcommon +PKGS="wlroots-0.20" wayland-server xkbcommon CFLAGS_PKG_CONFIG!=$(PKG_CONFIG) --cflags $(PKGS) CFLAGS+=$(CFLAGS_PKG_CONFIG) LIBS!=$(PKG_CONFIG) --libs $(PKGS) From c9f0dbc159f0d5408f28477c62e6ee8038726f1d Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 24 Apr 2025 14:12:43 +0100 Subject: [PATCH 364/519] Change all timespec pointers in events to owned Follow-up from !4803. Make things consistent by making all `struct timespec`s in events owned. Reduces the need for thinking about ownership/lifetimes. --- include/wlr/types/wlr_output.h | 4 ++-- types/ext_image_capture_source_v1/output.c | 4 ++-- types/output/output.c | 4 ++-- types/wlr_cursor.c | 2 +- types/wlr_export_dmabuf_v1.c | 4 ++-- types/wlr_screencopy_v1.c | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 9c08a2c34..31b757062 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -245,13 +245,13 @@ struct wlr_output_event_damage { struct wlr_output_event_precommit { struct wlr_output *output; - struct timespec *when; + struct timespec when; const struct wlr_output_state *state; }; struct wlr_output_event_commit { struct wlr_output *output; - struct timespec *when; + struct timespec when; const struct wlr_output_state *state; }; diff --git a/types/ext_image_capture_source_v1/output.c b/types/ext_image_capture_source_v1/output.c index e5ef78f93..a2726c759 100644 --- a/types/ext_image_capture_source_v1/output.c +++ b/types/ext_image_capture_source_v1/output.c @@ -40,7 +40,7 @@ struct wlr_ext_output_image_capture_source_v1 { struct wlr_ext_output_image_capture_source_v1_frame_event { struct wlr_ext_image_capture_source_v1_frame_event base; struct wlr_buffer *buffer; - struct timespec *when; + struct timespec when; }; static void output_source_start(struct wlr_ext_image_capture_source_v1 *base, @@ -85,7 +85,7 @@ static void output_source_copy_frame(struct wlr_ext_image_capture_source_v1 *bas if (wlr_ext_image_copy_capture_frame_v1_copy_buffer(frame, event->buffer, source->output->renderer)) { wlr_ext_image_copy_capture_frame_v1_ready(frame, - source->output->transform, event->when); + source->output->transform, &event->when); } } diff --git a/types/output/output.c b/types/output/output.c index a352a5e59..346a3aa40 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -728,7 +728,7 @@ bool output_prepare_commit(struct wlr_output *output, const struct wlr_output_st struct wlr_output_event_precommit pre_event = { .output = output, - .when = &now, + .when = now, .state = state, }; wl_signal_emit_mutable(&output->events.precommit, &pre_event); @@ -750,7 +750,7 @@ void output_apply_commit(struct wlr_output *output, const struct wlr_output_stat clock_gettime(CLOCK_MONOTONIC, &now); struct wlr_output_event_commit event = { .output = output, - .when = &now, + .when = now, .state = state, }; wl_signal_emit_mutable(&output->events.commit, &event); diff --git a/types/wlr_cursor.c b/types/wlr_cursor.c index 44b74e926..7f99fef0c 100644 --- a/types/wlr_cursor.c +++ b/types/wlr_cursor.c @@ -650,7 +650,7 @@ static void output_cursor_output_handle_output_commit( struct wlr_surface *surface = output_cursor->cursor->state->surface; if (surface && output_cursor->output_cursor->visible && (event->state->committed & WLR_OUTPUT_STATE_BUFFER)) { - wlr_surface_send_frame_done(surface, event->when); + wlr_surface_send_frame_done(surface, &event->when); } } diff --git a/types/wlr_export_dmabuf_v1.c b/types/wlr_export_dmabuf_v1.c index ea4a53880..4e629dcfd 100644 --- a/types/wlr_export_dmabuf_v1.c +++ b/types/wlr_export_dmabuf_v1.c @@ -85,11 +85,11 @@ static void frame_output_handle_commit(struct wl_listener *listener, attribs.fd[i], size, attribs.offset[i], attribs.stride[i], i); } - time_t tv_sec = event->when->tv_sec; + time_t tv_sec = event->when.tv_sec; uint32_t tv_sec_hi = (sizeof(tv_sec) > 4) ? tv_sec >> 32 : 0; uint32_t tv_sec_lo = tv_sec & 0xFFFFFFFF; zwlr_export_dmabuf_frame_v1_send_ready(frame->resource, - tv_sec_hi, tv_sec_lo, event->when->tv_nsec); + tv_sec_hi, tv_sec_lo, event->when.tv_nsec); frame_destroy(frame); } diff --git a/types/wlr_screencopy_v1.c b/types/wlr_screencopy_v1.c index c49ed6bdf..58e8dc8ba 100644 --- a/types/wlr_screencopy_v1.c +++ b/types/wlr_screencopy_v1.c @@ -334,7 +334,7 @@ static void frame_handle_output_commit(struct wl_listener *listener, zwlr_screencopy_frame_v1_send_flags(frame->resource, 0); frame_send_damage(frame); - frame_send_ready(frame, event->when); + frame_send_ready(frame, &event->when); frame_destroy(frame); return; From c2327248f8a22b3e66e00a6e8bccb3771834cad9 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 20 Apr 2025 21:46:11 +0200 Subject: [PATCH 365/519] output: don't send make/model - These are legacy wl_output properties [1] - wl_output exposes name and description, which are better defined - It's not clear what make/model/serial are for a virtual output - Clients shouldn't rely on these fields [1]: https://gitlab.freedesktop.org/wayland/wayland/-/blob/8f1795f9115bfb71cf143cea43bcb102b6c5c3ea/protocol/wayland.xml?page=3#L2508 Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/1623 --- types/output/output.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/types/output/output.c b/types/output/output.c index 346a3aa40..55f8a7645 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -16,20 +16,9 @@ static void send_geometry(struct wl_resource *resource) { struct wlr_output *output = wlr_output_from_resource(resource); - - const char *make = output->make; - if (make == NULL) { - make = "Unknown"; - } - - const char *model = output->model; - if (model == NULL) { - model = "Unknown"; - } - wl_output_send_geometry(resource, 0, 0, output->phys_width, output->phys_height, output->subpixel, - make, model, output->transform); + "Unknown", "Unknown", output->transform); } static void send_current_mode(struct wl_resource *resource) { From 62c86fb9754524e0779066e8f32730f0b85dcb6e Mon Sep 17 00:00:00 2001 From: Martin Rys Date: Tue, 18 Feb 2025 14:36:15 +0100 Subject: [PATCH 366/519] Add support for XKB_LED_NAME_COMPOSE and XKB_LED_NAME_KANA USB HID LEDs Requires xkbcommon 1.8.0 --- include/wlr/types/wlr_keyboard.h | 4 +++- meson.build | 4 ++-- types/wlr_keyboard.c | 2 ++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/include/wlr/types/wlr_keyboard.h b/include/wlr/types/wlr_keyboard.h index 23ca006a1..20a310fde 100644 --- a/include/wlr/types/wlr_keyboard.h +++ b/include/wlr/types/wlr_keyboard.h @@ -16,12 +16,14 @@ #include #include -#define WLR_LED_COUNT 3 +#define WLR_LED_COUNT 5 enum wlr_keyboard_led { WLR_LED_NUM_LOCK = 1 << 0, WLR_LED_CAPS_LOCK = 1 << 1, WLR_LED_SCROLL_LOCK = 1 << 2, + WLR_LED_COMPOSE = 1 << 3, + WLR_LED_KANA = 1 << 4, }; #define WLR_MODIFIER_COUNT 8 diff --git a/meson.build b/meson.build index fc7c179a6..8319eff69 100644 --- a/meson.build +++ b/meson.build @@ -105,8 +105,8 @@ drm = dependency('libdrm', 'tests=false', ], ) -xkbcommon = dependency( - 'xkbcommon', +xkbcommon = dependency('xkbcommon', + version: '>=1.8.0', fallback: 'libxkbcommon', default_options: [ 'enable-tools=false', diff --git a/types/wlr_keyboard.c b/types/wlr_keyboard.c index 613b4e841..a24335b3a 100644 --- a/types/wlr_keyboard.c +++ b/types/wlr_keyboard.c @@ -235,6 +235,8 @@ bool wlr_keyboard_set_keymap(struct wlr_keyboard *kb, struct xkb_keymap *keymap) XKB_LED_NAME_NUM, XKB_LED_NAME_CAPS, XKB_LED_NAME_SCROLL, + XKB_LED_NAME_COMPOSE, + XKB_LED_NAME_KANA, }; for (size_t i = 0; i < WLR_LED_COUNT; ++i) { kb->led_indexes[i] = xkb_map_led_get_index(kb->keymap, led_names[i]); From 536100488fc4c64528786801860f96cfa1a55765 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Thu, 10 Apr 2025 14:43:03 +0200 Subject: [PATCH 367/519] text-input-v3: Name new text input event correctly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Helps: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3851 Signed-off-by: Guido Günther --- include/wlr/types/wlr_text_input_v3.h | 2 +- types/wlr_text_input_v3.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/wlr/types/wlr_text_input_v3.h b/include/wlr/types/wlr_text_input_v3.h index 4bc4dbbd9..f9eb1d7ed 100644 --- a/include/wlr/types/wlr_text_input_v3.h +++ b/include/wlr/types/wlr_text_input_v3.h @@ -74,7 +74,7 @@ struct wlr_text_input_manager_v3 { struct wl_list text_inputs; // wlr_text_input_v3.link struct { - struct wl_signal text_input; // struct wlr_text_input_v3 + struct wl_signal new_text_input; // struct wlr_text_input_v3 struct wl_signal destroy; // struct wlr_text_input_manager_v3 } events; diff --git a/types/wlr_text_input_v3.c b/types/wlr_text_input_v3.c index 6c9f07964..1b1ea4df9 100644 --- a/types/wlr_text_input_v3.c +++ b/types/wlr_text_input_v3.c @@ -287,7 +287,7 @@ static void text_input_manager_get_text_input(struct wl_client *client, text_input_manager_from_resource(resource); wl_list_insert(&manager->text_inputs, &text_input->link); - wl_signal_emit_mutable(&manager->events.text_input, text_input); + wl_signal_emit_mutable(&manager->events.new_text_input, text_input); } static const struct zwp_text_input_manager_v3_interface @@ -315,7 +315,7 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); - assert(wl_list_empty(&manager->events.text_input.listener_list)); + assert(wl_list_empty(&manager->events.new_text_input.listener_list)); assert(wl_list_empty(&manager->events.destroy.listener_list)); wl_list_remove(&manager->display_destroy.link); @@ -332,7 +332,7 @@ struct wlr_text_input_manager_v3 *wlr_text_input_manager_v3_create( wl_list_init(&manager->text_inputs); - wl_signal_init(&manager->events.text_input); + wl_signal_init(&manager->events.new_text_input); wl_signal_init(&manager->events.destroy); manager->global = wl_global_create(display, From 2d5492c73770c9de420527df1098fefabe43d689 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Thu, 10 Apr 2025 14:46:35 +0200 Subject: [PATCH 368/519] text-input-v3: Use `NULL` when emitting signals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Listeners can use `wl_container_of`. Helps: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3851 Signed-off-by: Guido Günther --- types/wlr_text_input_v3.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/types/wlr_text_input_v3.c b/types/wlr_text_input_v3.c index 1b1ea4df9..bbae3a076 100644 --- a/types/wlr_text_input_v3.c +++ b/types/wlr_text_input_v3.c @@ -64,7 +64,7 @@ void wlr_text_input_v3_send_done(struct wlr_text_input_v3 *text_input) { } static void wlr_text_input_destroy(struct wlr_text_input_v3 *text_input) { - wl_signal_emit_mutable(&text_input->events.destroy, text_input); + wl_signal_emit_mutable(&text_input->events.destroy, NULL); assert(wl_list_empty(&text_input->events.enable.listener_list)); assert(wl_list_empty(&text_input->events.commit.listener_list)); @@ -192,12 +192,12 @@ static void text_input_commit(struct wl_client *client, if (!old_enabled && text_input->current_enabled) { text_input->active_features = text_input->current.features; - wl_signal_emit_mutable(&text_input->events.enable, text_input); + wl_signal_emit_mutable(&text_input->events.enable, NULL); } else if (old_enabled && !text_input->current_enabled) { text_input->active_features = 0; - wl_signal_emit_mutable(&text_input->events.disable, text_input); + wl_signal_emit_mutable(&text_input->events.disable, NULL); } else { // including never enabled - wl_signal_emit_mutable(&text_input->events.commit, text_input); + wl_signal_emit_mutable(&text_input->events.commit, NULL); } } @@ -313,7 +313,7 @@ static void text_input_manager_bind(struct wl_client *wl_client, void *data, static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_text_input_manager_v3 *manager = wl_container_of(listener, manager, display_destroy); - wl_signal_emit_mutable(&manager->events.destroy, manager); + wl_signal_emit_mutable(&manager->events.destroy, NULL); assert(wl_list_empty(&manager->events.new_text_input.listener_list)); assert(wl_list_empty(&manager->events.destroy.listener_list)); From 170f7e070603f0ecdadc4527c65bc08b62073e58 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Tue, 20 May 2025 01:14:16 +0900 Subject: [PATCH 369/519] backend/libinput: don't leak udev_device --- backend/libinput/tablet_pad.c | 1 + backend/libinput/tablet_tool.c | 1 + 2 files changed, 2 insertions(+) diff --git a/backend/libinput/tablet_pad.c b/backend/libinput/tablet_pad.c index 98b3e4f4b..2fbfb6a6c 100644 --- a/backend/libinput/tablet_pad.c +++ b/backend/libinput/tablet_pad.c @@ -104,6 +104,7 @@ void init_device_tablet_pad(struct wlr_libinput_input_device *dev) { struct udev_device *udev = libinput_device_get_udev_device(handle); char **dst = wl_array_add(&wlr_tablet_pad->paths, sizeof(char *)); *dst = strdup(udev_device_get_syspath(udev)); + udev_device_unref(udev); int groups = libinput_device_tablet_pad_get_num_mode_groups(handle); for (int i = 0; i < groups; ++i) { diff --git a/backend/libinput/tablet_tool.c b/backend/libinput/tablet_tool.c index eec697c49..d43c6cd0a 100644 --- a/backend/libinput/tablet_tool.c +++ b/backend/libinput/tablet_tool.c @@ -37,6 +37,7 @@ void init_device_tablet(struct wlr_libinput_input_device *dev) { struct udev_device *udev = libinput_device_get_udev_device(dev->handle); char **dst = wl_array_add(&wlr_tablet->paths, sizeof(char *)); *dst = strdup(udev_device_get_syspath(udev)); + udev_device_unref(udev); wl_list_init(&dev->tablet_tools); } From fae4c5097de0dd03240934a08b5845ac91b3e5e5 Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 15 May 2025 10:04:46 +0100 Subject: [PATCH 370/519] xwayland: Remove has_utf8_title field --- include/wlr/xwayland/xwayland.h | 1 - xwayland/xwm.c | 3 --- 2 files changed, 4 deletions(-) diff --git a/include/wlr/xwayland/xwayland.h b/include/wlr/xwayland/xwayland.h index 0eb466d96..92a45a493 100644 --- a/include/wlr/xwayland/xwayland.h +++ b/include/wlr/xwayland/xwayland.h @@ -146,7 +146,6 @@ struct wlr_xwayland_surface { char *role; char *startup_id; pid_t pid; - bool has_utf8_title; struct wl_list children; // wlr_xwayland_surface.parent_link struct wlr_xwayland_surface *parent; diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 8cd1d41ab..d5a57d5e5 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -774,9 +774,6 @@ static void read_surface_title(struct wlr_xwm *xwm, xsurface->title = NULL; } - // TODO: drop this field - xsurface->has_utf8_title = reply->type == xwm->atoms[UTF8_STRING]; - wl_signal_emit_mutable(&xsurface->events.set_title, NULL); } From aaeffe9769a2056ae1e8672b92add6d14df97413 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Wed, 23 Oct 2024 14:39:38 +0300 Subject: [PATCH 371/519] cursor-shape-v1: use generated enum validator --- types/wlr_cursor_shape_v1.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/types/wlr_cursor_shape_v1.c b/types/wlr_cursor_shape_v1.c index 3e657fb6d..83b829bcf 100644 --- a/types/wlr_cursor_shape_v1.c +++ b/types/wlr_cursor_shape_v1.c @@ -46,7 +46,8 @@ static void device_handle_set_shape(struct wl_client *client, struct wl_resource return; } - if (shape == 0 || shape > WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ZOOM_OUT) { + uint32_t version = wl_resource_get_version(device_resource); + if (!wp_cursor_shape_device_v1_shape_is_valid(shape, version)) { wl_resource_post_error(device_resource, WP_CURSOR_SHAPE_DEVICE_V1_ERROR_INVALID_SHAPE, "Invalid shape %"PRIu32, shape); return; From af43d3b9e7017d915dffe7bdeccea3adb8155774 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Wed, 23 Oct 2024 14:51:29 +0300 Subject: [PATCH 372/519] cursor-shape-v1: bump to version 2 --- protocol/meson.build | 2 +- types/wlr_cursor_shape_v1.c | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/protocol/meson.build b/protocol/meson.build index 2a04a0377..d092687bc 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -1,5 +1,5 @@ wayland_protos = dependency('wayland-protocols', - version: '>=1.41', + version: '>=1.43', fallback: 'wayland-protocols', default_options: ['tests=false'], ) diff --git a/types/wlr_cursor_shape_v1.c b/types/wlr_cursor_shape_v1.c index 83b829bcf..b8ae07136 100644 --- a/types/wlr_cursor_shape_v1.c +++ b/types/wlr_cursor_shape_v1.c @@ -7,7 +7,7 @@ #include #include "types/wlr_tablet_v2.h" -#define CURSOR_SHAPE_MANAGER_V1_VERSION 1 +#define CURSOR_SHAPE_MANAGER_V1_VERSION 2 struct wlr_cursor_shape_device_v1 { struct wl_resource *resource; @@ -257,6 +257,8 @@ static const char *const shape_names[] = { [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ALL_SCROLL] = "all-scroll", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ZOOM_IN] = "zoom-in", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ZOOM_OUT] = "zoom-out", + [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DND_ASK] = "dnd-ask", + [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ALL_RESIZE] = "all-resize", }; const char *wlr_cursor_shape_v1_name(enum wp_cursor_shape_device_v1_shape shape) { From a08acfcee0261ae9b084c217dd70dd52eea2904a Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Thu, 29 May 2025 17:38:32 +0800 Subject: [PATCH 373/519] render/pass: Ensure the precision is consistent during comparison --- render/pass.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/render/pass.c b/render/pass.c index 902228273..23bdf96dd 100644 --- a/render/pass.c +++ b/render/pass.c @@ -21,8 +21,8 @@ void wlr_render_pass_add_texture(struct wlr_render_pass *render_pass, if (!wlr_fbox_empty(&options->src_box)) { const struct wlr_fbox *box = &options->src_box; assert(box->x >= 0 && box->y >= 0 && - box->x + box->width <= options->texture->width && - box->y + box->height <= options->texture->height); + (uint32_t)(box->x + box->width) <= options->texture->width && + (uint32_t)(box->y + box->height) <= options->texture->height); } render_pass->impl->add_texture(render_pass, options); From afe427d149e701fb244152e1b44ebaa34e3a4cb0 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Thu, 9 May 2024 00:22:15 +0300 Subject: [PATCH 374/519] xdg-shell: add support for v7 --- include/wlr/types/wlr_xdg_shell.h | 10 ++++++++++ types/xdg_shell/wlr_xdg_shell.c | 2 +- types/xdg_shell/wlr_xdg_toplevel.c | 30 ++++++++++++++++++++++++++++-- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/include/wlr/types/wlr_xdg_shell.h b/include/wlr/types/wlr_xdg_shell.h index 3e9fd2b70..a835bb107 100644 --- a/include/wlr/types/wlr_xdg_shell.h +++ b/include/wlr/types/wlr_xdg_shell.h @@ -141,6 +141,7 @@ enum wlr_xdg_surface_role { struct wlr_xdg_toplevel_state { bool maximized, fullscreen, resizing, activated, suspended; uint32_t tiled; // enum wlr_edges + uint32_t constrained; // enum wlr_edges int32_t width, height; int32_t max_width, max_height; int32_t min_width, min_height; @@ -168,6 +169,7 @@ struct wlr_xdg_toplevel_configure { // The following fields must always be set to reflect the current state bool maximized, fullscreen, resizing, activated, suspended; uint32_t tiled; // enum wlr_edges + uint32_t constrained; // enum wlr_edges int32_t width, height; // Only for WLR_XDG_TOPLEVEL_CONFIGURE_BOUNDS @@ -454,6 +456,14 @@ uint32_t wlr_xdg_toplevel_set_wm_capabilities(struct wlr_xdg_toplevel *toplevel, uint32_t wlr_xdg_toplevel_set_suspended(struct wlr_xdg_toplevel *toplevel, bool suspended); +/** + * Request that this toplevel consider itself constrained and doesn't attempt to + * resize from some edges. `constrained_edges` is a bitfield of enum wlr_edges. + * Returns the associated configure serial. + */ +uint32_t wlr_xdg_toplevel_set_constrained(struct wlr_xdg_toplevel *toplevel, + uint32_t constrained_edges); + /** * Request that this toplevel closes. */ diff --git a/types/xdg_shell/wlr_xdg_shell.c b/types/xdg_shell/wlr_xdg_shell.c index 0cbadc271..2f048ac2c 100644 --- a/types/xdg_shell/wlr_xdg_shell.c +++ b/types/xdg_shell/wlr_xdg_shell.c @@ -2,7 +2,7 @@ #include #include "types/wlr_xdg_shell.h" -#define WM_BASE_VERSION 6 +#define WM_BASE_VERSION 7 static const struct xdg_wm_base_interface xdg_shell_impl; diff --git a/types/xdg_shell/wlr_xdg_toplevel.c b/types/xdg_shell/wlr_xdg_toplevel.c index 950a22b2d..1b33916e7 100644 --- a/types/xdg_shell/wlr_xdg_toplevel.c +++ b/types/xdg_shell/wlr_xdg_toplevel.c @@ -15,6 +15,7 @@ void handle_xdg_toplevel_ack_configure( toplevel->pending.resizing = configure->resizing; toplevel->pending.activated = configure->activated; toplevel->pending.tiled = configure->tiled; + toplevel->pending.constrained = configure->constrained; toplevel->pending.suspended = configure->suspended; toplevel->pending.width = configure->width; @@ -80,7 +81,7 @@ struct wlr_xdg_toplevel_configure *send_xdg_toplevel_configure( states[nstates++] = XDG_TOPLEVEL_STATE_ACTIVATED; } if (configure->tiled && version >= XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION) { - const struct { + static const struct { enum wlr_edges edge; enum xdg_toplevel_state state; } tiled[] = { @@ -90,7 +91,7 @@ struct wlr_xdg_toplevel_configure *send_xdg_toplevel_configure( { WLR_EDGE_BOTTOM, XDG_TOPLEVEL_STATE_TILED_BOTTOM }, }; - for (size_t i = 0; i < sizeof(tiled)/sizeof(tiled[0]); ++i) { + for (size_t i = 0; i < sizeof(tiled) / sizeof(tiled[0]); ++i) { if ((configure->tiled & tiled[i].edge) == 0) { continue; } @@ -100,6 +101,24 @@ struct wlr_xdg_toplevel_configure *send_xdg_toplevel_configure( if (configure->suspended && version >= XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION) { states[nstates++] = XDG_TOPLEVEL_STATE_SUSPENDED; } + if (configure->constrained && version >= XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT_SINCE_VERSION) { + static const struct { + enum wlr_edges edge; + enum xdg_toplevel_state state; + } constrained[] = { + { WLR_EDGE_LEFT, XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT }, + { WLR_EDGE_RIGHT, XDG_TOPLEVEL_STATE_CONSTRAINED_RIGHT }, + { WLR_EDGE_TOP, XDG_TOPLEVEL_STATE_CONSTRAINED_TOP }, + { WLR_EDGE_BOTTOM, XDG_TOPLEVEL_STATE_CONSTRAINED_BOTTOM }, + }; + + for (size_t i = 0; i < sizeof(constrained) / sizeof(constrained[0]); ++i) { + if ((configure->constrained & constrained[i].edge) == 0) { + continue; + } + states[nstates++] = constrained[i].state; + } + } assert(nstates <= sizeof(states) / sizeof(states[0])); int32_t width = configure->width; @@ -638,3 +657,10 @@ uint32_t wlr_xdg_toplevel_set_suspended(struct wlr_xdg_toplevel *toplevel, toplevel->scheduled.suspended = suspended; return wlr_xdg_surface_schedule_configure(toplevel->base); } + +uint32_t wlr_xdg_toplevel_set_constrained(struct wlr_xdg_toplevel *toplevel, uint32_t constrained) { + assert(toplevel->base->client->shell->version >= + XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT_SINCE_VERSION); + toplevel->scheduled.constrained = constrained; + return wlr_xdg_surface_schedule_configure(toplevel->base); +} From 83a5bdf5d546bafdcff57dadd3372b49a8c38708 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 23 May 2025 18:03:57 +0200 Subject: [PATCH 375/519] xwayland: Create a dummy no_focus_window to use for non-X window focus --- include/xwayland/xwm.h | 1 + xwayland/xwm.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h index 799cebc47..d38e5cc91 100644 --- a/include/xwayland/xwm.h +++ b/include/xwayland/xwm.h @@ -115,6 +115,7 @@ struct wlr_xwm { xcb_connection_t *xcb_conn; xcb_screen_t *screen; xcb_window_t window; + xcb_window_t no_focus_window; xcb_visualid_t visual_id; xcb_colormap_t colormap; xcb_render_pictformat_t render_format_id; diff --git a/xwayland/xwm.c b/xwayland/xwm.c index d5a57d5e5..953906fa8 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -2204,6 +2204,9 @@ void xwm_destroy(struct wlr_xwm *xwm) { if (xwm->colormap) { xcb_free_colormap(xwm->xcb_conn, xwm->colormap); } + if (xwm->no_focus_window) { + xcb_destroy_window(xwm->xcb_conn, xwm->no_focus_window); + } if (xwm->window) { xcb_destroy_window(xwm->xcb_conn, xwm->window); } @@ -2361,6 +2364,31 @@ static void xwm_create_wm_window(struct wlr_xwm *xwm) { XCB_CURRENT_TIME); } +static void xwm_create_no_focus_window(struct wlr_xwm *xwm) { + xwm->no_focus_window = xcb_generate_id(xwm->xcb_conn); + + uint32_t values[2] = { + 1, + XCB_EVENT_MASK_KEY_PRESS | + XCB_EVENT_MASK_KEY_RELEASE | + XCB_EVENT_MASK_FOCUS_CHANGE + }; + xcb_create_window(xwm->xcb_conn, + XCB_COPY_FROM_PARENT, + xwm->no_focus_window, + xwm->screen->root, + -100, -100, + 1, 1, + 0, + XCB_WINDOW_CLASS_COPY_FROM_PARENT, + XCB_COPY_FROM_PARENT, + XCB_CW_OVERRIDE_REDIRECT | + XCB_CW_EVENT_MASK, + values); + + xcb_map_window(xwm->xcb_conn, xwm->no_focus_window); +} + // TODO use me to support 32 bit color somehow static void xwm_get_visual_and_colormap(struct wlr_xwm *xwm) { xcb_depth_iterator_t d_iter; @@ -2587,6 +2615,7 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) { &xwm->shell_v1_destroy); xwm_create_wm_window(xwm); + xwm_create_no_focus_window(xwm); xcb_flush(xwm->xcb_conn); From 6c782251608e4f399d115af0d5c302f192e31df8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 22 May 2025 11:43:18 +0200 Subject: [PATCH 376/519] xwayland: Activate no_focus_window when a Wayland window is activated None active window might be interpreted from an X point of view as a transient focus state, and is used by multiple X window managers when a temporary focus change is in progress, or simply when grabbing the keyboard. From Wine side, we translate any active window change to the Win32 application, and handling None active window as an actual window deactivation and focus loss creates spurious events and an undesired feedback loop, as apps might react to it. We still want to be able to detect actual focus loss under an XWayland session, and having XWayland window manager focus an actual X window instead will make the distinction possible. --- xwayland/xwm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 953906fa8..0225a0d05 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -441,7 +441,7 @@ static void xwm_set_focused_window(struct wlr_xwm *xwm, xsurface_set_net_wm_state(xsurface); xwm_set_net_active_window(xwm, xsurface->window_id); } else { - xwm_set_net_active_window(xwm, XCB_WINDOW_NONE); + xwm_set_net_active_window(xwm, xwm->no_focus_window); } } From 37992cf3b89bc603f3073ced116a692860fd3992 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 5 Jun 2025 18:17:56 +0200 Subject: [PATCH 377/519] idle_notify_v1: drop trailing spaces --- types/wlr_idle_notify_v1.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/types/wlr_idle_notify_v1.c b/types/wlr_idle_notify_v1.c index b4b9afcc1..0488225a8 100644 --- a/types/wlr_idle_notify_v1.c +++ b/types/wlr_idle_notify_v1.c @@ -82,8 +82,7 @@ static void notification_destroy(struct wlr_idle_notification_v1 *notification) } static void notification_reset_timer(struct wlr_idle_notification_v1 *notification) { - if (notification->notifier->inhibited && - notification->obey_inhibitors) { + if (notification->notifier->inhibited && notification->obey_inhibitors) { notification_set_idle(notification, false); if (notification->timer != NULL) { wl_event_source_timer_update(notification->timer, 0); @@ -177,7 +176,7 @@ static void notifier_handle_get_input_idle_notification( struct wl_client *client, struct wl_resource *notifier_resource, uint32_t id, uint32_t timeout, struct wl_resource *seat_resource) { - construct_idle_notification(client, notifier_resource, id, + construct_idle_notification(client, notifier_resource, id, timeout, seat_resource, false); } @@ -191,10 +190,8 @@ static void notifier_handle_get_idle_notification( static const struct ext_idle_notifier_v1_interface notifier_impl = { .destroy = resource_handle_destroy, - .get_idle_notification = - notifier_handle_get_idle_notification, - .get_input_idle_notification = - notifier_handle_get_input_idle_notification, + .get_idle_notification = notifier_handle_get_idle_notification, + .get_input_idle_notification = notifier_handle_get_input_idle_notification, }; static void notifier_bind(struct wl_client *client, void *data, @@ -257,12 +254,9 @@ void wlr_idle_notifier_v1_set_inhibited(struct wlr_idle_notifier_v1 *notifier, void wlr_idle_notifier_v1_notify_activity(struct wlr_idle_notifier_v1 *notifier, struct wlr_seat *seat) { - struct wlr_idle_notification_v1 *notification; wl_list_for_each(notification, ¬ifier->notifications, link) { - if (notification->seat == seat && - !(notifier->inhibited && - notification->obey_inhibitors)) { + if (notification->seat == seat && !(notifier->inhibited && notification->obey_inhibitors)) { notification_handle_activity(notification); } } From 221b37355f0e57175dd881f154a877aaddab0080 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 1 May 2025 18:32:19 +0200 Subject: [PATCH 378/519] xwayland: require xcb-xfixes 1.15 xcb-xfixes 1.15 was released back in 2022. Let's simplify our build setup by requiring it. --- xwayland/meson.build | 24 +++++++++++------------- xwayland/xwayland.c | 2 -- xwayland/xwm.c | 3 --- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/xwayland/meson.build b/xwayland/meson.build index 81cb018eb..3d29dc6c5 100644 --- a/xwayland/meson.build +++ b/xwayland/meson.build @@ -1,13 +1,13 @@ xwayland_libs = [] -xwayland_required = [ - 'xcb', - 'xcb-composite', - 'xcb-ewmh', - 'xcb-icccm', - 'xcb-render', - 'xcb-res', - 'xcb-xfixes', -] +xwayland_required = { + 'xcb': [], + 'xcb-composite': [], + 'xcb-ewmh': [], + 'xcb-icccm': [], + 'xcb-render': [], + 'xcb-res': [], + 'xcb-xfixes': '>=1.15', +} xwayland_optional = { 'xcb-errors': 'Required for printing X11 errors.', } @@ -37,8 +37,9 @@ if not xwayland.found() subdir_done() endif -foreach lib : xwayland_required +foreach lib, version : xwayland_required dep = dependency(lib, + version: version, required: get_option('xwayland'), not_found_message: '\n'.join(msg).format(lib), ) @@ -93,6 +94,3 @@ wlr_files += files( ) wlr_deps += xwayland_libs features += { 'xwayland': true } - -have = cc.has_function('xcb_xfixes_set_client_disconnect_mode', dependencies: xwayland_libs) -internal_config.set10('HAVE_XCB_XFIXES_SET_CLIENT_DISCONNECT_MODE', have) diff --git a/xwayland/xwayland.c b/xwayland/xwayland.c index d3b2a6c84..d25a132b5 100644 --- a/xwayland/xwayland.c +++ b/xwayland/xwayland.c @@ -145,9 +145,7 @@ struct wlr_xwayland *wlr_xwayland_create(struct wl_display *wl_display, struct wlr_xwayland_server_options options = { .lazy = lazy, .enable_wm = true, -#if HAVE_XCB_XFIXES_SET_CLIENT_DISCONNECT_MODE .terminate_delay = lazy ? 10 : 0, -#endif }; struct wlr_xwayland_server *server = wlr_xwayland_server_create(wl_display, &options); if (server == NULL) { diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 0225a0d05..bcdcd218c 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -1,7 +1,6 @@ #include #include #include -#include #include #include #include @@ -2584,13 +2583,11 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) { sizeof(supported)/sizeof(*supported), supported); -#if HAVE_XCB_XFIXES_SET_CLIENT_DISCONNECT_MODE if (xwm->xwayland->server->options.terminate_delay > 0 && xwm->xfixes_major_version >= 6) { xcb_xfixes_set_client_disconnect_mode(xwm->xcb_conn, XCB_XFIXES_CLIENT_DISCONNECT_FLAGS_TERMINATE); } -#endif xcb_flush(xwm->xcb_conn); From bb50c7a5a4244ba70c38c99f92f86766359f69f9 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 1 May 2025 18:42:26 +0200 Subject: [PATCH 379/519] render/allocator/gbm: require GBM 21.1 Mesa 21.1 was released back in 2021. Let's require it so that we can simplify our build and remove the workaround. --- render/allocator/gbm.c | 29 ----------------------------- render/allocator/meson.build | 5 +---- 2 files changed, 1 insertion(+), 33 deletions(-) diff --git a/render/allocator/gbm.c b/render/allocator/gbm.c index 3e157bb69..ca16177d1 100644 --- a/render/allocator/gbm.c +++ b/render/allocator/gbm.c @@ -9,7 +9,6 @@ #include #include -#include "config.h" #include "render/allocator/gbm.h" #include "render/drm_format_set.h" @@ -39,40 +38,12 @@ static bool export_gbm_bo(struct gbm_bo *bo, attribs.modifier = gbm_bo_get_modifier(bo); int i; - int32_t handle = -1; for (i = 0; i < attribs.n_planes; ++i) { -#if HAVE_GBM_BO_GET_FD_FOR_PLANE - (void)handle; - attribs.fd[i] = gbm_bo_get_fd_for_plane(bo, i); if (attribs.fd[i] < 0) { wlr_log(WLR_ERROR, "gbm_bo_get_fd_for_plane failed"); goto error_fd; } -#else - // GBM is lacking a function to get a FD for a given plane. Instead, - // check all planes have the same handle. We can't use - // drmPrimeHandleToFD because that messes up handle ref'counting in - // the user-space driver. - union gbm_bo_handle plane_handle = gbm_bo_get_handle_for_plane(bo, i); - if (plane_handle.s32 < 0) { - wlr_log(WLR_ERROR, "gbm_bo_get_handle_for_plane failed"); - goto error_fd; - } - if (i == 0) { - handle = plane_handle.s32; - } else if (plane_handle.s32 != handle) { - wlr_log(WLR_ERROR, "Failed to export GBM BO: " - "all planes don't have the same GEM handle"); - goto error_fd; - } - - attribs.fd[i] = gbm_bo_get_fd(bo); - if (attribs.fd[i] < 0) { - wlr_log(WLR_ERROR, "gbm_bo_get_fd failed"); - goto error_fd; - } -#endif attribs.offset[i] = gbm_bo_get_offset(bo, i); attribs.stride[i] = gbm_bo_get_stride_for_plane(bo, i); diff --git a/render/allocator/meson.build b/render/allocator/meson.build index c7559b967..b812f6941 100644 --- a/render/allocator/meson.build +++ b/render/allocator/meson.build @@ -13,15 +13,12 @@ wlr_files += files( gbm = disabler() if 'gbm' in allocators or 'auto' in allocators - gbm = dependency('gbm', version: '>=17.1.0', required: 'gbm' in allocators) + gbm = dependency('gbm', version: '>=21.1', required: 'gbm' in allocators) endif if gbm.found() wlr_files += files('gbm.c') wlr_deps += gbm features += { 'gbm-allocator': true } - - has = cc.has_function('gbm_bo_get_fd_for_plane', dependencies: [gbm]) - internal_config.set10('HAVE_GBM_BO_GET_FD_FOR_PLANE', has) endif udmabuf = false From 8fb4e4dabb1526b939003345dab2a9744018b056 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 30 May 2025 23:10:39 +0200 Subject: [PATCH 380/519] swapchain: assert that size is not empty at creation time Failing later (at buffer allocation time) makes it more difficult to track down where the issue comes from. --- render/swapchain.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/render/swapchain.c b/render/swapchain.c index e87a99ead..24d6f1a87 100644 --- a/render/swapchain.c +++ b/render/swapchain.c @@ -18,6 +18,8 @@ static void swapchain_handle_allocator_destroy(struct wl_listener *listener, struct wlr_swapchain *wlr_swapchain_create( struct wlr_allocator *alloc, int width, int height, const struct wlr_drm_format *format) { + assert(width > 0 && height > 0); + struct wlr_swapchain *swapchain = calloc(1, sizeof(*swapchain)); if (swapchain == NULL) { return NULL; From b066fd6b5a80a20c7c2ec35dbfb8d534b301a280 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 25 May 2025 16:35:52 +0200 Subject: [PATCH 381/519] ext_image_capture_source_v1: add support for foreign toplevels --- .../types/wlr_ext_image_capture_source_v1.h | 32 +++++ .../foreign_toplevel.c | 115 ++++++++++++++++++ types/meson.build | 1 + 3 files changed, 148 insertions(+) create mode 100644 types/ext_image_capture_source_v1/foreign_toplevel.c diff --git a/include/wlr/types/wlr_ext_image_capture_source_v1.h b/include/wlr/types/wlr_ext_image_capture_source_v1.h index 8d80a9e3c..047e73df6 100644 --- a/include/wlr/types/wlr_ext_image_capture_source_v1.h +++ b/include/wlr/types/wlr_ext_image_capture_source_v1.h @@ -79,6 +79,31 @@ struct wlr_ext_output_image_capture_source_manager_v1 { } WLR_PRIVATE; }; +/** + * Interface exposing one screen capture source per foreign toplevel. + */ +struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1 { + struct wl_global *global; + + struct { + struct wl_signal destroy; + struct wl_signal new_request; // struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request + } events; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; +}; + +struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request { + struct wlr_ext_foreign_toplevel_handle_v1 *toplevel_handle; + struct wl_client *client; + + struct { + uint32_t new_id; + } WLR_PRIVATE; +}; + /** * Obtain a struct wlr_ext_image_capture_source_v1 from an ext_image_capture_source_v1 * resource. @@ -91,4 +116,11 @@ struct wlr_ext_image_capture_source_v1 *wlr_ext_image_capture_source_v1_from_res struct wlr_ext_output_image_capture_source_manager_v1 *wlr_ext_output_image_capture_source_manager_v1_create( struct wl_display *display, uint32_t version); +struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1 * +wlr_ext_foreign_toplevel_image_capture_source_manager_v1_create(struct wl_display *display, uint32_t version); + +bool wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request_accept( + struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request *request, + struct wlr_ext_image_capture_source_v1 *source); + #endif diff --git a/types/ext_image_capture_source_v1/foreign_toplevel.c b/types/ext_image_capture_source_v1/foreign_toplevel.c new file mode 100644 index 000000000..c79156bce --- /dev/null +++ b/types/ext_image_capture_source_v1/foreign_toplevel.c @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include "ext-image-capture-source-v1-protocol.h" + +#define FOREIGN_TOPLEVEL_IMAGE_SOURCE_MANAGER_V1_VERSION 1 + +struct wlr_ext_foreign_toplevel_image_capture_source_v1 { + struct wlr_ext_image_capture_source_v1 base; +}; + +static const struct ext_foreign_toplevel_image_capture_source_manager_v1_interface foreign_toplevel_manager_impl; + +static struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1 * +foreign_toplevel_manager_from_resource(struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &ext_foreign_toplevel_image_capture_source_manager_v1_interface, + &foreign_toplevel_manager_impl)); + return wl_resource_get_user_data(resource); +} + +bool wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request_accept( + struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request *request, + struct wlr_ext_image_capture_source_v1 *source) { + return wlr_ext_image_capture_source_v1_create_resource(source, request->client, request->new_id); +} + +static void foreign_toplevel_manager_handle_create_source(struct wl_client *client, + struct wl_resource *manager_resource, uint32_t new_id, + struct wl_resource *foreign_toplevel_resource) { + struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1 *manager = + foreign_toplevel_manager_from_resource(manager_resource); + struct wlr_ext_foreign_toplevel_handle_v1 *toplevel_handle = + wlr_ext_foreign_toplevel_handle_v1_from_resource(foreign_toplevel_resource); + if (toplevel_handle == NULL) { + wlr_ext_image_capture_source_v1_create_resource(NULL, client, new_id); + return; + } + + struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request *request = + calloc(1, sizeof(*request)); + if (request == NULL) { + wl_resource_post_no_memory(manager_resource); + return; + } + + request->toplevel_handle = toplevel_handle; + request->client = client; + request->new_id = new_id; + + wl_signal_emit_mutable(&manager->events.new_request, request); +} + +static void foreign_toplevel_manager_handle_destroy(struct wl_client *client, + struct wl_resource *manager_resource) { + wl_resource_destroy(manager_resource); +} + +static const struct ext_foreign_toplevel_image_capture_source_manager_v1_interface foreign_toplevel_manager_impl = { + .create_source = foreign_toplevel_manager_handle_create_source, + .destroy = foreign_toplevel_manager_handle_destroy, +}; + +static void foreign_toplevel_manager_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id) { + struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1 *manager = data; + + struct wl_resource *resource = wl_resource_create(client, + &ext_foreign_toplevel_image_capture_source_manager_v1_interface, version, id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &foreign_toplevel_manager_impl, manager, NULL); +} + +static void foreign_toplevel_manager_handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1 *manager = + wl_container_of(listener, manager, display_destroy); + wl_signal_emit_mutable(&manager->events.destroy, NULL); + assert(wl_list_empty(&manager->events.destroy.listener_list)); + assert(wl_list_empty(&manager->events.new_request.listener_list)); + wl_list_remove(&manager->display_destroy.link); + wl_global_destroy(manager->global); + free(manager); +} + +struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1 * +wlr_ext_foreign_toplevel_image_capture_source_manager_v1_create(struct wl_display *display, + uint32_t version) { + assert(version <= FOREIGN_TOPLEVEL_IMAGE_SOURCE_MANAGER_V1_VERSION); + + struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1 *manager = + calloc(1, sizeof(*manager)); + if (manager == NULL) { + return NULL; + } + + manager->global = wl_global_create(display, + &ext_foreign_toplevel_image_capture_source_manager_v1_interface, + version, manager, foreign_toplevel_manager_bind); + if (manager->global == NULL) { + free(manager); + return NULL; + } + + wl_signal_init(&manager->events.destroy); + wl_signal_init(&manager->events.new_request); + + manager->display_destroy.notify = foreign_toplevel_manager_handle_display_destroy; + wl_display_add_destroy_listener(display, &manager->display_destroy); + + return manager; +} diff --git a/types/meson.build b/types/meson.build index 831698eb9..b08a566a4 100644 --- a/types/meson.build +++ b/types/meson.build @@ -5,6 +5,7 @@ wlr_files += files( 'data_device/wlr_drag.c', 'ext_image_capture_source_v1/base.c', 'ext_image_capture_source_v1/output.c', + 'ext_image_capture_source_v1/foreign_toplevel.c', 'output/cursor.c', 'output/output.c', 'output/render.c', From da820070f4cdb15c53e5a54177cc6c2689d40438 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 25 May 2025 19:33:57 +0200 Subject: [PATCH 382/519] ext_image_capture_source_v1: add helper to capture scene nodes --- include/types/wlr_scene.h | 2 + .../types/wlr_ext_image_capture_source_v1.h | 8 + types/ext_image_capture_source_v1/scene.c | 325 ++++++++++++++++++ types/meson.build | 1 + types/scene/wlr_scene.c | 5 +- 5 files changed, 337 insertions(+), 4 deletions(-) create mode 100644 types/ext_image_capture_source_v1/scene.c diff --git a/include/types/wlr_scene.h b/include/types/wlr_scene.h index 80dcfd1bd..c4b40cdd0 100644 --- a/include/types/wlr_scene.h +++ b/include/types/wlr_scene.h @@ -5,6 +5,8 @@ struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node); +void scene_node_get_size(struct wlr_scene_node *node, int *width, int *height); + void scene_surface_set_clip(struct wlr_scene_surface *surface, struct wlr_box *clip); #endif diff --git a/include/wlr/types/wlr_ext_image_capture_source_v1.h b/include/wlr/types/wlr_ext_image_capture_source_v1.h index 047e73df6..e38f68795 100644 --- a/include/wlr/types/wlr_ext_image_capture_source_v1.h +++ b/include/wlr/types/wlr_ext_image_capture_source_v1.h @@ -13,6 +13,10 @@ #include #include +struct wlr_scene_node; +struct wlr_allocator; +struct wlr_renderer; + /** * A screen capture source. * @@ -123,4 +127,8 @@ bool wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request_accept( struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request *request, struct wlr_ext_image_capture_source_v1 *source); +struct wlr_ext_image_capture_source_v1 *wlr_ext_image_capture_source_v1_create_with_scene_node( + struct wlr_scene_node *node, struct wl_event_loop *event_loop, + struct wlr_allocator *allocator, struct wlr_renderer *renderer); + #endif diff --git a/types/ext_image_capture_source_v1/scene.c b/types/ext_image_capture_source_v1/scene.c new file mode 100644 index 000000000..a8bce9d3d --- /dev/null +++ b/types/ext_image_capture_source_v1/scene.c @@ -0,0 +1,325 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "types/wlr_output.h" +#include "types/wlr_scene.h" + +struct scene_node_source { + struct wlr_ext_image_capture_source_v1 base; + + struct wlr_scene_node *node; + + struct wlr_backend backend; + struct wlr_output output; + struct wlr_scene_output *scene_output; + + struct wl_event_source *idle_frame; + + struct wl_listener node_destroy; + struct wl_listener scene_output_destroy; + struct wl_listener output_frame; +}; + +struct scene_node_source_frame_event { + struct wlr_ext_image_capture_source_v1_frame_event base; + struct wlr_buffer *buffer; + struct timespec when; +}; + +static size_t last_output_num = 0; + +static void _get_scene_node_extents(struct wlr_scene_node *node, struct wlr_box *box, int lx, int ly) { + switch (node->type) { + case WLR_SCENE_NODE_TREE:; + struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); + struct wlr_scene_node *child; + wl_list_for_each(child, &scene_tree->children, link) { + _get_scene_node_extents(child, box, lx + child->x, ly + child->y); + } + break; + case WLR_SCENE_NODE_RECT: + case WLR_SCENE_NODE_BUFFER:; + struct wlr_box node_box = { .x = lx, .y = ly }; + scene_node_get_size(node, &node_box.width, &node_box.height); + + if (node_box.x < box->x) { + box->x = node_box.x; + } + if (node_box.y < box->y) { + box->y = node_box.y; + } + if (node_box.x + node_box.width > box->x + box->width) { + box->width = node_box.x + node_box.width - box->x; + } + if (node_box.y + node_box.height > box->y + box->height) { + box->height = node_box.y + node_box.height - box->y; + } + break; + } +} + +static void get_scene_node_extents(struct wlr_scene_node *node, struct wlr_box *box) { + *box = (struct wlr_box){ .x = INT_MAX, .y = INT_MAX }; + int lx = 0, ly = 0; + wlr_scene_node_coords(node, &lx, &ly); + _get_scene_node_extents(node, box, lx, ly); +} + +static void source_render(struct scene_node_source *source) { + struct wlr_scene_output *scene_output = source->scene_output; + + struct wlr_box extents; + get_scene_node_extents(source->node, &extents); + + if (extents.width == 0 || extents.height == 0) { + return; + } + + wlr_scene_output_set_position(scene_output, extents.x, extents.y); + + struct wlr_output_state state; + wlr_output_state_init(&state); + wlr_output_state_set_enabled(&state, true); + wlr_output_state_set_custom_mode(&state, extents.width, extents.height, 0); + bool ok = wlr_scene_output_build_state(scene_output, &state, NULL) && + wlr_output_commit_state(scene_output->output, &state); + wlr_output_state_finish(&state); + + if (!ok) { + // TODO: send failure + return; + } + + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + wlr_scene_output_send_frame_done(scene_output, &now); +} + +static void source_start(struct wlr_ext_image_capture_source_v1 *base, bool with_cursors) { + struct scene_node_source *source = wl_container_of(base, source, base); + source_render(source); +} + +static void source_stop(struct wlr_ext_image_capture_source_v1 *base) { + struct scene_node_source *source = wl_container_of(base, source, base); + + struct wlr_output_state state; + wlr_output_state_init(&state); + wlr_output_state_set_enabled(&state, false); + wlr_output_commit_state(&source->output, &state); + wlr_output_state_finish(&state); +} + +static void source_schedule_frame(struct wlr_ext_image_capture_source_v1 *base) { + struct scene_node_source *source = wl_container_of(base, source, base); + wlr_output_update_needs_frame(&source->output); +} + +static void source_copy_frame(struct wlr_ext_image_capture_source_v1 *base, + struct wlr_ext_image_copy_capture_frame_v1 *frame, + struct wlr_ext_image_capture_source_v1_frame_event *base_event) { + struct scene_node_source *source = wl_container_of(base, source, base); + struct scene_node_source_frame_event *event = wl_container_of(base_event, event, base); + + if (wlr_ext_image_copy_capture_frame_v1_copy_buffer(frame, + event->buffer, source->output.renderer)) { + wlr_ext_image_copy_capture_frame_v1_ready(frame, + source->output.transform, &event->when); + } +} + +static const struct wlr_ext_image_capture_source_v1_interface source_impl = { + .start = source_start, + .stop = source_stop, + .schedule_frame = source_schedule_frame, + .copy_frame = source_copy_frame, +}; + +static const struct wlr_backend_impl backend_impl = {0}; + +static void source_update_buffer_constraints(struct scene_node_source *source, + const struct wlr_output_state *state) { + struct wlr_output *output = &source->output; + + if (!wlr_output_configure_primary_swapchain(output, state, &output->swapchain)) { + return; + } + + wlr_ext_image_capture_source_v1_set_constraints_from_swapchain(&source->base, + output->swapchain, output->renderer); +} + +static void source_handle_idle_frame(void *data) { + struct scene_node_source *source = data; + source->idle_frame = NULL; + wlr_output_send_frame(&source->output); +} + +static bool output_test(struct wlr_output *output, const struct wlr_output_state *state) { + struct scene_node_source *source = wl_container_of(output, source, output); + + uint32_t supported = + WLR_OUTPUT_STATE_BACKEND_OPTIONAL | + WLR_OUTPUT_STATE_BUFFER | + WLR_OUTPUT_STATE_ENABLED | + WLR_OUTPUT_STATE_MODE; + if ((state->committed & ~supported) != 0) { + return false; + } + + if (state->committed & WLR_OUTPUT_STATE_BUFFER) { + int pending_width, pending_height; + output_pending_resolution(output, state, + &pending_width, &pending_height); + if (state->buffer->width != pending_width || + state->buffer->height != pending_height) { + return false; + } + struct wlr_fbox src_box; + output_state_get_buffer_src_box(state, &src_box); + if (src_box.x != 0.0 || src_box.y != 0.0 || + src_box.width != (double)state->buffer->width || + src_box.height != (double)state->buffer->height) { + return false; + } + } + + return true; +} + +static bool output_commit(struct wlr_output *output, const struct wlr_output_state *state) { + struct scene_node_source *source = wl_container_of(output, source, output); + + if (source->idle_frame != NULL) { + wlr_log(WLR_DEBUG, "Failed to commit capture output: a frame is still pending"); + return false; + } + + if ((state->committed & WLR_OUTPUT_STATE_ENABLED) && !state->enabled) { + return true; + } + + if (state->committed & WLR_OUTPUT_STATE_MODE) { + source_update_buffer_constraints(source, state); + } + + if (!(state->committed & WLR_OUTPUT_STATE_BUFFER)) { + wlr_log(WLR_DEBUG, "Failed to commit capture output: missing buffer"); + return false; + } + + struct wlr_buffer *buffer = state->buffer; + + pixman_region32_t full_damage; + pixman_region32_init_rect(&full_damage, 0, 0, buffer->width, buffer->height); + + const pixman_region32_t *damage; + if (state->committed & WLR_OUTPUT_STATE_DAMAGE) { + damage = &state->damage; + } else { + damage = &full_damage; + } + + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + struct scene_node_source_frame_event frame_event = { + .base = { .damage = damage }, + .buffer = buffer, + .when = now, + }; + wl_signal_emit_mutable(&source->base.events.frame, &frame_event.base); + + pixman_region32_fini(&full_damage); + + source->idle_frame = + wl_event_loop_add_idle(output->event_loop, source_handle_idle_frame, source); + + return true; +} + +static const struct wlr_output_impl output_impl = { + .test = output_test, + .commit = output_commit, +}; + +static void source_destroy(struct scene_node_source *source) { + wl_list_remove(&source->node_destroy.link); + wl_list_remove(&source->scene_output_destroy.link); + wl_list_remove(&source->output_frame.link); + wlr_ext_image_capture_source_v1_finish(&source->base); + wlr_scene_output_destroy(source->scene_output); + wlr_output_finish(&source->output); + wlr_backend_finish(&source->backend); + free(source); +} + +static void source_handle_node_destroy(struct wl_listener *listener, void *data) { + struct scene_node_source *source = wl_container_of(listener, source, node_destroy); + source_destroy(source); +} + +static void source_handle_scene_output_destroy(struct wl_listener *listener, void *data) { + struct scene_node_source *source = wl_container_of(listener, source, scene_output_destroy); + source->scene_output = NULL; + wl_list_remove(&source->scene_output_destroy.link); + wl_list_init(&source->scene_output_destroy.link); +} + +static void source_handle_output_frame(struct wl_listener *listener, void *data) { + struct scene_node_source *source = wl_container_of(listener, source, output_frame); + if (source->scene_output == NULL) { + return; + } + + if (!wlr_scene_output_needs_frame(source->scene_output)) { + return; + } + + source_render(source); +} + +struct wlr_ext_image_capture_source_v1 *wlr_ext_image_capture_source_v1_create_with_scene_node( + struct wlr_scene_node *node, struct wl_event_loop *event_loop, + struct wlr_allocator *allocator, struct wlr_renderer *renderer) { + struct scene_node_source *source = calloc(1, sizeof(*source)); + if (source == NULL) { + return NULL; + } + + source->node = node; + + wlr_ext_image_capture_source_v1_init(&source->base, &source_impl); + + wlr_backend_init(&source->backend, &backend_impl); + source->backend.buffer_caps = WLR_BUFFER_CAP_DMABUF | WLR_BUFFER_CAP_SHM; + + wlr_output_init(&source->output, &source->backend, &output_impl, event_loop, NULL); + + size_t output_num = ++last_output_num; + char name[64]; + snprintf(name, sizeof(name), "CAPTURE-%zu", output_num); + wlr_output_set_name(&source->output, name); + + wlr_output_init_render(&source->output, allocator, renderer); + + struct wlr_scene *scene = scene_node_get_root(node); + source->scene_output = wlr_scene_output_create(scene, &source->output); + + source->node_destroy.notify = source_handle_node_destroy; + wl_signal_add(&node->events.destroy, &source->node_destroy); + + source->scene_output_destroy.notify = source_handle_scene_output_destroy; + wl_signal_add(&source->scene_output->events.destroy, &source->scene_output_destroy); + + source->output_frame.notify = source_handle_output_frame; + wl_signal_add(&source->output.events.frame, &source->output_frame); + + return &source->base; +} diff --git a/types/meson.build b/types/meson.build index b08a566a4..43c4eb2ad 100644 --- a/types/meson.build +++ b/types/meson.build @@ -6,6 +6,7 @@ wlr_files += files( 'ext_image_capture_source_v1/base.c', 'ext_image_capture_source_v1/output.c', 'ext_image_capture_source_v1/foreign_toplevel.c', + 'ext_image_capture_source_v1/scene.c', 'output/cursor.c', 'output/output.c', 'output/render.c', diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index e823d62ae..1dba73f07 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -210,8 +210,6 @@ struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_tree *parent) { return tree; } -static void scene_node_get_size(struct wlr_scene_node *node, int *lx, int *ly); - typedef bool (*scene_node_box_iterator_func_t)(struct wlr_scene_node *node, int sx, int sy, void *data); @@ -1120,8 +1118,7 @@ static struct wlr_texture *scene_buffer_get_texture( return texture; } -static void scene_node_get_size(struct wlr_scene_node *node, - int *width, int *height) { +void scene_node_get_size(struct wlr_scene_node *node, int *width, int *height) { *width = 0; *height = 0; From 95b2771bfd096cd8b3a3212c1ba0e1026f410cc4 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 5 Jun 2025 11:45:56 +0200 Subject: [PATCH 383/519] scene: ignore outputs with too small intersection with nodes If a node has a very small intersection with an output, there's no point in trying to adapt the node's rendering to that output. --- types/scene/wlr_scene.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 1dba73f07..256d68648 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -424,6 +424,8 @@ static void update_node_update_outputs(struct wlr_scene_node *node, size_t count = 0; uint64_t active_outputs = 0; + uint32_t visible_area = region_area(&node->visible); + // let's update the outputs in two steps: // - the primary outputs // - the enter/leave signals @@ -451,9 +453,12 @@ static void update_node_update_outputs(struct wlr_scene_node *node, pixman_region32_init(&intersection); pixman_region32_intersect_rect(&intersection, &node->visible, output_box.x, output_box.y, output_box.width, output_box.height); + uint32_t overlap = region_area(&intersection); + pixman_region32_fini(&intersection); - if (!pixman_region32_empty(&intersection)) { - uint32_t overlap = region_area(&intersection); + // If the overlap accounts for less than 10% of the visible node area, + // ignore this output + if (overlap >= 0.1 * visible_area) { if (overlap >= largest_overlap) { largest_overlap = overlap; scene_buffer->primary_output = scene_output; @@ -462,8 +467,6 @@ static void update_node_update_outputs(struct wlr_scene_node *node, active_outputs |= 1ull << scene_output->index; count++; } - - pixman_region32_fini(&intersection); } if (old_primary_output != scene_buffer->primary_output) { From 8713ac72fb9126f2cbc3f6dfd7f4154ea3174553 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 5 Jun 2025 11:36:39 +0200 Subject: [PATCH 384/519] scene: configure clients with the highest output scale If a surface appears on two outputs with the same intersection area, or even if a surface appears on an output with a small intersection area, we want to use the highest scale. Fixes flip-flop when a surface is added to multiple scenes. References: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3901 --- types/scene/surface.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/types/scene/surface.c b/types/scene/surface.c index e5b3b0f7d..d834aa71b 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -11,19 +11,30 @@ #include #include "types/wlr_scene.h" +static double get_surface_preferred_buffer_scale(struct wlr_surface *surface) { + double scale = 1; + struct wlr_surface_output *surface_output; + wl_list_for_each(surface_output, &surface->current_outputs, link) { + if (surface_output->output->scale > scale) { + scale = surface_output->output->scale; + } + } + return scale; +} + static void handle_scene_buffer_outputs_update( struct wl_listener *listener, void *data) { struct wlr_scene_surface *surface = wl_container_of(listener, surface, outputs_update); - if (surface->buffer->primary_output == NULL) { - return; - } - double scale = surface->buffer->primary_output->output->scale; + double scale = get_surface_preferred_buffer_scale(surface->surface); wlr_fractional_scale_v1_notify_scale(surface->surface, scale); wlr_surface_set_preferred_buffer_scale(surface->surface, ceil(scale)); - wlr_surface_set_preferred_buffer_transform(surface->surface, - surface->buffer->primary_output->output->transform); + + if (surface->buffer->primary_output != NULL) { + wlr_surface_set_preferred_buffer_transform(surface->surface, + surface->buffer->primary_output->output->transform); + } } static void handle_scene_buffer_output_enter( From 51d051497d9114e98e6004aa37bb1e2c537534fb Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 5 Jun 2025 12:11:01 +0200 Subject: [PATCH 385/519] scene: filter frame_done primary output in surface handler This lets the surface handler decide which output to send frame callbacks from. The output_sample event already works this way. Introduce wlr_scene_surface_send_frame_done() as a replacement for wlr_scene_buffer_send_frame_done() when a compositor doesn't have an output at hand. --- include/wlr/types/wlr_scene.h | 15 +++++++++++++-- types/scene/surface.c | 14 ++++++++++++-- types/scene/wlr_scene.c | 13 +++++++------ 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 2292f1fc8..9195264ab 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -152,6 +152,11 @@ struct wlr_scene_output_sample_event { bool direct_scanout; }; +struct wlr_scene_frame_done_event { + struct wlr_scene_output *output; + struct timespec when; +}; + /** A scene-graph node displaying a buffer */ struct wlr_scene_buffer { struct wlr_scene_node node; @@ -164,7 +169,7 @@ struct wlr_scene_buffer { struct wl_signal output_enter; // struct wlr_scene_output struct wl_signal output_leave; // struct wlr_scene_output struct wl_signal output_sample; // struct wlr_scene_output_sample_event - struct wl_signal frame_done; // struct timespec + struct wl_signal frame_done; // struct wlr_scene_frame_done_event } events; // May be NULL @@ -416,6 +421,12 @@ struct wlr_scene_rect *wlr_scene_rect_from_node(struct wlr_scene_node *node); struct wlr_scene_surface *wlr_scene_surface_try_from_buffer( struct wlr_scene_buffer *scene_buffer); +/** + * Call wlr_surface_send_frame_done() if the surface is visible. + */ +void wlr_scene_surface_send_frame_done(struct wlr_scene_surface *scene_surface, + const struct timespec *when); + /** * Add a node displaying a solid-colored rectangle to the scene-graph. * @@ -531,7 +542,7 @@ void wlr_scene_buffer_set_filter_mode(struct wlr_scene_buffer *scene_buffer, * Calls the buffer's frame_done signal. */ void wlr_scene_buffer_send_frame_done(struct wlr_scene_buffer *scene_buffer, - struct timespec *now); + struct wlr_scene_frame_done_event *event); /** * Add a viewport for the specified output to the scene-graph. diff --git a/types/scene/surface.c b/types/scene/surface.c index d834aa71b..f4bfe56ae 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -76,9 +76,19 @@ static void handle_scene_buffer_frame_done( struct wl_listener *listener, void *data) { struct wlr_scene_surface *surface = wl_container_of(listener, surface, frame_done); - struct timespec *now = data; + struct wlr_scene_frame_done_event *event = data; + if (surface->buffer->primary_output != event->output) { + return; + } - wlr_surface_send_frame_done(surface->surface, now); + wlr_surface_send_frame_done(surface->surface, &event->when); +} + +void wlr_scene_surface_send_frame_done(struct wlr_scene_surface *scene_surface, + const struct timespec *when) { + if (!pixman_region32_empty(&scene_surface->buffer->node.visible)) { + wlr_surface_send_frame_done(scene_surface->surface, when); + } } static void scene_surface_handle_surface_destroy( diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 256d68648..f12f0d263 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1072,9 +1072,9 @@ void wlr_scene_buffer_set_transform(struct wlr_scene_buffer *scene_buffer, } void wlr_scene_buffer_send_frame_done(struct wlr_scene_buffer *scene_buffer, - struct timespec *now) { + struct wlr_scene_frame_done_event *event) { if (!pixman_region32_empty(&scene_buffer->node.visible)) { - wl_signal_emit_mutable(&scene_buffer->events.frame_done, now); + wl_signal_emit_mutable(&scene_buffer->events.frame_done, event); } } @@ -2376,10 +2376,11 @@ static void scene_node_send_frame_done(struct wlr_scene_node *node, if (node->type == WLR_SCENE_NODE_BUFFER) { struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); - - if (scene_buffer->primary_output == scene_output) { - wlr_scene_buffer_send_frame_done(scene_buffer, now); - } + struct wlr_scene_frame_done_event event = { + .output = scene_output, + .when = *now, + }; + wlr_scene_buffer_send_frame_done(scene_buffer, &event); } else if (node->type == WLR_SCENE_NODE_TREE) { struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); struct wlr_scene_node *child; From 6204fc3278b6db77b70ef75469733296c9a37be3 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 5 Jun 2025 12:29:55 +0200 Subject: [PATCH 386/519] scene: use output with highest refresh rate for frame pacing If a surface is mirrored on two outputs, we don't want to pick the first output if the second has a higher refresh rate. Also fixes duplicate frame/feedback events when a surface is added to multiple scenes. --- include/wlr/types/wlr_scene.h | 7 +++++-- types/scene/surface.c | 24 +++++++++++++++++++----- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 9195264ab..114d5c0f3 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -123,6 +123,10 @@ struct wlr_scene_surface { struct { struct wlr_box clip; + // Output used for frame pacing (surface frame callbacks, presentation + // time feedback, etc), may be NULL + struct wlr_output *frame_pacing_output; + struct wlr_addon addon; struct wl_listener outputs_update; @@ -178,8 +182,7 @@ struct wlr_scene_buffer { /** * The output that the largest area of this buffer is displayed on. * This may be NULL if the buffer is not currently displayed on any - * outputs. This is the output that should be used for frame callbacks, - * presentation feedback, etc. + * outputs. */ struct wlr_scene_output *primary_output; diff --git a/types/scene/surface.c b/types/scene/surface.c index f4bfe56ae..53a4b1069 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -22,11 +22,25 @@ static double get_surface_preferred_buffer_scale(struct wlr_surface *surface) { return scale; } +static struct wlr_output *get_surface_frame_pacing_output(struct wlr_surface *surface) { + struct wlr_output *frame_pacing_output = NULL; + struct wlr_surface_output *surface_output; + wl_list_for_each(surface_output, &surface->current_outputs, link) { + if (frame_pacing_output == NULL || + surface_output->output->refresh > frame_pacing_output->refresh) { + frame_pacing_output = surface_output->output; + } + } + return frame_pacing_output; +} + static void handle_scene_buffer_outputs_update( struct wl_listener *listener, void *data) { struct wlr_scene_surface *surface = wl_container_of(listener, surface, outputs_update); + surface->frame_pacing_output = get_surface_frame_pacing_output(surface->surface); + double scale = get_surface_preferred_buffer_scale(surface->surface); wlr_fractional_scale_v1_notify_scale(surface->surface, scale); wlr_surface_set_preferred_buffer_scale(surface->surface, ceil(scale)); @@ -60,15 +74,15 @@ static void handle_scene_buffer_output_sample( struct wlr_scene_surface *surface = wl_container_of(listener, surface, output_sample); const struct wlr_scene_output_sample_event *event = data; - struct wlr_scene_output *scene_output = event->output; - if (surface->buffer->primary_output != scene_output) { + struct wlr_output *output = event->output->output; + if (surface->frame_pacing_output != output) { return; } if (event->direct_scanout) { - wlr_presentation_surface_scanned_out_on_output(surface->surface, scene_output->output); + wlr_presentation_surface_scanned_out_on_output(surface->surface, output); } else { - wlr_presentation_surface_textured_on_output(surface->surface, scene_output->output); + wlr_presentation_surface_textured_on_output(surface->surface, output); } } @@ -77,7 +91,7 @@ static void handle_scene_buffer_frame_done( struct wlr_scene_surface *surface = wl_container_of(listener, surface, frame_done); struct wlr_scene_frame_done_event *event = data; - if (surface->buffer->primary_output != event->output) { + if (surface->frame_pacing_output != event->output->output) { return; } From c6133f9912b96b3f03ba30347678712c0776ad5c Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 5 Jun 2025 12:42:12 +0200 Subject: [PATCH 387/519] scene: send surface preferred transform alongside DMA-BUF feedback wl_surface.preferred_buffer_transform is mainly useful to make direct scan-out more likely. It shouldn't make a difference with GL/Vulkan rendering. --- types/scene/surface.c | 5 ----- types/scene/wlr_scene.c | 8 ++++++++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/types/scene/surface.c b/types/scene/surface.c index 53a4b1069..1973abcaa 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -44,11 +44,6 @@ static void handle_scene_buffer_outputs_update( double scale = get_surface_preferred_buffer_scale(surface->surface); wlr_fractional_scale_v1_notify_scale(surface->surface, scale); wlr_surface_set_preferred_buffer_scale(surface->surface, ceil(scale)); - - if (surface->buffer->primary_output != NULL) { - wlr_surface_set_preferred_buffer_transform(surface->surface, - surface->buffer->primary_output->output->transform); - } } static void handle_scene_buffer_output_enter( diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index f12f0d263..1acbb5650 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1867,6 +1867,14 @@ static void scene_buffer_send_dmabuf_feedback(const struct wlr_scene *scene, return; } + enum wl_output_transform preferred_buffer_transform = WL_OUTPUT_TRANSFORM_NORMAL; + if (options->scanout_primary_output != NULL) { + preferred_buffer_transform = options->scanout_primary_output->transform; + } + + // TODO: also send wl_surface.preferred_buffer_transform when running with + // pure software rendering + wlr_surface_set_preferred_buffer_transform(surface->surface, preferred_buffer_transform); wlr_linux_dmabuf_v1_set_surface_feedback(scene->linux_dmabuf_v1, surface->surface, &feedback); From d421538b4acb69cc4064666bd9d0c23b37007fba Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 28 Jan 2025 09:57:41 +0100 Subject: [PATCH 388/519] render/color: add wlr_color_transform_init() --- include/render/color.h | 3 +++ render/color.c | 12 +++++++++--- render/color_lcms2.c | 4 +--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/include/render/color.h b/include/render/color.h index cddf1e219..176abf3a7 100644 --- a/include/render/color.h +++ b/include/render/color.h @@ -38,6 +38,9 @@ struct wlr_color_transform_lut3d { size_t dim_len; }; +void wlr_color_transform_init(struct wlr_color_transform *tr, + enum wlr_color_transform_type type); + /** * Gets a wlr_color_transform_lut3d from a generic wlr_color_transform. * Asserts that the base type is COLOR_TRANSFORM_LUT_3D diff --git a/render/color.c b/render/color.c index c708938c7..a033619b5 100644 --- a/render/color.c +++ b/render/color.c @@ -21,14 +21,20 @@ static const struct wlr_color_primaries COLOR_PRIMARIES_BT2020 = { // code point .white = { 0.3127, 0.3290 }, }; +void wlr_color_transform_init(struct wlr_color_transform *tr, enum wlr_color_transform_type type) { + *tr = (struct wlr_color_transform){ + .type = type, + .ref_count = 1, + }; + wlr_addon_set_init(&tr->addons); +} + struct wlr_color_transform *wlr_color_transform_init_srgb(void) { struct wlr_color_transform *tx = calloc(1, sizeof(struct wlr_color_transform)); if (!tx) { return NULL; } - tx->type = COLOR_TRANSFORM_SRGB; - tx->ref_count = 1; - wlr_addon_set_init(&tx->addons); + wlr_color_transform_init(tx, COLOR_TRANSFORM_SRGB); return tx; } diff --git a/render/color_lcms2.c b/render/color_lcms2.c index c0f4db817..e6a2673e1 100644 --- a/render/color_lcms2.c +++ b/render/color_lcms2.c @@ -99,11 +99,9 @@ struct wlr_color_transform *wlr_color_transform_init_linear_to_icc( if (!tx) { goto out_lcms_tr; } - tx->base.type = COLOR_TRANSFORM_LUT_3D; + wlr_color_transform_init(&tx->base, COLOR_TRANSFORM_LUT_3D); tx->dim_len = dim_len; tx->lut_3d = lut_3d; - tx->base.ref_count = 1; - wlr_addon_set_init(&tx->base.addons); out_lcms_tr: cmsDeleteTransform(lcms_tr); From 9b97e2607d37186d749b4a07456294471014b465 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 24 May 2025 11:10:22 +0200 Subject: [PATCH 389/519] render/color: use variable instead of type in sizeof() Conforms to the wlroots code style. --- render/color.c | 2 +- render/color_lcms2.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/render/color.c b/render/color.c index a033619b5..bbc07dc9f 100644 --- a/render/color.c +++ b/render/color.c @@ -30,7 +30,7 @@ void wlr_color_transform_init(struct wlr_color_transform *tr, enum wlr_color_tra } struct wlr_color_transform *wlr_color_transform_init_srgb(void) { - struct wlr_color_transform *tx = calloc(1, sizeof(struct wlr_color_transform)); + struct wlr_color_transform *tx = calloc(1, sizeof(*tx)); if (!tx) { return NULL; } diff --git a/render/color_lcms2.c b/render/color_lcms2.c index e6a2673e1..88df46087 100644 --- a/render/color_lcms2.c +++ b/render/color_lcms2.c @@ -95,7 +95,7 @@ struct wlr_color_transform *wlr_color_transform_init_linear_to_icc( } } - tx = calloc(1, sizeof(struct wlr_color_transform_lut3d)); + tx = calloc(1, sizeof(*tx)); if (!tx) { goto out_lcms_tr; } From 3665b53e296a69cf9f32f5099117ae30c84fca92 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 24 May 2025 13:04:45 +0200 Subject: [PATCH 390/519] render/color: replace COLOR_TRANSFORM_LUT_3D with COLOR_TRANSFORM_LCMS2 Converting the LCMS2 transform to a 3D LUT early causes issues: - It's a lossy process, the consumer will not be able to pick a 3D LUT size on their own. - It requires unnecessary conversions and allocations: an intermediate 3D LUT is allocated, but the renderer already allocates one. - It makes it harder to support arbitrary color transforms in the renderer, because each type needs to be handled differently. Instead, expose a function to evaluate a color transform, and use that to build the 3D LUT in the renderer. --- include/render/color.h | 37 ++++++---------- include/render/vulkan.h | 1 + render/color.c | 13 +----- render/color_fallback.c | 17 +++++++- render/color_lcms2.c | 96 ++++++++++++++++++++--------------------- render/vulkan/pass.c | 56 +++++++++++++----------- 6 files changed, 109 insertions(+), 111 deletions(-) diff --git a/include/render/color.h b/include/render/color.h index 176abf3a7..a3ab73d83 100644 --- a/include/render/color.h +++ b/include/render/color.h @@ -7,7 +7,7 @@ enum wlr_color_transform_type { COLOR_TRANSFORM_SRGB, - COLOR_TRANSFORM_LUT_3D, + COLOR_TRANSFORM_LCMS2, }; struct wlr_color_transform { @@ -17,37 +17,24 @@ struct wlr_color_transform { enum wlr_color_transform_type type; }; -/** - * The formula is approximated via a 3D look-up table. A 3D LUT is a - * three-dimensional array where each element is an RGB triplet. The flat lut_3d - * array has a length of dim_len³. - * - * Color channel values in the range [0.0, 1.0] are mapped linearly to - * 3D LUT indices such that 0.0 maps exactly to the first element and 1.0 maps - * exactly to the last element in each dimension. - * - * The offset of the RGB triplet given red, green and blue indices r_index, - * g_index and b_index is: - * - * offset = 3 * (r_index + dim_len * g_index + dim_len * dim_len * b_index) - */ -struct wlr_color_transform_lut3d { - struct wlr_color_transform base; - - float *lut_3d; - size_t dim_len; -}; - void wlr_color_transform_init(struct wlr_color_transform *tr, enum wlr_color_transform_type type); /** - * Gets a wlr_color_transform_lut3d from a generic wlr_color_transform. - * Asserts that the base type is COLOR_TRANSFORM_LUT_3D + * Get a struct wlr_color_transform_lcms2 from a generic struct wlr_color_transform. + * Asserts that the base type is COLOR_TRANSFORM_LCMS2. */ -struct wlr_color_transform_lut3d *wlr_color_transform_lut3d_from_base( +struct wlr_color_transform_lcms2 *color_transform_lcms2_from_base( struct wlr_color_transform *tr); +void color_transform_lcms2_finish(struct wlr_color_transform_lcms2 *tr); + +/** + * Evaluate a LCMS2 color transform for a given RGB triplet. + */ +void color_transform_lcms2_eval(struct wlr_color_transform_lcms2 *tr, + float out[static 3], const float in[static 3]); + /** * Obtain primaries values from a well-known primaries name. */ diff --git a/include/render/vulkan.h b/include/render/vulkan.h index abeb11cc5..78109356f 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -516,6 +516,7 @@ struct wlr_vk_color_transform { struct wl_list link; // wlr_vk_renderer, list of all color transforms struct { + size_t dim; VkImage image; VkImageView image_view; VkDeviceMemory memory; diff --git a/render/color.c b/render/color.c index bbc07dc9f..1c8365013 100644 --- a/render/color.c +++ b/render/color.c @@ -42,10 +42,8 @@ static void color_transform_destroy(struct wlr_color_transform *tr) { switch (tr->type) { case COLOR_TRANSFORM_SRGB: break; - case COLOR_TRANSFORM_LUT_3D:; - struct wlr_color_transform_lut3d *lut3d = - wlr_color_transform_lut3d_from_base(tr); - free(lut3d->lut_3d); + case COLOR_TRANSFORM_LCMS2: + color_transform_lcms2_finish(color_transform_lcms2_from_base(tr)); break; } wlr_addon_set_finish(&tr->addons); @@ -68,13 +66,6 @@ void wlr_color_transform_unref(struct wlr_color_transform *tr) { } } -struct wlr_color_transform_lut3d *wlr_color_transform_lut3d_from_base( - struct wlr_color_transform *tr) { - assert(tr->type == COLOR_TRANSFORM_LUT_3D); - struct wlr_color_transform_lut3d *lut3d = wl_container_of(tr, lut3d, base); - return lut3d; -} - void wlr_color_primaries_from_named(struct wlr_color_primaries *out, enum wlr_color_named_primaries named) { switch (named) { diff --git a/render/color_fallback.c b/render/color_fallback.c index 72741c331..1b25881c4 100644 --- a/render/color_fallback.c +++ b/render/color_fallback.c @@ -1,5 +1,6 @@ -#include +#include #include +#include "render/color.h" struct wlr_color_transform *wlr_color_transform_init_linear_to_icc( const void *data, size_t size) { @@ -7,3 +8,17 @@ struct wlr_color_transform *wlr_color_transform_init_linear_to_icc( "LCMS2 is compile-time disabled"); return NULL; } + +struct wlr_color_transform_lcms2 *color_transform_lcms2_from_base( + struct wlr_color_transform *tr) { + abort(); // unreachable +} + +void color_transform_lcms2_finish(struct wlr_color_transform_lcms2 *tr) { + abort(); // unreachable +} + +void color_transform_lcms2_eval(struct wlr_color_transform_lcms2 *tr, + float out[static 3], const float in[static 3]) { + abort(); // unreachable +} diff --git a/render/color_lcms2.c b/render/color_lcms2.c index 88df46087..39e16cdd7 100644 --- a/render/color_lcms2.c +++ b/render/color_lcms2.c @@ -1,9 +1,17 @@ +#include #include #include #include #include #include "render/color.h" +struct wlr_color_transform_lcms2 { + struct wlr_color_transform base; + + cmsContext ctx; + cmsHTRANSFORM lcms; +}; + static const cmsCIExyY srgb_whitepoint = { 0.3127, 0.3291, 1 }; static const cmsCIExyYTRIPLE srgb_primaries = { @@ -18,7 +26,7 @@ static void handle_lcms_error(cmsContext ctx, cmsUInt32Number code, const char * struct wlr_color_transform *wlr_color_transform_init_linear_to_icc( const void *data, size_t size) { - struct wlr_color_transform_lut3d *tx = NULL; + struct wlr_color_transform_lcms2 *tx = NULL; cmsContext ctx = cmsCreateContext(NULL, NULL); if (ctx == NULL) { @@ -31,18 +39,18 @@ struct wlr_color_transform *wlr_color_transform_init_linear_to_icc( cmsHPROFILE icc_profile = cmsOpenProfileFromMemTHR(ctx, data, size); if (icc_profile == NULL) { wlr_log(WLR_ERROR, "cmsOpenProfileFromMemTHR failed"); - goto out_ctx; + goto error_ctx; } if (cmsGetDeviceClass(icc_profile) != cmsSigDisplayClass) { wlr_log(WLR_ERROR, "ICC profile must have the Display device class"); - goto out_icc_profile; + goto error_icc_profile; } cmsToneCurve *linear_tone_curve = cmsBuildGamma(ctx, 1); if (linear_tone_curve == NULL) { wlr_log(WLR_ERROR, "cmsBuildGamma failed"); - goto out_icc_profile; + goto error_icc_profile; } cmsToneCurve *linear_tf[] = { @@ -52,66 +60,54 @@ struct wlr_color_transform *wlr_color_transform_init_linear_to_icc( }; cmsHPROFILE srgb_profile = cmsCreateRGBProfileTHR(ctx, &srgb_whitepoint, &srgb_primaries, linear_tf); + cmsFreeToneCurve(linear_tone_curve); if (srgb_profile == NULL) { wlr_log(WLR_ERROR, "cmsCreateRGBProfileTHR failed"); - goto out_linear_tone_curve; + goto error_icc_profile; } cmsHTRANSFORM lcms_tr = cmsCreateTransformTHR(ctx, srgb_profile, TYPE_RGB_FLT, icc_profile, TYPE_RGB_FLT, INTENT_RELATIVE_COLORIMETRIC, 0); + cmsCloseProfile(srgb_profile); + cmsCloseProfile(icc_profile); if (lcms_tr == NULL) { wlr_log(WLR_ERROR, "cmsCreateTransformTHR failed"); - goto out_srgb_profile; - } - - size_t dim_len = 33; - float *lut_3d = calloc(3 * dim_len * dim_len * dim_len, sizeof(float)); - if (lut_3d == NULL) { - wlr_log_errno(WLR_ERROR, "Allocation failed"); - goto out_lcms_tr; - } - - float factor = 1.0f / (dim_len - 1); - for (size_t b_index = 0; b_index < dim_len; b_index++) { - for (size_t g_index = 0; g_index < dim_len; g_index++) { - for (size_t r_index = 0; r_index < dim_len; r_index++) { - float rgb_in[3] = { - r_index * factor, - g_index * factor, - b_index * factor, - }; - float rgb_out[3]; - // TODO: use a single call to cmsDoTransform for the entire calculation? - // this does require allocating an extra temp buffer - cmsDoTransform(lcms_tr, rgb_in, rgb_out, 1); - - size_t offset = 3 * (r_index + dim_len * g_index + dim_len * dim_len * b_index); - // TODO: maybe clamp values to [0.0, 1.0] here? - lut_3d[offset] = rgb_out[0]; - lut_3d[offset + 1] = rgb_out[1]; - lut_3d[offset + 2] = rgb_out[2]; - } - } + goto error_ctx; } tx = calloc(1, sizeof(*tx)); if (!tx) { - goto out_lcms_tr; + cmsDeleteTransform(lcms_tr); + goto error_ctx; } - wlr_color_transform_init(&tx->base, COLOR_TRANSFORM_LUT_3D); - tx->dim_len = dim_len; - tx->lut_3d = lut_3d; + wlr_color_transform_init(&tx->base, COLOR_TRANSFORM_LCMS2); + + tx->ctx = ctx; + tx->lcms = lcms_tr; -out_lcms_tr: - cmsDeleteTransform(lcms_tr); -out_linear_tone_curve: - cmsFreeToneCurve(linear_tone_curve); -out_srgb_profile: - cmsCloseProfile(srgb_profile); -out_icc_profile: - cmsCloseProfile(icc_profile); -out_ctx: - cmsDeleteContext(ctx); return &tx->base; + +error_icc_profile: + cmsCloseProfile(icc_profile); +error_ctx: + cmsDeleteContext(ctx); + return NULL; +} + +void color_transform_lcms2_finish(struct wlr_color_transform_lcms2 *tr) { + cmsDeleteTransform(tr->lcms); + cmsDeleteContext(tr->ctx); +} + +struct wlr_color_transform_lcms2 *color_transform_lcms2_from_base( + struct wlr_color_transform *tr) { + assert(tr->type == COLOR_TRANSFORM_LCMS2); + struct wlr_color_transform_lcms2 *lcms2 = wl_container_of(tr, lcms2, base); + return lcms2; +} + +void color_transform_lcms2_eval(struct wlr_color_transform_lcms2 *tr, + float out[static 3], const float in[static 3]) { + cmsDoTransform(tr->lcms, in, out, 1); } diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 3f662b203..2ad6d9086 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -179,11 +179,12 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { .uv_size = { 1, 1 }, }; + struct wlr_vk_color_transform *transform = NULL; size_t dim = 1; - if (pass->color_transform && pass->color_transform->type == COLOR_TRANSFORM_LUT_3D) { - struct wlr_color_transform_lut3d *lut3d = - wlr_color_transform_lut3d_from_base(pass->color_transform); - dim = lut3d->dim_len; + if (pass->color_transform && pass->color_transform->type != COLOR_TRANSFORM_SRGB) { + transform = get_color_transform(pass->color_transform, renderer); + assert(transform); + dim = transform->lut_3d.dim; } struct wlr_vk_frag_output_pcr_data frag_pcr_data = { @@ -204,10 +205,7 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { sizeof(frag_pcr_data), &frag_pcr_data); VkDescriptorSet lut_ds; - if (pass->color_transform && pass->color_transform->type == COLOR_TRANSFORM_LUT_3D) { - struct wlr_vk_color_transform *transform = - get_color_transform(pass->color_transform, renderer); - assert(transform); + if (transform != NULL) { lut_ds = transform->lut_3d.ds; } else { lut_ds = renderer->output_ds_lut3d_dummy; @@ -840,9 +838,9 @@ void vk_color_transform_destroy(struct wlr_addon *addon) { } static bool create_3d_lut_image(struct wlr_vk_renderer *renderer, - const struct wlr_color_transform_lut3d *lut_3d, + struct wlr_color_transform_lcms2 *tr, size_t dim_len, VkImage *image, VkImageView *image_view, - VkDeviceMemory *memory, VkDescriptorSet *ds, + VkDeviceMemory *memory, VkDescriptorSet *ds, struct wlr_vk_descriptor_pool **ds_pool) { VkDevice dev = renderer->dev->dev; VkResult res; @@ -866,7 +864,7 @@ static bool create_3d_lut_image(struct wlr_vk_renderer *renderer, .samples = VK_SAMPLE_COUNT_1_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .extent = (VkExtent3D) { lut_3d->dim_len, lut_3d->dim_len, lut_3d->dim_len }, + .extent = (VkExtent3D) { dim_len, dim_len, dim_len }, .tiling = VK_IMAGE_TILING_OPTIMAL, .usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, }; @@ -927,7 +925,7 @@ static bool create_3d_lut_image(struct wlr_vk_renderer *renderer, } size_t bytes_per_block = 4 * sizeof(float); - size_t size = lut_3d->dim_len * lut_3d->dim_len * lut_3d->dim_len * bytes_per_block; + size_t size = dim_len * dim_len * dim_len * bytes_per_block; struct wlr_vk_buffer_span span = vulkan_get_stage_span(renderer, size, bytes_per_block); if (!span.buffer || span.alloc.size != size) { @@ -935,18 +933,26 @@ static bool create_3d_lut_image(struct wlr_vk_renderer *renderer, goto fail_imageview; } - char *map = (char*)span.buffer->cpu_mapping + span.alloc.start; - float *dst = (float*)map; - size_t dim_len = lut_3d->dim_len; + float sample_range = 1.0f / (dim_len - 1); + char *map = (char *)span.buffer->cpu_mapping + span.alloc.start; + float *dst = (float *)map; for (size_t b_index = 0; b_index < dim_len; b_index++) { for (size_t g_index = 0; g_index < dim_len; g_index++) { for (size_t r_index = 0; r_index < dim_len; r_index++) { size_t sample_index = r_index + dim_len * g_index + dim_len * dim_len * b_index; - size_t src_offset = 3 * sample_index; size_t dst_offset = 4 * sample_index; - dst[dst_offset] = lut_3d->lut_3d[src_offset]; - dst[dst_offset + 1] = lut_3d->lut_3d[src_offset + 1]; - dst[dst_offset + 2] = lut_3d->lut_3d[src_offset + 2]; + + float rgb_in[3] = { + r_index * sample_range, + g_index * sample_range, + b_index * sample_range, + }; + float rgb_out[3]; + color_transform_lcms2_eval(tr, rgb_out, rgb_in); + + dst[dst_offset] = rgb_out[0]; + dst[dst_offset + 1] = rgb_out[1]; + dst[dst_offset + 2] = rgb_out[2]; dst[dst_offset + 3] = 1.0; } } @@ -959,9 +965,9 @@ static bool create_3d_lut_image(struct wlr_vk_renderer *renderer, VK_ACCESS_TRANSFER_WRITE_BIT); VkBufferImageCopy copy = { .bufferOffset = span.alloc.start, - .imageExtent.width = lut_3d->dim_len, - .imageExtent.height = lut_3d->dim_len, - .imageExtent.depth = lut_3d->dim_len, + .imageExtent.width = dim_len, + .imageExtent.height = dim_len, + .imageExtent.depth = dim_len, .imageSubresource.layerCount = 1, .imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, }; @@ -1012,9 +1018,11 @@ static struct wlr_vk_color_transform *vk_color_transform_create( return NULL; } - if (transform->type == COLOR_TRANSFORM_LUT_3D) { + if (transform->type == COLOR_TRANSFORM_LCMS2) { + vk_transform->lut_3d.dim = 33; if (!create_3d_lut_image(renderer, - wlr_color_transform_lut3d_from_base(transform), + color_transform_lcms2_from_base(transform), + vk_transform->lut_3d.dim, &vk_transform->lut_3d.image, &vk_transform->lut_3d.image_view, &vk_transform->lut_3d.memory, From 74217a4d9341db0b214be4a8e65c4695f289d668 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 24 May 2025 11:19:32 +0200 Subject: [PATCH 391/519] render/color: introduce COLOR_TRANSFORM_LUT_3X1D This will be useful to apply LUTs applied via wlr_gamma_control_v1, and to add wlr_color_transform support to wlr_output. --- include/render/color.h | 30 ++++++++++++++++++++ include/wlr/render/color.h | 8 ++++++ render/color.c | 57 ++++++++++++++++++++++++++++++++++++++ render/vulkan/pass.c | 26 +++++++++++++---- 4 files changed, 116 insertions(+), 5 deletions(-) diff --git a/include/render/color.h b/include/render/color.h index a3ab73d83..a797881bf 100644 --- a/include/render/color.h +++ b/include/render/color.h @@ -8,6 +8,7 @@ enum wlr_color_transform_type { COLOR_TRANSFORM_SRGB, COLOR_TRANSFORM_LCMS2, + COLOR_TRANSFORM_LUT_3X1D, }; struct wlr_color_transform { @@ -20,6 +21,21 @@ struct wlr_color_transform { void wlr_color_transform_init(struct wlr_color_transform *tr, enum wlr_color_transform_type type); +/** + * The formula is approximated via three 1D look-up tables. The flat lut_3x1d + * array has a length of 3 * dim. + * + * The offset of a color value for a given channel and color index is: + * + * offset = channel_index * dim + color_index + */ +struct wlr_color_transform_lut_3x1d { + struct wlr_color_transform base; + + uint16_t *lut_3x1d; + size_t dim; +}; + /** * Get a struct wlr_color_transform_lcms2 from a generic struct wlr_color_transform. * Asserts that the base type is COLOR_TRANSFORM_LCMS2. @@ -35,6 +51,20 @@ void color_transform_lcms2_finish(struct wlr_color_transform_lcms2 *tr); void color_transform_lcms2_eval(struct wlr_color_transform_lcms2 *tr, float out[static 3], const float in[static 3]); +/** + * Get a struct wlr_color_transform_lut_3x1d from a generic + * struct wlr_color_transform. Asserts that the base type is + * COLOR_TRANSFORM_LUT_3X1D. + */ +struct wlr_color_transform_lut_3x1d *color_transform_lut_3x1d_from_base( + struct wlr_color_transform *tr); + +/** + * Evaluate a 3x1D LUT color transform for a given RGB triplet. + */ +void color_transform_lut_3x1d_eval(struct wlr_color_transform_lut_3x1d *tr, + float out[static 3], const float in[static 3]); + /** * Obtain primaries values from a well-known primaries name. */ diff --git a/include/wlr/render/color.h b/include/wlr/render/color.h index dc6f4097f..c2cbee265 100644 --- a/include/wlr/render/color.h +++ b/include/wlr/render/color.h @@ -10,6 +10,7 @@ #define WLR_RENDER_COLOR_H #include +#include #include /** @@ -78,6 +79,13 @@ struct wlr_color_transform *wlr_color_transform_init_linear_to_icc( */ struct wlr_color_transform *wlr_color_transform_init_srgb(void); +/** + * Initialize a color transformation to apply three 1D look-up tables. dim + * is the number of elements in each individual LUT. Returns NULL on failure. + */ +struct wlr_color_transform *wlr_color_transform_init_lut_3x1d(size_t dim, + const uint16_t *r, const uint16_t *g, const uint16_t *b); + /** * Increase the reference count of the color transform by 1. */ diff --git a/render/color.c b/render/color.c index 1c8365013..2bd8d65ec 100644 --- a/render/color.c +++ b/render/color.c @@ -38,6 +38,28 @@ struct wlr_color_transform *wlr_color_transform_init_srgb(void) { return tx; } +struct wlr_color_transform *wlr_color_transform_init_lut_3x1d(size_t dim, + const uint16_t *r, const uint16_t *g, const uint16_t *b) { + uint16_t *lut_3x1d = malloc(3 * dim * sizeof(lut_3x1d[0])); + if (lut_3x1d == NULL) { + return NULL; + } + + memcpy(&lut_3x1d[0 * dim], r, dim * sizeof(lut_3x1d[0])); + memcpy(&lut_3x1d[1 * dim], g, dim * sizeof(lut_3x1d[0])); + memcpy(&lut_3x1d[2 * dim], b, dim * sizeof(lut_3x1d[0])); + + struct wlr_color_transform_lut_3x1d *tx = calloc(1, sizeof(*tx)); + if (!tx) { + free(lut_3x1d); + return NULL; + } + wlr_color_transform_init(&tx->base, COLOR_TRANSFORM_LUT_3X1D); + tx->lut_3x1d = lut_3x1d; + tx->dim = dim; + return &tx->base; +} + static void color_transform_destroy(struct wlr_color_transform *tr) { switch (tr->type) { case COLOR_TRANSFORM_SRGB: @@ -45,6 +67,10 @@ static void color_transform_destroy(struct wlr_color_transform *tr) { case COLOR_TRANSFORM_LCMS2: color_transform_lcms2_finish(color_transform_lcms2_from_base(tr)); break; + case COLOR_TRANSFORM_LUT_3X1D:; + struct wlr_color_transform_lut_3x1d *lut_3x1d = color_transform_lut_3x1d_from_base(tr); + free(lut_3x1d->lut_3x1d); + break; } wlr_addon_set_finish(&tr->addons); free(tr); @@ -66,6 +92,37 @@ void wlr_color_transform_unref(struct wlr_color_transform *tr) { } } +struct wlr_color_transform_lut_3x1d *color_transform_lut_3x1d_from_base( + struct wlr_color_transform *tr) { + assert(tr->type == COLOR_TRANSFORM_LUT_3X1D); + struct wlr_color_transform_lut_3x1d *lut_3x1d = wl_container_of(tr, lut_3x1d, base); + return lut_3x1d; +} + +static float lut_1d_get(const uint16_t *lut, size_t len, size_t i) { + if (i > len) { + i = len - 1; + } + return (float) lut[i] / UINT16_MAX; +} + +static float lut_1d_eval(const uint16_t *lut, size_t len, float x) { + double pos = x * (len - 1); + double int_part; + double frac_part = modf(pos, &int_part); + size_t i = (size_t) int_part; + double a = lut_1d_get(lut, len, i); + double b = lut_1d_get(lut, len, i + 1); + return a * (1 - frac_part) + b * frac_part; +} + +void color_transform_lut_3x1d_eval(struct wlr_color_transform_lut_3x1d *tr, + float out[static 3], const float in[static 3]) { + for (size_t i = 0; i < 3; i++) { + out[i] = lut_1d_eval(&tr->lut_3x1d[tr->dim * i], tr->dim, in[i]); + } +} + void wlr_color_primaries_from_named(struct wlr_color_primaries *out, enum wlr_color_named_primaries named) { switch (named) { diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 2ad6d9086..529743305 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -838,7 +838,7 @@ void vk_color_transform_destroy(struct wlr_addon *addon) { } static bool create_3d_lut_image(struct wlr_vk_renderer *renderer, - struct wlr_color_transform_lcms2 *tr, size_t dim_len, + struct wlr_color_transform *tr, size_t dim_len, VkImage *image, VkImageView *image_view, VkDeviceMemory *memory, VkDescriptorSet *ds, struct wlr_vk_descriptor_pool **ds_pool) { @@ -851,6 +851,19 @@ static bool create_3d_lut_image(struct wlr_vk_renderer *renderer, *ds = VK_NULL_HANDLE; *ds_pool = NULL; + struct wlr_color_transform_lcms2 *tr_lcms2 = NULL; + struct wlr_color_transform_lut_3x1d *tr_lut_3x1d = NULL; + switch (tr->type) { + case COLOR_TRANSFORM_SRGB: + abort(); // unreachable + case COLOR_TRANSFORM_LCMS2: + tr_lcms2 = color_transform_lcms2_from_base(tr); + break; + case COLOR_TRANSFORM_LUT_3X1D: + tr_lut_3x1d = color_transform_lut_3x1d_from_base(tr); + break; + } + // R32G32B32 is not a required Vulkan format // TODO: use it when available VkFormat format = VK_FORMAT_R32G32B32A32_SFLOAT; @@ -948,7 +961,11 @@ static bool create_3d_lut_image(struct wlr_vk_renderer *renderer, b_index * sample_range, }; float rgb_out[3]; - color_transform_lcms2_eval(tr, rgb_out, rgb_in); + if (tr_lcms2 != NULL) { + color_transform_lcms2_eval(tr_lcms2, rgb_out, rgb_in); + } else { + color_transform_lut_3x1d_eval(tr_lut_3x1d, rgb_out, rgb_in); + } dst[dst_offset] = rgb_out[0]; dst[dst_offset + 1] = rgb_out[1]; @@ -1018,10 +1035,9 @@ static struct wlr_vk_color_transform *vk_color_transform_create( return NULL; } - if (transform->type == COLOR_TRANSFORM_LCMS2) { + if (transform->type != COLOR_TRANSFORM_SRGB) { vk_transform->lut_3d.dim = 33; - if (!create_3d_lut_image(renderer, - color_transform_lcms2_from_base(transform), + if (!create_3d_lut_image(renderer, transform, vk_transform->lut_3d.dim, &vk_transform->lut_3d.image, &vk_transform->lut_3d.image_view, From 97f6946c8d1603dc6faa8689bd7e63aa2cea4a36 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 24 Aug 2024 11:14:54 +0200 Subject: [PATCH 392/519] output: add color transform to state Color transforms are better suited than raw gamma tables, because: - They don't need to get copied around: they are ref'counted. - They can represent more color operations (will be useful for the upcoming KMS color pipeline API, and for the Wayland color management protocol). --- include/wlr/types/wlr_output.h | 10 ++++++++++ types/output/output.c | 4 ++++ types/output/state.c | 20 +++++++++++++++++++- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 31b757062..6473ece4f 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -74,6 +74,7 @@ enum wlr_output_state_field { WLR_OUTPUT_STATE_LAYERS = 1 << 10, WLR_OUTPUT_STATE_WAIT_TIMELINE = 1 << 11, WLR_OUTPUT_STATE_SIGNAL_TIMELINE = 1 << 12, + WLR_OUTPUT_STATE_COLOR_TRANSFORM = 1 << 13, }; enum wlr_output_state_mode_type { @@ -132,6 +133,8 @@ struct wlr_output_state { uint64_t wait_point; struct wlr_drm_syncobj_timeline *signal_timeline; uint64_t signal_point; + + struct wlr_color_transform *color_transform; }; struct wlr_output_impl; @@ -586,6 +589,13 @@ void wlr_output_state_set_wait_timeline(struct wlr_output_state *state, */ void wlr_output_state_set_signal_timeline(struct wlr_output_state *state, struct wlr_drm_syncobj_timeline *timeline, uint64_t dst_point); +/** + * Set the color transform for an output. + * + * The color transform is applied after blending output layers. + */ +void wlr_output_state_set_color_transform(struct wlr_output_state *state, + struct wlr_color_transform *tr); /** * Copies the output state from src to dst. It is safe to then diff --git a/types/output/output.c b/types/output/output.c index 55f8a7645..9e63ec76e 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -651,6 +651,10 @@ static bool output_basic_test(struct wlr_output *output, wlr_log(WLR_DEBUG, "Tried to set the subpixel layout on a disabled output"); return false; } + if (!enabled && state->committed & WLR_OUTPUT_STATE_COLOR_TRANSFORM) { + wlr_log(WLR_DEBUG, "Tried to set a color transform on a disabled output"); + return false; + } if (state->committed & WLR_OUTPUT_STATE_LAYERS) { if (state->layers_len != (size_t)wl_list_length(&output->layers)) { diff --git a/types/output/state.c b/types/output/state.c index 72849c027..1a7b00508 100644 --- a/types/output/state.c +++ b/types/output/state.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include "types/wlr_output.h" @@ -133,6 +134,17 @@ void wlr_output_state_set_signal_timeline(struct wlr_output_state *state, state->signal_point = dst_point; } +void wlr_output_state_set_color_transform(struct wlr_output_state *state, + struct wlr_color_transform *tr) { + state->committed |= WLR_OUTPUT_STATE_COLOR_TRANSFORM; + wlr_color_transform_unref(state->color_transform); + if (tr) { + state->color_transform = wlr_color_transform_ref(tr); + } else { + state->color_transform = NULL; + } +} + bool wlr_output_state_copy(struct wlr_output_state *dst, const struct wlr_output_state *src) { struct wlr_output_state copy = *src; @@ -140,7 +152,8 @@ bool wlr_output_state_copy(struct wlr_output_state *dst, WLR_OUTPUT_STATE_DAMAGE | WLR_OUTPUT_STATE_GAMMA_LUT | WLR_OUTPUT_STATE_WAIT_TIMELINE | - WLR_OUTPUT_STATE_SIGNAL_TIMELINE); + WLR_OUTPUT_STATE_SIGNAL_TIMELINE | + WLR_OUTPUT_STATE_COLOR_TRANSFORM); copy.buffer = NULL; copy.buffer_src_box = (struct wlr_fbox){0}; copy.buffer_dst_box = (struct wlr_box){0}; @@ -149,6 +162,7 @@ bool wlr_output_state_copy(struct wlr_output_state *dst, copy.gamma_lut_size = 0; copy.wait_timeline = NULL; copy.signal_timeline = NULL; + copy.color_transform = NULL; if (src->committed & WLR_OUTPUT_STATE_BUFFER) { wlr_output_state_set_buffer(©, src->buffer); @@ -178,6 +192,10 @@ bool wlr_output_state_copy(struct wlr_output_state *dst, src->signal_point); } + if (src->committed & WLR_OUTPUT_STATE_COLOR_TRANSFORM) { + wlr_output_state_set_color_transform(©, src->color_transform); + } + wlr_output_state_finish(dst); *dst = copy; return true; From f10dd1da1c7b58bd1dde79e18c70109e3f90c3d4 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 24 May 2025 13:40:05 +0200 Subject: [PATCH 393/519] backend/drm: add support for color transforms --- backend/drm/atomic.c | 19 +++++++++++++------ backend/drm/drm.c | 12 ++++++++++-- backend/drm/legacy.c | 15 ++++++++++++--- 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index 037c3c23a..cfef2ff36 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -10,6 +10,7 @@ #include "backend/drm/fb.h" #include "backend/drm/iface.h" #include "backend/drm/util.h" +#include "render/color.h" static char *atomic_commit_flags_str(uint32_t flags) { const char *const l[] = { @@ -251,19 +252,25 @@ bool drm_atomic_connector_prepare(struct wlr_drm_connector_state *state, bool mo } uint32_t gamma_lut = crtc->gamma_lut; - if (state->base->committed & WLR_OUTPUT_STATE_GAMMA_LUT) { + if (state->base->committed & WLR_OUTPUT_STATE_COLOR_TRANSFORM) { + size_t dim = 0; + uint16_t *lut = NULL; + if (state->base->color_transform != NULL) { + struct wlr_color_transform_lut_3x1d *tr = + color_transform_lut_3x1d_from_base(state->base->color_transform); + dim = tr->dim; + lut = tr->lut_3x1d; + } + // Fallback to legacy gamma interface when gamma properties are not // available (can happen on older Intel GPUs that support gamma but not // degamma). if (crtc->props.gamma_lut == 0) { - if (!drm_legacy_crtc_set_gamma(drm, crtc, - state->base->gamma_lut_size, - state->base->gamma_lut)) { + if (!drm_legacy_crtc_set_gamma(drm, crtc, dim, lut)) { return false; } } else { - if (!create_gamma_lut_blob(drm, state->base->gamma_lut_size, - state->base->gamma_lut, &gamma_lut)) { + if (!create_gamma_lut_blob(drm, dim, lut, &gamma_lut)) { return false; } } diff --git a/backend/drm/drm.c b/backend/drm/drm.c index e9e4c6db2..653497813 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -24,6 +24,7 @@ #include "backend/drm/fb.h" #include "backend/drm/iface.h" #include "backend/drm/util.h" +#include "render/color.h" #include "types/wlr_output.h" #include "util/env.h" #include "config.h" @@ -37,11 +38,11 @@ static const uint32_t COMMIT_OUTPUT_STATE = WLR_OUTPUT_STATE_BUFFER | WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_ENABLED | - WLR_OUTPUT_STATE_GAMMA_LUT | WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED | WLR_OUTPUT_STATE_LAYERS | WLR_OUTPUT_STATE_WAIT_TIMELINE | - WLR_OUTPUT_STATE_SIGNAL_TIMELINE; + WLR_OUTPUT_STATE_SIGNAL_TIMELINE | + WLR_OUTPUT_STATE_COLOR_TRANSFORM; static const uint32_t SUPPORTED_OUTPUT_STATE = WLR_OUTPUT_STATE_BACKEND_OPTIONAL | COMMIT_OUTPUT_STATE; @@ -856,6 +857,13 @@ static bool drm_connector_prepare(struct wlr_drm_connector_state *conn_state, bo } } + if ((state->committed & WLR_OUTPUT_STATE_COLOR_TRANSFORM) && state->color_transform != NULL && + state->color_transform->type != COLOR_TRANSFORM_LUT_3X1D) { + wlr_drm_conn_log(conn, WLR_DEBUG, + "Only 3x1D LUT color transforms are supported"); + return false; + } + if (test_only && conn->backend->mgpu_renderer.wlr_rend) { // If we're running as a secondary GPU, we can't perform an atomic // commit without blitting a buffer. diff --git a/backend/drm/legacy.c b/backend/drm/legacy.c index d77c9d342..223852ec1 100644 --- a/backend/drm/legacy.c +++ b/backend/drm/legacy.c @@ -7,6 +7,7 @@ #include "backend/drm/fb.h" #include "backend/drm/iface.h" #include "backend/drm/util.h" +#include "render/color.h" #include "types/wlr_output.h" static bool legacy_fb_props_match(struct wlr_drm_fb *fb1, @@ -124,9 +125,17 @@ static bool legacy_crtc_commit(const struct wlr_drm_connector_state *state, } } - if (state->base->committed & WLR_OUTPUT_STATE_GAMMA_LUT) { - if (!drm_legacy_crtc_set_gamma(drm, crtc, - state->base->gamma_lut_size, state->base->gamma_lut)) { + if (state->base->committed & WLR_OUTPUT_STATE_COLOR_TRANSFORM) { + size_t dim = 0; + uint16_t *lut = NULL; + if (state->base->color_transform != NULL) { + struct wlr_color_transform_lut_3x1d *tr = + color_transform_lut_3x1d_from_base(state->base->color_transform); + dim = tr->dim; + lut = tr->lut_3x1d; + } + + if (!drm_legacy_crtc_set_gamma(drm, crtc, dim, lut)) { return false; } } From bfcb4211f629be19ba022c1813f2ac381c9f3459 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 24 May 2025 13:48:55 +0200 Subject: [PATCH 394/519] wlr_gamma_control_v1: use color transforms --- types/wlr_gamma_control_v1.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/types/wlr_gamma_control_v1.c b/types/wlr_gamma_control_v1.c index 087281423..207d1977f 100644 --- a/types/wlr_gamma_control_v1.c +++ b/types/wlr_gamma_control_v1.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -263,15 +264,20 @@ struct wlr_gamma_control_v1 *wlr_gamma_control_manager_v1_get_control( bool wlr_gamma_control_v1_apply(struct wlr_gamma_control_v1 *gamma_control, struct wlr_output_state *output_state) { - if (gamma_control == NULL || gamma_control->table == NULL) { - return wlr_output_state_set_gamma_lut(output_state, 0, NULL, NULL, NULL); + struct wlr_color_transform *tr = NULL; + if (gamma_control != NULL && gamma_control->table != NULL) { + const uint16_t *r = gamma_control->table; + const uint16_t *g = gamma_control->table + gamma_control->ramp_size; + const uint16_t *b = gamma_control->table + 2 * gamma_control->ramp_size; + + tr = wlr_color_transform_init_lut_3x1d(gamma_control->ramp_size, r, g, b); + if (tr == NULL) { + return false; + } } - const uint16_t *r = gamma_control->table; - const uint16_t *g = gamma_control->table + gamma_control->ramp_size; - const uint16_t *b = gamma_control->table + 2 * gamma_control->ramp_size; - return wlr_output_state_set_gamma_lut(output_state, - gamma_control->ramp_size, r, g, b); + wlr_output_state_set_color_transform(output_state, tr); + return true; } void wlr_gamma_control_v1_send_failed_and_destroy(struct wlr_gamma_control_v1 *gamma_control) { From a30c1021638b08654c3b6528a25da4b50ad6d6bf Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 24 Aug 2024 11:16:22 +0200 Subject: [PATCH 395/519] output: drop gamma LUT from state This has been superseded by color transforms. --- include/wlr/types/wlr_output.h | 27 ++++++----------------- types/output/output.c | 4 ---- types/output/state.c | 39 ---------------------------------- 3 files changed, 6 insertions(+), 64 deletions(-) diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 6473ece4f..dfa5ddb01 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -68,13 +68,12 @@ enum wlr_output_state_field { WLR_OUTPUT_STATE_SCALE = 1 << 4, WLR_OUTPUT_STATE_TRANSFORM = 1 << 5, WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED = 1 << 6, - WLR_OUTPUT_STATE_GAMMA_LUT = 1 << 7, - WLR_OUTPUT_STATE_RENDER_FORMAT = 1 << 8, - WLR_OUTPUT_STATE_SUBPIXEL = 1 << 9, - WLR_OUTPUT_STATE_LAYERS = 1 << 10, - WLR_OUTPUT_STATE_WAIT_TIMELINE = 1 << 11, - WLR_OUTPUT_STATE_SIGNAL_TIMELINE = 1 << 12, - WLR_OUTPUT_STATE_COLOR_TRANSFORM = 1 << 13, + WLR_OUTPUT_STATE_RENDER_FORMAT = 1 << 7, + WLR_OUTPUT_STATE_SUBPIXEL = 1 << 8, + WLR_OUTPUT_STATE_LAYERS = 1 << 9, + WLR_OUTPUT_STATE_WAIT_TIMELINE = 1 << 10, + WLR_OUTPUT_STATE_SIGNAL_TIMELINE = 1 << 11, + WLR_OUTPUT_STATE_COLOR_TRANSFORM = 1 << 12, }; enum wlr_output_state_mode_type { @@ -123,9 +122,6 @@ struct wlr_output_state { int32_t refresh; // mHz, may be zero } custom_mode; - uint16_t *gamma_lut; - size_t gamma_lut_size; - struct wlr_output_layer_state *layers; size_t layers_len; @@ -530,17 +526,6 @@ void wlr_output_state_set_subpixel(struct wlr_output_state *state, */ void wlr_output_state_set_buffer(struct wlr_output_state *state, struct wlr_buffer *buffer); -/** - * Sets the gamma table for an output. `r`, `g` and `b` are gamma ramps for - * red, green and blue. `size` is the length of the ramps and must not exceed - * the value returned by wlr_output_get_gamma_size(). - * - * Providing zero-sized ramps resets the gamma table. - * - * This state will be applied once wlr_output_commit_state() is called. - */ -bool wlr_output_state_set_gamma_lut(struct wlr_output_state *state, - size_t ramp_size, const uint16_t *r, const uint16_t *g, const uint16_t *b); /** * Sets the damage region for an output. This is used as a hint to the backend * and can be used to reduce power consumption or increase performance on some diff --git a/types/output/output.c b/types/output/output.c index 9e63ec76e..1e60bc669 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -643,10 +643,6 @@ static bool output_basic_test(struct wlr_output *output, wlr_log(WLR_DEBUG, "Tried to set format for a disabled output"); return false; } - if (!enabled && state->committed & WLR_OUTPUT_STATE_GAMMA_LUT) { - wlr_log(WLR_DEBUG, "Tried to set the gamma lut on a disabled output"); - return false; - } if (!enabled && state->committed & WLR_OUTPUT_STATE_SUBPIXEL) { wlr_log(WLR_DEBUG, "Tried to set the subpixel layout on a disabled output"); return false; diff --git a/types/output/state.c b/types/output/state.c index 1a7b00508..379fd1c51 100644 --- a/types/output/state.c +++ b/types/output/state.c @@ -17,7 +17,6 @@ void wlr_output_state_finish(struct wlr_output_state *state) { // reads it after output_state_finish(). state->buffer = NULL; pixman_region32_fini(&state->damage); - free(state->gamma_lut); wlr_drm_syncobj_timeline_unref(state->wait_timeline); wlr_drm_syncobj_timeline_unref(state->signal_timeline); } @@ -89,28 +88,6 @@ void wlr_output_state_set_damage(struct wlr_output_state *state, pixman_region32_copy(&state->damage, damage); } -bool wlr_output_state_set_gamma_lut(struct wlr_output_state *state, - size_t ramp_size, const uint16_t *r, const uint16_t *g, const uint16_t *b) { - uint16_t *gamma_lut = NULL; - if (ramp_size > 0) { - gamma_lut = realloc(state->gamma_lut, 3 * ramp_size * sizeof(uint16_t)); - if (gamma_lut == NULL) { - wlr_log_errno(WLR_ERROR, "Allocation failed"); - return false; - } - memcpy(gamma_lut, r, ramp_size * sizeof(uint16_t)); - memcpy(gamma_lut + ramp_size, g, ramp_size * sizeof(uint16_t)); - memcpy(gamma_lut + 2 * ramp_size, b, ramp_size * sizeof(uint16_t)); - } else { - free(state->gamma_lut); - } - - state->committed |= WLR_OUTPUT_STATE_GAMMA_LUT; - state->gamma_lut_size = ramp_size; - state->gamma_lut = gamma_lut; - return true; -} - void wlr_output_state_set_layers(struct wlr_output_state *state, struct wlr_output_layer_state *layers, size_t layers_len) { state->committed |= WLR_OUTPUT_STATE_LAYERS; @@ -150,7 +127,6 @@ bool wlr_output_state_copy(struct wlr_output_state *dst, struct wlr_output_state copy = *src; copy.committed &= ~(WLR_OUTPUT_STATE_BUFFER | WLR_OUTPUT_STATE_DAMAGE | - WLR_OUTPUT_STATE_GAMMA_LUT | WLR_OUTPUT_STATE_WAIT_TIMELINE | WLR_OUTPUT_STATE_SIGNAL_TIMELINE | WLR_OUTPUT_STATE_COLOR_TRANSFORM); @@ -158,8 +134,6 @@ bool wlr_output_state_copy(struct wlr_output_state *dst, copy.buffer_src_box = (struct wlr_fbox){0}; copy.buffer_dst_box = (struct wlr_box){0}; pixman_region32_init(©.damage); - copy.gamma_lut = NULL; - copy.gamma_lut_size = 0; copy.wait_timeline = NULL; copy.signal_timeline = NULL; copy.color_transform = NULL; @@ -174,15 +148,6 @@ bool wlr_output_state_copy(struct wlr_output_state *dst, wlr_output_state_set_damage(©, &src->damage); } - if (src->committed & WLR_OUTPUT_STATE_GAMMA_LUT) { - const uint16_t *r = src->gamma_lut; - const uint16_t *g = src->gamma_lut + src->gamma_lut_size; - const uint16_t *b = src->gamma_lut + 2 * src->gamma_lut_size; - if (!wlr_output_state_set_gamma_lut(©, src->gamma_lut_size, r, g, b)) { - goto err; - } - } - if (src->committed & WLR_OUTPUT_STATE_WAIT_TIMELINE) { wlr_output_state_set_wait_timeline(©, src->wait_timeline, src->wait_point); @@ -199,8 +164,4 @@ bool wlr_output_state_copy(struct wlr_output_state *dst, wlr_output_state_finish(dst); *dst = copy; return true; - -err: - wlr_output_state_finish(©); - return false; } From 2ea0e386c4cbcb3147eda56f2db02d00d2a3ea66 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 27 Jan 2025 18:04:53 +0100 Subject: [PATCH 396/519] render/vulkan: add color transformation matrix --- include/render/vulkan.h | 1 + render/vulkan/pass.c | 7 ++++++- render/vulkan/shaders/output.frag | 7 +++++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/include/render/vulkan.h b/include/render/vulkan.h index 78109356f..1db42fe4c 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -335,6 +335,7 @@ struct wlr_vk_vert_pcr_data { }; struct wlr_vk_frag_output_pcr_data { + float matrix[4][4]; // only a 3x3 subset is used float lut_3d_offset; float lut_3d_scale; }; diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 529743305..32984e3c7 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -178,6 +178,7 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { .uv_off = { 0, 0 }, .uv_size = { 1, 1 }, }; + mat3_to_mat4(final_matrix, vert_pcr_data.mat4); struct wlr_vk_color_transform *transform = NULL; size_t dim = 1; @@ -188,10 +189,14 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { } struct wlr_vk_frag_output_pcr_data frag_pcr_data = { + .matrix = { + {1, 0, 0}, + {0, 1, 0}, + {0, 0, 1}, + }, .lut_3d_offset = 0.5f / dim, .lut_3d_scale = (float)(dim - 1) / dim, }; - mat3_to_mat4(final_matrix, vert_pcr_data.mat4); if (pass->color_transform) { bind_pipeline(pass, render_buffer->plain.render_setup->output_pipe_lut3d); diff --git a/render/vulkan/shaders/output.frag b/render/vulkan/shaders/output.frag index e351079da..783817707 100644 --- a/render/vulkan/shaders/output.frag +++ b/render/vulkan/shaders/output.frag @@ -8,8 +8,9 @@ layout(location = 0) in vec2 uv; layout(location = 0) out vec4 out_color; /* struct wlr_vk_frag_output_pcr_data */ -layout(push_constant) uniform UBO { - layout(offset = 80) float lut_3d_offset; +layout(push_constant, row_major) uniform UBO { + layout(offset = 80) mat4 matrix; + float lut_3d_offset; float lut_3d_scale; } data; @@ -43,6 +44,8 @@ void main() { rgb = in_color.rgb / alpha; } + rgb = mat3(data.matrix) * rgb; + if (OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_LUT_3D) { // Apply 3D LUT vec3 pos = data.lut_3d_offset + rgb * data.lut_3d_scale; From 30c6efedf1106ccff326e1e843c21ab53d9c56e8 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 27 Jan 2025 18:35:01 +0100 Subject: [PATCH 397/519] render/vulkan: use output_pipe_srgb for non-NULL sRGB color transform --- render/vulkan/pass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 32984e3c7..2fddfb008 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -198,7 +198,7 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { .lut_3d_scale = (float)(dim - 1) / dim, }; - if (pass->color_transform) { + if (pass->color_transform && pass->color_transform->type != COLOR_TRANSFORM_SRGB) { bind_pipeline(pass, render_buffer->plain.render_setup->output_pipe_lut3d); } else { bind_pipeline(pass, render_buffer->plain.render_setup->output_pipe_srgb); From 1df2274f6cb52edc122dd9bf56e5f448eca87524 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 17 Jun 2025 19:35:16 +0200 Subject: [PATCH 398/519] render/vulkan: rename mat3_to_mat4() to encode_proj_matrix() This function is specific to projection matrices. --- render/vulkan/pass.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 2fddfb008..83a96686d 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -67,7 +67,7 @@ static float color_to_linear_premult(float non_linear, float alpha) { return (alpha == 0) ? 0 : color_to_linear(non_linear / alpha) * alpha; } -static void mat3_to_mat4(const float mat3[9], float mat4[4][4]) { +static void encode_proj_matrix(const float mat3[9], float mat4[4][4]) { memset(mat4, 0, sizeof(float) * 16); mat4[0][0] = mat3[0]; mat4[0][1] = mat3[1]; @@ -178,7 +178,7 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { .uv_off = { 0, 0 }, .uv_size = { 1, 1 }, }; - mat3_to_mat4(final_matrix, vert_pcr_data.mat4); + encode_proj_matrix(final_matrix, vert_pcr_data.mat4); struct wlr_vk_color_transform *transform = NULL; size_t dim = 1; @@ -644,7 +644,7 @@ static void render_pass_add_rect(struct wlr_render_pass *wlr_pass, .uv_off = { 0, 0 }, .uv_size = { 1, 1 }, }; - mat3_to_mat4(matrix, vert_pcr_data.mat4); + encode_proj_matrix(matrix, vert_pcr_data.mat4); bind_pipeline(pass, pipe->vk); vkCmdPushConstants(cb, pipe->layout->vk, @@ -727,7 +727,7 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, src_box.height / options->texture->height, }, }; - mat3_to_mat4(matrix, vert_pcr_data.mat4); + encode_proj_matrix(matrix, vert_pcr_data.mat4); struct wlr_vk_render_format_setup *setup = pass->srgb_pathway ? pass->render_buffer->srgb.render_setup : From a5706e2fb925a163d1675114b7e4fa744863310e Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 17 Jun 2025 19:41:29 +0200 Subject: [PATCH 399/519] render/vulkan: use array declaration in encode_proj_matrix() This makes it more obvious what the final layout of the matrix will be. --- render/vulkan/pass.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 83a96686d..b44eef0a2 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -68,17 +68,14 @@ static float color_to_linear_premult(float non_linear, float alpha) { } static void encode_proj_matrix(const float mat3[9], float mat4[4][4]) { - memset(mat4, 0, sizeof(float) * 16); - mat4[0][0] = mat3[0]; - mat4[0][1] = mat3[1]; - mat4[0][3] = mat3[2]; + float result[4][4] = { + { mat3[0], mat3[1], 0, mat3[2] }, + { mat3[3], mat3[4], 0, mat3[5] }, + { 0, 0, 1, 0 }, + { 0, 0, 0, 1 }, + }; - mat4[1][0] = mat3[3]; - mat4[1][1] = mat3[4]; - mat4[1][3] = mat3[5]; - - mat4[2][2] = 1.f; - mat4[3][3] = 1.f; + memcpy(mat4, result, sizeof(result)); } static void render_pass_destroy(struct wlr_vk_render_pass *pass) { From f3524de98095a0e595758b27db4c3e6b0d72d7d1 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 18 Jun 2025 00:07:35 +0200 Subject: [PATCH 400/519] render, render/vulkan: add primaries to wlr_buffer_pass_options --- include/render/vulkan.h | 4 ++++ include/wlr/render/pass.h | 2 ++ render/vulkan/pass.c | 38 +++++++++++++++++++++++++++++++++----- 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/include/render/vulkan.h b/include/render/vulkan.h index 1db42fe4c..b4a2852af 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -390,6 +391,9 @@ struct wlr_vk_render_pass { bool srgb_pathway; // if false, rendering via intermediate blending buffer struct wlr_color_transform *color_transform; + bool has_primaries; + struct wlr_color_primaries primaries; + struct wlr_drm_syncobj_timeline *signal_timeline; uint64_t signal_point; diff --git a/include/wlr/render/pass.h b/include/wlr/render/pass.h index 2159bc3df..1323a8c9b 100644 --- a/include/wlr/render/pass.h +++ b/include/wlr/render/pass.h @@ -33,6 +33,8 @@ struct wlr_buffer_pass_options { /* Color transform to apply to the output of the render pass, * leave NULL to indicate sRGB/no custom transform */ struct wlr_color_transform *color_transform; + /** Primaries describing the color volume of the destination buffer */ + const struct wlr_color_primaries *primaries; /* Signal a timeline synchronization point when the render pass completes. * diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index b44eef0a2..9ce77eb08 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -78,6 +78,17 @@ static void encode_proj_matrix(const float mat3[9], float mat4[4][4]) { memcpy(mat4, result, sizeof(result)); } +static void encode_color_matrix(const float mat3[9], float mat4[4][4]) { + float result[4][4] = { + { mat3[0], mat3[1], mat3[2], 0 }, + { mat3[3], mat3[4], mat3[5], 0 }, + { mat3[6], mat3[7], mat3[8], 0 }, + { 0, 0, 0, 0 }, + }; + + memcpy(mat4, result, sizeof(result)); +} + static void render_pass_destroy(struct wlr_vk_render_pass *pass) { struct wlr_vk_render_pass_texture *pass_texture; wl_array_for_each(pass_texture, &pass->textures) { @@ -186,15 +197,28 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { } struct wlr_vk_frag_output_pcr_data frag_pcr_data = { - .matrix = { - {1, 0, 0}, - {0, 1, 0}, - {0, 0, 1}, - }, .lut_3d_offset = 0.5f / dim, .lut_3d_scale = (float)(dim - 1) / dim, }; + float matrix[9]; + if (pass->has_primaries) { + struct wlr_color_primaries srgb; + wlr_color_primaries_from_named(&srgb, WLR_COLOR_NAMED_PRIMARIES_SRGB); + + float srgb_to_xyz[9]; + wlr_color_primaries_to_xyz(&srgb, srgb_to_xyz); + float dst_primaries_to_xyz[9]; + wlr_color_primaries_to_xyz(&pass->primaries, dst_primaries_to_xyz); + float xyz_to_dst_primaries[9]; + matrix_invert(xyz_to_dst_primaries, dst_primaries_to_xyz); + + wlr_matrix_multiply(matrix, srgb_to_xyz, xyz_to_dst_primaries); + } else { + wlr_matrix_identity(matrix); + } + encode_color_matrix(matrix, frag_pcr_data.matrix); + if (pass->color_transform && pass->color_transform->type != COLOR_TRANSFORM_SRGB) { bind_pipeline(pass, render_buffer->plain.render_setup->output_pipe_lut3d); } else { @@ -1106,6 +1130,10 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend pass->signal_timeline = wlr_drm_syncobj_timeline_ref(options->signal_timeline); pass->signal_point = options->signal_point; } + if (options != NULL && options->primaries != NULL) { + pass->has_primaries = true; + pass->primaries = *options->primaries; + } rect_union_init(&pass->updated_region); From e64de4d55f9c4de2b1d08ba8b41968d0535f6280 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 27 Jan 2025 20:17:34 +0100 Subject: [PATCH 401/519] output: add color primaries to output state --- include/wlr/types/wlr_output.h | 23 +++++++++++++++++++++++ types/output/output.c | 12 ++++++++++++ types/output/state.c | 31 ++++++++++++++++++++++++++++++- 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index dfa5ddb01..c94b82116 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -74,6 +75,7 @@ enum wlr_output_state_field { WLR_OUTPUT_STATE_WAIT_TIMELINE = 1 << 10, WLR_OUTPUT_STATE_SIGNAL_TIMELINE = 1 << 11, WLR_OUTPUT_STATE_COLOR_TRANSFORM = 1 << 12, + WLR_OUTPUT_STATE_IMAGE_DESCRIPTION = 1 << 13, }; enum wlr_output_state_mode_type { @@ -81,6 +83,17 @@ enum wlr_output_state_mode_type { WLR_OUTPUT_STATE_MODE_CUSTOM, }; +/** + * Colorimetric image description. + * + * Carries information about the color encoding used for a struct wlr_buffer. + * + * Supported primaries are advertised in wlr_output.supported_primaries. + */ +struct wlr_output_image_description { + enum wlr_color_named_primaries primaries; +}; + /** * Holds the double-buffered output state. */ @@ -131,6 +144,8 @@ struct wlr_output_state { uint64_t signal_point; struct wlr_color_transform *color_transform; + + struct wlr_output_image_description *image_description; }; struct wlr_output_impl; @@ -166,6 +181,8 @@ struct wlr_output { int32_t width, height; int32_t refresh; // mHz, may be zero + uint32_t supported_primaries; // bitfield of enum wlr_color_named_primaries + bool enabled; float scale; enum wl_output_subpixel subpixel; @@ -582,6 +599,12 @@ void wlr_output_state_set_signal_timeline(struct wlr_output_state *state, void wlr_output_state_set_color_transform(struct wlr_output_state *state, struct wlr_color_transform *tr); +/** + * Set the colorimetry image description. + */ +bool wlr_output_state_set_image_description(struct wlr_output_state *state, + const struct wlr_output_image_description *image_desc); + /** * Copies the output state from src to dst. It is safe to then * wlr_output_state_finish() src and have dst still be valid. diff --git a/types/output/output.c b/types/output/output.c index 1e60bc669..a0a3f9e02 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -651,6 +651,10 @@ static bool output_basic_test(struct wlr_output *output, wlr_log(WLR_DEBUG, "Tried to set a color transform on a disabled output"); return false; } + if (!enabled && state->committed & WLR_OUTPUT_STATE_IMAGE_DESCRIPTION) { + wlr_log(WLR_DEBUG, "Tried to set the image description on a disabled output"); + return false; + } if (state->committed & WLR_OUTPUT_STATE_LAYERS) { if (state->layers_len != (size_t)wl_list_length(&output->layers)) { @@ -669,6 +673,14 @@ static bool output_basic_test(struct wlr_output *output, return false; } + if ((state->committed & WLR_OUTPUT_STATE_IMAGE_DESCRIPTION) && + state->image_description != NULL) { + if (!(output->supported_primaries & state->image_description->primaries)) { + wlr_log(WLR_DEBUG, "Unsupported image description primaries"); + return false; + } + } + return true; } diff --git a/types/output/state.c b/types/output/state.c index 379fd1c51..a2e8a0042 100644 --- a/types/output/state.c +++ b/types/output/state.c @@ -19,6 +19,7 @@ void wlr_output_state_finish(struct wlr_output_state *state) { pixman_region32_fini(&state->damage); wlr_drm_syncobj_timeline_unref(state->wait_timeline); wlr_drm_syncobj_timeline_unref(state->signal_timeline); + free(state->image_description); } void wlr_output_state_set_enabled(struct wlr_output_state *state, @@ -122,6 +123,23 @@ void wlr_output_state_set_color_transform(struct wlr_output_state *state, } } +bool wlr_output_state_set_image_description(struct wlr_output_state *state, + const struct wlr_output_image_description *image_desc) { + struct wlr_output_image_description *copy = NULL; + if (image_desc != NULL) { + copy = malloc(sizeof(*copy)); + if (copy == NULL) { + return false; + } + *copy = *image_desc; + } + + state->committed |= WLR_OUTPUT_STATE_IMAGE_DESCRIPTION; + free(state->image_description); + state->image_description = copy; + return true; +} + bool wlr_output_state_copy(struct wlr_output_state *dst, const struct wlr_output_state *src) { struct wlr_output_state copy = *src; @@ -129,7 +147,8 @@ bool wlr_output_state_copy(struct wlr_output_state *dst, WLR_OUTPUT_STATE_DAMAGE | WLR_OUTPUT_STATE_WAIT_TIMELINE | WLR_OUTPUT_STATE_SIGNAL_TIMELINE | - WLR_OUTPUT_STATE_COLOR_TRANSFORM); + WLR_OUTPUT_STATE_COLOR_TRANSFORM | + WLR_OUTPUT_STATE_IMAGE_DESCRIPTION); copy.buffer = NULL; copy.buffer_src_box = (struct wlr_fbox){0}; copy.buffer_dst_box = (struct wlr_box){0}; @@ -137,6 +156,7 @@ bool wlr_output_state_copy(struct wlr_output_state *dst, copy.wait_timeline = NULL; copy.signal_timeline = NULL; copy.color_transform = NULL; + copy.image_description = NULL; if (src->committed & WLR_OUTPUT_STATE_BUFFER) { wlr_output_state_set_buffer(©, src->buffer); @@ -160,8 +180,17 @@ bool wlr_output_state_copy(struct wlr_output_state *dst, if (src->committed & WLR_OUTPUT_STATE_COLOR_TRANSFORM) { wlr_output_state_set_color_transform(©, src->color_transform); } + if (src->committed & WLR_OUTPUT_STATE_IMAGE_DESCRIPTION) { + if (!wlr_output_state_set_image_description(©, src->image_description)) { + goto err; + } + } wlr_output_state_finish(dst); *dst = copy; return true; + +err: + wlr_output_state_finish(dst); + return false; } From f024d1b8c8e0e7833ab0ebd239535f586977eba8 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 27 Jan 2025 20:24:36 +0100 Subject: [PATCH 402/519] backend/drm: add support for color primaries --- backend/drm/atomic.c | 22 ++++++++++++++++++++++ backend/drm/drm.c | 9 ++++++++- backend/drm/meson.build | 1 + backend/drm/properties.c | 1 + backend/drm/util.c | 6 ++++++ include/backend/drm/drm.h | 4 ++++ include/backend/drm/properties.h | 1 + 7 files changed, 43 insertions(+), 1 deletion(-) diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index cfef2ff36..5d42dad5f 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -178,6 +178,16 @@ bool create_fb_damage_clips_blob(struct wlr_drm_backend *drm, return true; } +static uint64_t convert_primaries_to_colorspace(uint32_t primaries) { + switch (primaries) { + case 0: + return 0; // Default + case WLR_COLOR_NAMED_PRIMARIES_BT2020: + return 9; // BT2020_RGB + } + abort(); // unreachable +} + static uint64_t max_bpc_for_format(uint32_t format) { switch (format) { case DRM_FORMAT_XRGB2101010: @@ -302,11 +312,18 @@ bool drm_atomic_connector_prepare(struct wlr_drm_connector_state *state, bool mo vrr_enabled = state->base->adaptive_sync_enabled; } + uint32_t colorspace = conn->colorspace; + if (state->base->committed & WLR_OUTPUT_STATE_IMAGE_DESCRIPTION) { + colorspace = convert_primaries_to_colorspace( + state->base->image_description ? state->base->image_description->primaries : 0); + } + state->mode_id = mode_id; state->gamma_lut = gamma_lut; state->fb_damage_clips = fb_damage_clips; state->primary_in_fence_fd = in_fence_fd; state->vrr_enabled = vrr_enabled; + state->colorspace = colorspace; return true; } @@ -335,6 +352,8 @@ void drm_atomic_connector_apply_commit(struct wlr_drm_connector_state *state) { state->base->signal_point, state->out_fence_fd); close(state->out_fence_fd); } + + conn->colorspace = state->colorspace; } void drm_atomic_connector_rollback_commit(struct wlr_drm_connector_state *state) { @@ -435,6 +454,9 @@ static void atomic_connector_add(struct atomic *atom, if (modeset && active && conn->props.max_bpc != 0 && conn->max_bpc_bounds[1] != 0) { atomic_add(atom, conn->id, conn->props.max_bpc, pick_max_bpc(conn, state->primary_fb)); } + if (conn->props.colorspace != 0) { + atomic_add(atom, conn->id, conn->props.colorspace, state->colorspace); + } atomic_add(atom, crtc->id, crtc->props.mode_id, state->mode_id); atomic_add(atom, crtc->id, crtc->props.active, active); if (active) { diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 653497813..15cb181cf 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -42,7 +42,8 @@ static const uint32_t COMMIT_OUTPUT_STATE = WLR_OUTPUT_STATE_LAYERS | WLR_OUTPUT_STATE_WAIT_TIMELINE | WLR_OUTPUT_STATE_SIGNAL_TIMELINE | - WLR_OUTPUT_STATE_COLOR_TRANSFORM; + WLR_OUTPUT_STATE_COLOR_TRANSFORM | + WLR_OUTPUT_STATE_IMAGE_DESCRIPTION; static const uint32_t SUPPORTED_OUTPUT_STATE = WLR_OUTPUT_STATE_BACKEND_OPTIONAL | COMMIT_OUTPUT_STATE; @@ -864,6 +865,12 @@ static bool drm_connector_prepare(struct wlr_drm_connector_state *conn_state, bo return false; } + if ((state->committed & WLR_OUTPUT_STATE_IMAGE_DESCRIPTION) && + conn->backend->iface != &atomic_iface) { + wlr_log(WLR_DEBUG, "Image descriptions are only supported by the atomic interface"); + return false; + } + if (test_only && conn->backend->mgpu_renderer.wlr_rend) { // If we're running as a secondary GPU, we can't perform an atomic // commit without blitting a buffer. diff --git a/backend/drm/meson.build b/backend/drm/meson.build index 666c7cc46..d9e5b77f5 100644 --- a/backend/drm/meson.build +++ b/backend/drm/meson.build @@ -7,6 +7,7 @@ hwdata = dependency( libdisplay_info = dependency( 'libdisplay-info', + version: '>=0.2.0', required: 'drm' in backends, fallback: 'libdisplay-info', not_found_message: 'Required for the DRM backend.', diff --git a/backend/drm/properties.c b/backend/drm/properties.c index 0104304e5..ca48c0d57 100644 --- a/backend/drm/properties.c +++ b/backend/drm/properties.c @@ -22,6 +22,7 @@ struct prop_info { static const struct prop_info connector_info[] = { #define INDEX(name) (offsetof(struct wlr_drm_connector_props, name) / sizeof(uint32_t)) { "CRTC_ID", INDEX(crtc_id) }, + { "Colorspace", INDEX(colorspace) }, { "DPMS", INDEX(dpms) }, { "EDID", INDEX(edid) }, { "PATH", INDEX(path) }, diff --git a/backend/drm/util.c b/backend/drm/util.c index f30898966..365ebbe08 100644 --- a/backend/drm/util.c +++ b/backend/drm/util.c @@ -83,6 +83,12 @@ void parse_edid(struct wlr_drm_connector *conn, size_t len, const uint8_t *data) output->model = di_info_get_model(info); output->serial = di_info_get_serial(info); + const struct di_supported_signal_colorimetry *colorimetry = di_info_get_supported_signal_colorimetry(info); + bool has_bt2020 = colorimetry->bt2020_cycc || colorimetry->bt2020_ycc || colorimetry->bt2020_rgb; + if (conn->props.colorspace != 0 && has_bt2020) { + output->supported_primaries |= WLR_COLOR_NAMED_PRIMARIES_BT2020; + } + di_info_destroy(info); } diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index 9409b7b10..c8f26184b 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -158,6 +158,7 @@ struct wlr_drm_connector_state { uint32_t fb_damage_clips; int primary_in_fence_fd, out_fence_fd; bool vrr_enabled; + uint32_t colorspace; }; /** @@ -212,6 +213,9 @@ struct wlr_drm_connector { // Last committed page-flip struct wlr_drm_page_flip *pending_page_flip; + // Atomic modesetting only + uint32_t colorspace; + int32_t refresh; }; diff --git a/include/backend/drm/properties.h b/include/backend/drm/properties.h index 421eb4275..870ea4379 100644 --- a/include/backend/drm/properties.h +++ b/include/backend/drm/properties.h @@ -26,6 +26,7 @@ struct wlr_drm_connector_props { // atomic-modesetting only uint32_t crtc_id; + uint32_t colorspace; }; struct wlr_drm_crtc_props { From 8430a1922d9f8762c8fef9c63f1cb076caa71bc5 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 28 Jan 2025 10:46:25 +0100 Subject: [PATCH 403/519] render/vulkan: add PQ inverse EOTF to output shader --- include/render/vulkan.h | 4 +++- render/vulkan/renderer.c | 10 ++++++++-- render/vulkan/shaders/output.frag | 16 +++++++++++++++- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/include/render/vulkan.h b/include/render/vulkan.h index b4a2852af..530fa1d27 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -163,7 +163,8 @@ enum wlr_vk_shader_source { // fragment shader. Must match those in shaders/output.frag enum wlr_vk_output_transform { WLR_VK_OUTPUT_TRANSFORM_INVERSE_SRGB = 0, - WLR_VK_OUTPUT_TRANSFORM_LUT3D = 1, + WLR_VK_OUTPUT_TRANSFORM_INVERSE_ST2084_PQ = 1, + WLR_VK_OUTPUT_TRANSFORM_LUT3D = 2, }; struct wlr_vk_pipeline_key { @@ -193,6 +194,7 @@ struct wlr_vk_render_format_setup { VkRenderPass render_pass; VkPipeline output_pipe_srgb; + VkPipeline output_pipe_pq; VkPipeline output_pipe_lut3d; struct wlr_vk_renderer *renderer; diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index b2feca013..98701626f 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -159,6 +159,7 @@ static void destroy_render_format_setup(struct wlr_vk_renderer *renderer, VkDevice dev = renderer->dev->dev; vkDestroyRenderPass(dev, setup->render_pass, NULL); vkDestroyPipeline(dev, setup->output_pipe_srgb, NULL); + vkDestroyPipeline(dev, setup->output_pipe_pq, NULL); vkDestroyPipeline(dev, setup->output_pipe_lut3d, NULL); struct wlr_vk_pipeline *pipeline, *tmp_pipeline; @@ -2299,8 +2300,13 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup( goto error; } if (!init_blend_to_output_pipeline( - renderer, setup->render_pass, renderer->output_pipe_layout, - &setup->output_pipe_srgb, WLR_VK_OUTPUT_TRANSFORM_INVERSE_SRGB)) { + renderer, setup->render_pass, renderer->output_pipe_layout, + &setup->output_pipe_srgb, WLR_VK_OUTPUT_TRANSFORM_INVERSE_SRGB)) { + goto error; + } + if (!init_blend_to_output_pipeline( + renderer, setup->render_pass, renderer->output_pipe_layout, + &setup->output_pipe_pq, WLR_VK_OUTPUT_TRANSFORM_INVERSE_ST2084_PQ)) { goto error; } } else { diff --git a/render/vulkan/shaders/output.frag b/render/vulkan/shaders/output.frag index 783817707..7478e249d 100644 --- a/render/vulkan/shaders/output.frag +++ b/render/vulkan/shaders/output.frag @@ -18,7 +18,8 @@ layout (constant_id = 0) const int OUTPUT_TRANSFORM = 0; // Matches enum wlr_vk_output_transform #define OUTPUT_TRANSFORM_INVERSE_SRGB 0 -#define OUTPUT_TRANSFORM_LUT_3D 1 +#define OUTPUT_TRANSFORM_INVERSE_ST2084_PQ 1 +#define OUTPUT_TRANSFORM_LUT_3D 2 float linear_channel_to_srgb(float x) { return max(min(x * 12.92, 0.04045), 1.055 * pow(x, 1. / 2.4) - 0.055); @@ -32,6 +33,17 @@ vec3 linear_color_to_srgb(vec3 color) { ); } +vec3 linear_color_to_pq(vec3 color) { + // H.273 TransferCharacteristics code point 16 + float c1 = 0.8359375; + float c2 = 18.8515625; + float c3 = 18.6875; + float m = 78.84375; + float n = 0.1593017578125; + vec3 pow_n = pow(clamp(color, vec3(0), vec3(1)), vec3(n)); + return pow((vec3(c1) + c2 * pow_n) / (vec3(1) + c3 * pow_n), vec3(m)); +} + void main() { vec4 in_color = subpassLoad(in_color).rgba; @@ -50,6 +62,8 @@ void main() { // Apply 3D LUT vec3 pos = data.lut_3d_offset + rgb * data.lut_3d_scale; rgb = texture(lut_3d, pos).rgb; + } else if (OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_INVERSE_ST2084_PQ) { + rgb = linear_color_to_pq(rgb); } else { // OUTPUT_TRANSFORM_INVERSE_SRGB // Produce sRGB encoded values rgb = linear_color_to_srgb(rgb); From 44706835911202e427941c4e76abd8045407225b Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 28 Jan 2025 12:15:51 +0100 Subject: [PATCH 404/519] render/color, render/vulkan: add support for PQ transfer function --- include/render/color.h | 19 ++++++++++++++++--- include/wlr/render/color.h | 7 ++++--- render/color.c | 19 ++++++++++++++----- render/vulkan/pass.c | 28 ++++++++++++++++++++++------ 4 files changed, 56 insertions(+), 17 deletions(-) diff --git a/include/render/color.h b/include/render/color.h index a797881bf..8730ac6e9 100644 --- a/include/render/color.h +++ b/include/render/color.h @@ -6,7 +6,7 @@ #include enum wlr_color_transform_type { - COLOR_TRANSFORM_SRGB, + COLOR_TRANSFORM_INVERSE_EOTF, COLOR_TRANSFORM_LCMS2, COLOR_TRANSFORM_LUT_3X1D, }; @@ -18,8 +18,11 @@ struct wlr_color_transform { enum wlr_color_transform_type type; }; -void wlr_color_transform_init(struct wlr_color_transform *tr, - enum wlr_color_transform_type type); +struct wlr_color_transform_inverse_eotf { + struct wlr_color_transform base; + + enum wlr_color_transfer_function tf; +}; /** * The formula is approximated via three 1D look-up tables. The flat lut_3x1d @@ -36,6 +39,9 @@ struct wlr_color_transform_lut_3x1d { size_t dim; }; +void wlr_color_transform_init(struct wlr_color_transform *tr, + enum wlr_color_transform_type type); + /** * Get a struct wlr_color_transform_lcms2 from a generic struct wlr_color_transform. * Asserts that the base type is COLOR_TRANSFORM_LCMS2. @@ -51,6 +57,13 @@ void color_transform_lcms2_finish(struct wlr_color_transform_lcms2 *tr); void color_transform_lcms2_eval(struct wlr_color_transform_lcms2 *tr, float out[static 3], const float in[static 3]); +/** + * Gets a wlr_color_transform_inverse_eotf from a generic wlr_color_transform. + * Asserts that the base type is COLOR_TRANSFORM_INVERSE_EOTF + */ +struct wlr_color_transform_inverse_eotf *wlr_color_transform_inverse_eotf_from_base( + struct wlr_color_transform *tr); + /** * Get a struct wlr_color_transform_lut_3x1d from a generic * struct wlr_color_transform. Asserts that the base type is diff --git a/include/wlr/render/color.h b/include/wlr/render/color.h index c2cbee265..42ce5bc30 100644 --- a/include/wlr/render/color.h +++ b/include/wlr/render/color.h @@ -74,10 +74,11 @@ struct wlr_color_transform *wlr_color_transform_init_linear_to_icc( const void *data, size_t size); /** - * Initialize a color transformation to apply sRGB encoding. - * Returns NULL on failure. + * Initialize a color transformation to apply EOTF⁻¹ encoding. Returns + * NULL on failure. */ -struct wlr_color_transform *wlr_color_transform_init_srgb(void); +struct wlr_color_transform *wlr_color_transform_init_linear_to_inverse_eotf( + enum wlr_color_transfer_function tf); /** * Initialize a color transformation to apply three 1D look-up tables. dim diff --git a/render/color.c b/render/color.c index 2bd8d65ec..ca8236515 100644 --- a/render/color.c +++ b/render/color.c @@ -29,13 +29,15 @@ void wlr_color_transform_init(struct wlr_color_transform *tr, enum wlr_color_tra wlr_addon_set_init(&tr->addons); } -struct wlr_color_transform *wlr_color_transform_init_srgb(void) { - struct wlr_color_transform *tx = calloc(1, sizeof(*tx)); +struct wlr_color_transform *wlr_color_transform_init_linear_to_inverse_eotf( + enum wlr_color_transfer_function tf) { + struct wlr_color_transform_inverse_eotf *tx = calloc(1, sizeof(*tx)); if (!tx) { return NULL; } - wlr_color_transform_init(tx, COLOR_TRANSFORM_SRGB); - return tx; + wlr_color_transform_init(&tx->base, COLOR_TRANSFORM_INVERSE_EOTF); + tx->tf = tf; + return &tx->base; } struct wlr_color_transform *wlr_color_transform_init_lut_3x1d(size_t dim, @@ -62,7 +64,7 @@ struct wlr_color_transform *wlr_color_transform_init_lut_3x1d(size_t dim, static void color_transform_destroy(struct wlr_color_transform *tr) { switch (tr->type) { - case COLOR_TRANSFORM_SRGB: + case COLOR_TRANSFORM_INVERSE_EOTF: break; case COLOR_TRANSFORM_LCMS2: color_transform_lcms2_finish(color_transform_lcms2_from_base(tr)); @@ -92,6 +94,13 @@ void wlr_color_transform_unref(struct wlr_color_transform *tr) { } } +struct wlr_color_transform_inverse_eotf *wlr_color_transform_inverse_eotf_from_base( + struct wlr_color_transform *tr) { + assert(tr->type == COLOR_TRANSFORM_INVERSE_EOTF); + struct wlr_color_transform_inverse_eotf *inverse_eotf = wl_container_of(tr, inverse_eotf, base); + return inverse_eotf; +} + struct wlr_color_transform_lut_3x1d *color_transform_lut_3x1d_from_base( struct wlr_color_transform *tr) { assert(tr->type == COLOR_TRANSFORM_LUT_3X1D); diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 9ce77eb08..68ee78e67 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -190,7 +190,7 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { struct wlr_vk_color_transform *transform = NULL; size_t dim = 1; - if (pass->color_transform && pass->color_transform->type != COLOR_TRANSFORM_SRGB) { + if (pass->color_transform && pass->color_transform->type != COLOR_TRANSFORM_INVERSE_EOTF) { transform = get_color_transform(pass->color_transform, renderer); assert(transform); dim = transform->lut_3d.dim; @@ -219,11 +219,27 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { } encode_color_matrix(matrix, frag_pcr_data.matrix); - if (pass->color_transform && pass->color_transform->type != COLOR_TRANSFORM_SRGB) { - bind_pipeline(pass, render_buffer->plain.render_setup->output_pipe_lut3d); + VkPipeline pipeline = VK_NULL_HANDLE; + if (pass->color_transform && pass->color_transform->type != COLOR_TRANSFORM_INVERSE_EOTF) { + pipeline = render_buffer->plain.render_setup->output_pipe_lut3d; } else { - bind_pipeline(pass, render_buffer->plain.render_setup->output_pipe_srgb); + enum wlr_color_transfer_function tf = WLR_COLOR_TRANSFER_FUNCTION_SRGB; + if (pass->color_transform && pass->color_transform->type == COLOR_TRANSFORM_INVERSE_EOTF) { + struct wlr_color_transform_inverse_eotf *inverse_eotf = + wlr_color_transform_inverse_eotf_from_base(pass->color_transform); + tf = inverse_eotf->tf; + } + + switch (tf) { + case WLR_COLOR_TRANSFER_FUNCTION_SRGB: + pipeline = render_buffer->plain.render_setup->output_pipe_srgb; + break; + case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ: + pipeline = render_buffer->plain.render_setup->output_pipe_pq; + break; + } } + bind_pipeline(pass, pipeline); vkCmdPushConstants(render_cb->vk, renderer->output_pipe_layout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(vert_pcr_data), &vert_pcr_data); vkCmdPushConstants(render_cb->vk, renderer->output_pipe_layout, @@ -880,7 +896,7 @@ static bool create_3d_lut_image(struct wlr_vk_renderer *renderer, struct wlr_color_transform_lcms2 *tr_lcms2 = NULL; struct wlr_color_transform_lut_3x1d *tr_lut_3x1d = NULL; switch (tr->type) { - case COLOR_TRANSFORM_SRGB: + case COLOR_TRANSFORM_INVERSE_EOTF: abort(); // unreachable case COLOR_TRANSFORM_LCMS2: tr_lcms2 = color_transform_lcms2_from_base(tr); @@ -1061,7 +1077,7 @@ static struct wlr_vk_color_transform *vk_color_transform_create( return NULL; } - if (transform->type != COLOR_TRANSFORM_SRGB) { + if (transform->type != COLOR_TRANSFORM_INVERSE_EOTF) { vk_transform->lut_3d.dim = 33; if (!create_3d_lut_image(renderer, transform, vk_transform->lut_3d.dim, From dc258b2237df9aaf898efa849bcb8e2d1cddec68 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 28 Jan 2025 12:40:40 +0100 Subject: [PATCH 405/519] output: add transfer function to image description --- include/wlr/types/wlr_output.h | 4 ++++ types/output/output.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index c94b82116..e6cd58824 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -89,9 +89,12 @@ enum wlr_output_state_mode_type { * Carries information about the color encoding used for a struct wlr_buffer. * * Supported primaries are advertised in wlr_output.supported_primaries. + * Supported transfer functions are advertised in + * wlr_output.supported_transfer_functions. */ struct wlr_output_image_description { enum wlr_color_named_primaries primaries; + enum wlr_color_transfer_function transfer_function; }; /** @@ -182,6 +185,7 @@ struct wlr_output { int32_t refresh; // mHz, may be zero uint32_t supported_primaries; // bitfield of enum wlr_color_named_primaries + uint32_t supported_transfer_functions; // bitfield of enum wlr_color_transfer_function bool enabled; float scale; diff --git a/types/output/output.c b/types/output/output.c index a0a3f9e02..8576bb0e6 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -679,6 +679,10 @@ static bool output_basic_test(struct wlr_output *output, wlr_log(WLR_DEBUG, "Unsupported image description primaries"); return false; } + if (!(output->supported_transfer_functions & state->image_description->transfer_function)) { + wlr_log(WLR_DEBUG, "Unsupported image description transfer function"); + return false; + } } return true; From b482e9089bfaa4698966fde476cb88cf66a1faab Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 28 Jan 2025 12:41:05 +0100 Subject: [PATCH 406/519] backend/drm: add support for image description transfer function --- backend/drm/atomic.c | 43 ++++++++++++++++++++++++++++++++ backend/drm/properties.c | 1 + backend/drm/util.c | 5 ++++ include/backend/drm/drm.h | 2 ++ include/backend/drm/properties.h | 1 + 5 files changed, 52 insertions(+) diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index 5d42dad5f..a952dc426 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -178,6 +178,37 @@ bool create_fb_damage_clips_blob(struct wlr_drm_backend *drm, return true; } +static uint8_t convert_cta861_eotf(enum wlr_color_transfer_function tf) { + switch (tf) { + case WLR_COLOR_TRANSFER_FUNCTION_SRGB: + abort(); // unsupported + case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ: + return 2; + } + abort(); // unreachable +} + +static bool create_hdr_output_metadata_blob(struct wlr_drm_backend *drm, + const struct wlr_output_image_description *img_desc, uint32_t *blob_id) { + if (img_desc == NULL) { + *blob_id = 0; + return true; + } + + struct hdr_output_metadata metadata = { + .metadata_type = 0, + .hdmi_metadata_type1 = { + .eotf = convert_cta861_eotf(img_desc->transfer_function), + .metadata_type = 0, + }, + }; + if (drmModeCreatePropertyBlob(drm->fd, &metadata, sizeof(metadata), blob_id) != 0) { + wlr_log_errno(WLR_ERROR, "Failed to create HDR_OUTPUT_METADATA property"); + return false; + } + return true; +} + static uint64_t convert_primaries_to_colorspace(uint32_t primaries) { switch (primaries) { case 0: @@ -318,12 +349,19 @@ bool drm_atomic_connector_prepare(struct wlr_drm_connector_state *state, bool mo state->base->image_description ? state->base->image_description->primaries : 0); } + uint32_t hdr_output_metadata = conn->hdr_output_metadata; + if ((state->base->committed & WLR_OUTPUT_STATE_IMAGE_DESCRIPTION) && + !create_hdr_output_metadata_blob(drm, state->base->image_description, &hdr_output_metadata)) { + return false; + } + state->mode_id = mode_id; state->gamma_lut = gamma_lut; state->fb_damage_clips = fb_damage_clips; state->primary_in_fence_fd = in_fence_fd; state->vrr_enabled = vrr_enabled; state->colorspace = colorspace; + state->hdr_output_metadata = hdr_output_metadata; return true; } @@ -338,6 +376,7 @@ void drm_atomic_connector_apply_commit(struct wlr_drm_connector_state *state) { crtc->own_mode_id = true; commit_blob(drm, &crtc->mode_id, state->mode_id); commit_blob(drm, &crtc->gamma_lut, state->gamma_lut); + commit_blob(drm, &conn->hdr_output_metadata, state->hdr_output_metadata); conn->output.adaptive_sync_status = state->vrr_enabled ? WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED : WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED; @@ -363,6 +402,7 @@ void drm_atomic_connector_rollback_commit(struct wlr_drm_connector_state *state) rollback_blob(drm, &crtc->mode_id, state->mode_id); rollback_blob(drm, &crtc->gamma_lut, state->gamma_lut); + rollback_blob(drm, &conn->hdr_output_metadata, state->hdr_output_metadata); destroy_blob(drm, state->fb_damage_clips); if (state->primary_in_fence_fd >= 0) { @@ -457,6 +497,9 @@ static void atomic_connector_add(struct atomic *atom, if (conn->props.colorspace != 0) { atomic_add(atom, conn->id, conn->props.colorspace, state->colorspace); } + if (conn->props.hdr_output_metadata != 0) { + atomic_add(atom, conn->id, conn->props.hdr_output_metadata, state->hdr_output_metadata); + } atomic_add(atom, crtc->id, crtc->props.mode_id, state->mode_id); atomic_add(atom, crtc->id, crtc->props.active, active); if (active) { diff --git a/backend/drm/properties.c b/backend/drm/properties.c index ca48c0d57..314023954 100644 --- a/backend/drm/properties.c +++ b/backend/drm/properties.c @@ -25,6 +25,7 @@ static const struct prop_info connector_info[] = { { "Colorspace", INDEX(colorspace) }, { "DPMS", INDEX(dpms) }, { "EDID", INDEX(edid) }, + { "HDR_OUTPUT_METADATA", INDEX(hdr_output_metadata) }, { "PATH", INDEX(path) }, { "content type", INDEX(content_type) }, { "link-status", INDEX(link_status) }, diff --git a/backend/drm/util.c b/backend/drm/util.c index 365ebbe08..dd2b37351 100644 --- a/backend/drm/util.c +++ b/backend/drm/util.c @@ -89,6 +89,11 @@ void parse_edid(struct wlr_drm_connector *conn, size_t len, const uint8_t *data) output->supported_primaries |= WLR_COLOR_NAMED_PRIMARIES_BT2020; } + const struct di_hdr_static_metadata *hdr_static_metadata = di_info_get_hdr_static_metadata(info); + if (conn->props.hdr_output_metadata != 0 && hdr_static_metadata->type1 && hdr_static_metadata->pq) { + output->supported_transfer_functions |= WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ; + } + di_info_destroy(info); } diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index c8f26184b..af4231f54 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -159,6 +159,7 @@ struct wlr_drm_connector_state { int primary_in_fence_fd, out_fence_fd; bool vrr_enabled; uint32_t colorspace; + uint32_t hdr_output_metadata; }; /** @@ -215,6 +216,7 @@ struct wlr_drm_connector { // Atomic modesetting only uint32_t colorspace; + uint32_t hdr_output_metadata; int32_t refresh; }; diff --git a/include/backend/drm/properties.h b/include/backend/drm/properties.h index 870ea4379..c02d655ba 100644 --- a/include/backend/drm/properties.h +++ b/include/backend/drm/properties.h @@ -27,6 +27,7 @@ struct wlr_drm_connector_props { uint32_t crtc_id; uint32_t colorspace; + uint32_t hdr_output_metadata; }; struct wlr_drm_crtc_props { From 7b6eec530c03e3e611d32269497416e728643897 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 27 Feb 2025 18:36:15 +0100 Subject: [PATCH 407/519] render/vulkan: add luminance multipler for output shader --- include/render/vulkan.h | 1 + render/vulkan/pass.c | 12 ++++++++++++ render/vulkan/shaders/output.frag | 3 +++ 3 files changed, 16 insertions(+) diff --git a/include/render/vulkan.h b/include/render/vulkan.h index 530fa1d27..b6bd0c2e8 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -339,6 +339,7 @@ struct wlr_vk_vert_pcr_data { struct wlr_vk_frag_output_pcr_data { float matrix[4][4]; // only a 3x3 subset is used + float luminance_multiplier; float lut_3d_offset; float lut_3d_scale; }; diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 68ee78e67..ab044f6a0 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -149,6 +149,11 @@ static VkSemaphore render_pass_wait_sync_file(struct wlr_vk_render_pass *pass, return *sem_ptr; } +static float get_luminance_multiplier(const struct wlr_color_luminances *src_lum, + const struct wlr_color_luminances *dst_lum) { + return (dst_lum->reference / src_lum->reference) * (src_lum->max / dst_lum->max); +} + static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { struct wlr_vk_render_pass *pass = get_render_pass(wlr_pass); struct wlr_vk_renderer *renderer = pass->renderer; @@ -197,6 +202,7 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { } struct wlr_vk_frag_output_pcr_data frag_pcr_data = { + .luminance_multiplier = 1, .lut_3d_offset = 0.5f / dim, .lut_3d_scale = (float)(dim - 1) / dim, }; @@ -238,6 +244,12 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { pipeline = render_buffer->plain.render_setup->output_pipe_pq; break; } + + struct wlr_color_luminances srgb_lum, dst_lum; + wlr_color_transfer_function_get_default_luminance( + WLR_COLOR_TRANSFER_FUNCTION_SRGB, &srgb_lum); + wlr_color_transfer_function_get_default_luminance(tf, &dst_lum); + frag_pcr_data.luminance_multiplier = get_luminance_multiplier(&srgb_lum, &dst_lum); } bind_pipeline(pass, pipeline); vkCmdPushConstants(render_cb->vk, renderer->output_pipe_layout, diff --git a/render/vulkan/shaders/output.frag b/render/vulkan/shaders/output.frag index 7478e249d..f2d5d645c 100644 --- a/render/vulkan/shaders/output.frag +++ b/render/vulkan/shaders/output.frag @@ -10,6 +10,7 @@ layout(location = 0) out vec4 out_color; /* struct wlr_vk_frag_output_pcr_data */ layout(push_constant, row_major) uniform UBO { layout(offset = 80) mat4 matrix; + float luminance_multiplier; float lut_3d_offset; float lut_3d_scale; } data; @@ -56,6 +57,8 @@ void main() { rgb = in_color.rgb / alpha; } + rgb *= data.luminance_multiplier; + rgb = mat3(data.matrix) * rgb; if (OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_LUT_3D) { From f5a09926868d81d81f1029c7ca6077af0974b9ba Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 18 Jun 2025 21:25:37 +0200 Subject: [PATCH 408/519] render/vulkan: fix multiplication order for output color matrix This had the same bug as the texture side, but I forgot to fix it. See: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4988#note_2867416 Fixes: f3524de98095 ("render, render/vulkan: add primaries to wlr_buffer_pass_options") --- render/vulkan/pass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index ab044f6a0..290d8e2ca 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -219,7 +219,7 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { float xyz_to_dst_primaries[9]; matrix_invert(xyz_to_dst_primaries, dst_primaries_to_xyz); - wlr_matrix_multiply(matrix, srgb_to_xyz, xyz_to_dst_primaries); + wlr_matrix_multiply(matrix, xyz_to_dst_primaries, srgb_to_xyz); } else { wlr_matrix_identity(matrix); } From 0ee0452af08b7fbf5a4c7a4e3aedb34ad5058a02 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 23 Feb 2025 14:57:04 +0100 Subject: [PATCH 409/519] render/color, render/vulkan: add EXT_LINEAR to enum wlr_color_transfer_function --- backend/drm/atomic.c | 2 ++ include/render/vulkan.h | 8 +++++--- include/wlr/render/color.h | 1 + render/vulkan/pass.c | 3 +++ render/vulkan/renderer.c | 6 ++++++ render/vulkan/shaders/output.frag | 9 +++++---- 6 files changed, 22 insertions(+), 7 deletions(-) diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index a952dc426..66569232b 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -184,6 +184,8 @@ static uint8_t convert_cta861_eotf(enum wlr_color_transfer_function tf) { abort(); // unsupported case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ: return 2; + case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR: + abort(); // unsupported } abort(); // unreachable } diff --git a/include/render/vulkan.h b/include/render/vulkan.h index b6bd0c2e8..1d4b8a0ba 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -162,9 +162,10 @@ enum wlr_vk_shader_source { // Constants used to pick the color transform for the blend-to-output // fragment shader. Must match those in shaders/output.frag enum wlr_vk_output_transform { - WLR_VK_OUTPUT_TRANSFORM_INVERSE_SRGB = 0, - WLR_VK_OUTPUT_TRANSFORM_INVERSE_ST2084_PQ = 1, - WLR_VK_OUTPUT_TRANSFORM_LUT3D = 2, + WLR_VK_OUTPUT_TRANSFORM_IDENTITY = 0, + WLR_VK_OUTPUT_TRANSFORM_INVERSE_SRGB = 1, + WLR_VK_OUTPUT_TRANSFORM_INVERSE_ST2084_PQ = 2, + WLR_VK_OUTPUT_TRANSFORM_LUT3D = 3, }; struct wlr_vk_pipeline_key { @@ -193,6 +194,7 @@ struct wlr_vk_render_format_setup { bool use_blending_buffer; VkRenderPass render_pass; + VkPipeline output_pipe_identity; VkPipeline output_pipe_srgb; VkPipeline output_pipe_pq; VkPipeline output_pipe_lut3d; diff --git a/include/wlr/render/color.h b/include/wlr/render/color.h index 42ce5bc30..3341668a8 100644 --- a/include/wlr/render/color.h +++ b/include/wlr/render/color.h @@ -27,6 +27,7 @@ enum wlr_color_named_primaries { enum wlr_color_transfer_function { WLR_COLOR_TRANSFER_FUNCTION_SRGB = 1 << 0, WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ = 1 << 1, + WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR = 1 << 2, }; /** diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 290d8e2ca..75c454c87 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -237,6 +237,9 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { } switch (tf) { + case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR: + pipeline = render_buffer->plain.render_setup->output_pipe_identity; + break; case WLR_COLOR_TRANSFER_FUNCTION_SRGB: pipeline = render_buffer->plain.render_setup->output_pipe_srgb; break; diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index 98701626f..cbfb022f7 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -158,6 +158,7 @@ static void destroy_render_format_setup(struct wlr_vk_renderer *renderer, VkDevice dev = renderer->dev->dev; vkDestroyRenderPass(dev, setup->render_pass, NULL); + vkDestroyPipeline(dev, setup->output_pipe_identity, NULL); vkDestroyPipeline(dev, setup->output_pipe_srgb, NULL); vkDestroyPipeline(dev, setup->output_pipe_pq, NULL); vkDestroyPipeline(dev, setup->output_pipe_lut3d, NULL); @@ -2294,6 +2295,11 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup( } // this is only well defined if render pass has a 2nd subpass + if (!init_blend_to_output_pipeline( + renderer, setup->render_pass, renderer->output_pipe_layout, + &setup->output_pipe_identity, WLR_VK_OUTPUT_TRANSFORM_IDENTITY)) { + goto error; + } if (!init_blend_to_output_pipeline( renderer, setup->render_pass, renderer->output_pipe_layout, &setup->output_pipe_lut3d, WLR_VK_OUTPUT_TRANSFORM_LUT3D)) { diff --git a/render/vulkan/shaders/output.frag b/render/vulkan/shaders/output.frag index f2d5d645c..3d5ac4089 100644 --- a/render/vulkan/shaders/output.frag +++ b/render/vulkan/shaders/output.frag @@ -18,9 +18,10 @@ layout(push_constant, row_major) uniform UBO { layout (constant_id = 0) const int OUTPUT_TRANSFORM = 0; // Matches enum wlr_vk_output_transform -#define OUTPUT_TRANSFORM_INVERSE_SRGB 0 -#define OUTPUT_TRANSFORM_INVERSE_ST2084_PQ 1 -#define OUTPUT_TRANSFORM_LUT_3D 2 +#define OUTPUT_TRANSFORM_IDENTITY 0 +#define OUTPUT_TRANSFORM_INVERSE_SRGB 1 +#define OUTPUT_TRANSFORM_INVERSE_ST2084_PQ 2 +#define OUTPUT_TRANSFORM_LUT_3D 3 float linear_channel_to_srgb(float x) { return max(min(x * 12.92, 0.04045), 1.055 * pow(x, 1. / 2.4) - 0.055); @@ -67,7 +68,7 @@ void main() { rgb = texture(lut_3d, pos).rgb; } else if (OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_INVERSE_ST2084_PQ) { rgb = linear_color_to_pq(rgb); - } else { // OUTPUT_TRANSFORM_INVERSE_SRGB + } else if (OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_INVERSE_SRGB) { // Produce sRGB encoded values rgb = linear_color_to_srgb(rgb); } From c8d94000a656934e58ddf1531f7f02499734e20c Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 27 Feb 2025 23:06:18 +0100 Subject: [PATCH 410/519] color-management-v1: add EXT_LINEAR --- types/wlr_color_management_v1.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/types/wlr_color_management_v1.c b/types/wlr_color_management_v1.c index bdf13768d..b5e9a65fa 100644 --- a/types/wlr_color_management_v1.c +++ b/types/wlr_color_management_v1.c @@ -81,6 +81,8 @@ static enum wlr_color_transfer_function transfer_function_to_wlr( return WLR_COLOR_TRANSFER_FUNCTION_SRGB; case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ: return WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ; + case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR: + return WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR; default: abort(); } From dd3d9be41e02ff5b0dfce2eb6cf009322acbfbf2 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 23 Feb 2025 14:58:04 +0100 Subject: [PATCH 411/519] render/pass: add wlr_render_texture_options.transfer_function Also add a bit in wlr_renderer.features to indicate support. --- include/wlr/render/pass.h | 3 +++ include/wlr/render/wlr_renderer.h | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/include/wlr/render/pass.h b/include/wlr/render/pass.h index 1323a8c9b..b8854bbf9 100644 --- a/include/wlr/render/pass.h +++ b/include/wlr/render/pass.h @@ -12,6 +12,7 @@ #include #include #include +#include #include struct wlr_renderer; @@ -101,6 +102,8 @@ struct wlr_render_texture_options { enum wlr_scale_filter_mode filter_mode; /* Blend mode */ enum wlr_render_blend_mode blend_mode; + /* Transfer function the source texture is encoded with */ + enum wlr_color_transfer_function transfer_function; /* Wait for a timeline synchronization point before texturing. * diff --git a/include/wlr/render/wlr_renderer.h b/include/wlr/render/wlr_renderer.h index debeb4e29..62a1cca2d 100644 --- a/include/wlr/render/wlr_renderer.h +++ b/include/wlr/render/wlr_renderer.h @@ -41,6 +41,10 @@ struct wlr_renderer { } events; struct { + /** + * Whether color transforms are supported for input textures + */ + bool input_color_transform; /** * Does the renderer support color transforms on its output? */ From b1a9dab03e6e406b8ef71a0bcc8281ed6c2773aa Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 23 Feb 2025 15:08:04 +0100 Subject: [PATCH 412/519] render/vulkan: fix typo in wlr_vk_texture.views comment --- include/render/vulkan.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/render/vulkan.h b/include/render/vulkan.h index 1d4b8a0ba..e2846565c 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -478,7 +478,7 @@ struct wlr_vk_texture { struct wlr_buffer *buffer; struct wlr_addon buffer_addon; - struct wl_list views; // struct wlr_vk_texture_ds.link + struct wl_list views; // struct wlr_vk_texture_view.link }; struct wlr_vk_texture *vulkan_get_texture(struct wlr_texture *wlr_texture); From 8d1c6e42ac708927b814518cb7f9e48863b79743 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 23 Feb 2025 15:08:50 +0100 Subject: [PATCH 413/519] render/vulkan: add support for texture transfer functions --- include/render/vulkan.h | 6 +++--- render/vulkan/pass.c | 12 ++++++++++-- render/vulkan/renderer.c | 1 + render/vulkan/texture.c | 14 ++++++++------ 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/include/render/vulkan.h b/include/render/vulkan.h index e2846565c..651106042 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -349,6 +349,7 @@ struct wlr_vk_frag_output_pcr_data { struct wlr_vk_texture_view { struct wl_list link; // struct wlr_vk_texture.views const struct wlr_vk_pipeline_layout *layout; + bool srgb; VkDescriptorSet ds; VkImageView image_view; @@ -363,7 +364,7 @@ struct wlr_vk_pipeline_layout *get_or_create_pipeline_layout( const struct wlr_vk_pipeline_layout_key *key); struct wlr_vk_texture_view *vulkan_texture_get_or_create_view( struct wlr_vk_texture *texture, - const struct wlr_vk_pipeline_layout *layout); + const struct wlr_vk_pipeline_layout *layout, bool srgb); // Creates a vulkan renderer for the given device. struct wlr_renderer *vulkan_renderer_create_for_device(struct wlr_vk_device *dev); @@ -463,13 +464,12 @@ struct wlr_vk_texture { VkDeviceMemory memories[WLR_DMABUF_MAX_PLANES]; VkImage image; const struct wlr_vk_format *format; - enum wlr_vk_texture_transform transform; struct wlr_vk_command_buffer *last_used_cb; // to track when it can be destroyed bool dmabuf_imported; bool owned; // if dmabuf_imported: whether we have ownership of the image bool transitioned; // if dma_imported: whether we transitioned it away from preinit bool has_alpha; // whether the image is has alpha channel - bool using_mutable_srgb; // is this accessed through _SRGB format view + bool using_mutable_srgb; // can be accessed through _SRGB format view struct wl_list foreign_link; // wlr_vk_renderer.foreign_textures struct wl_list destroy_link; // wlr_vk_command_buffer.destroy_textures struct wl_list link; // wlr_vk_renderer.textures diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 75c454c87..95fbd319e 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -781,6 +781,14 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, }; encode_proj_matrix(matrix, vert_pcr_data.mat4); + bool srgb = options->transfer_function == 0 || + options->transfer_function == WLR_COLOR_TRANSFER_FUNCTION_SRGB; + bool srgb_image_view = srgb && texture->using_mutable_srgb; + enum wlr_vk_texture_transform tex_transform = + srgb_image_view || options->transfer_function == WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR ? + WLR_VK_TEXTURE_TRANSFORM_IDENTITY : + WLR_VK_TEXTURE_TRANSFORM_SRGB; + struct wlr_vk_render_format_setup *setup = pass->srgb_pathway ? pass->render_buffer->srgb.render_setup : pass->render_buffer->plain.render_setup; @@ -792,7 +800,7 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, .ycbcr_format = texture->format->is_ycbcr ? texture->format : NULL, .filter_mode = options->filter_mode, }, - .texture_transform = texture->transform, + .texture_transform = tex_transform, .blend_mode = !texture->has_alpha && alpha == 1.0 ? WLR_RENDER_BLEND_MODE_NONE : options->blend_mode, }); @@ -802,7 +810,7 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, } struct wlr_vk_texture_view *view = - vulkan_texture_get_or_create_view(texture, pipe->layout); + vulkan_texture_get_or_create_view(texture, pipe->layout, srgb_image_view); if (!view) { pass->failed = true; return; diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index cbfb022f7..a0e456580 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -2442,6 +2442,7 @@ struct wlr_renderer *vulkan_renderer_create_for_device(struct wlr_vk_device *dev renderer->dev = dev; wlr_renderer_init(&renderer->wlr_renderer, &renderer_impl, WLR_BUFFER_CAP_DMABUF); + renderer->wlr_renderer.features.input_color_transform = true; renderer->wlr_renderer.features.output_color_transform = true; wl_list_init(&renderer->stage.buffers); wl_list_init(&renderer->foreign_textures); diff --git a/render/vulkan/texture.c b/render/vulkan/texture.c index 61a6040ee..2b21d458c 100644 --- a/render/vulkan/texture.c +++ b/render/vulkan/texture.c @@ -269,10 +269,12 @@ static struct wlr_vk_texture *vulkan_texture_create( } struct wlr_vk_texture_view *vulkan_texture_get_or_create_view(struct wlr_vk_texture *texture, - const struct wlr_vk_pipeline_layout *pipeline_layout) { + const struct wlr_vk_pipeline_layout *pipeline_layout, bool srgb) { + assert(texture->using_mutable_srgb || !srgb); + struct wlr_vk_texture_view *view; wl_list_for_each(view, &texture->views, link) { - if (view->layout == pipeline_layout) { + if (view->layout == pipeline_layout && view->srgb == srgb) { return view; } } @@ -283,6 +285,7 @@ struct wlr_vk_texture_view *vulkan_texture_get_or_create_view(struct wlr_vk_text } view->layout = pipeline_layout; + view->srgb = srgb; VkResult res; VkDevice dev = texture->renderer->dev->dev; @@ -290,8 +293,7 @@ struct wlr_vk_texture_view *vulkan_texture_get_or_create_view(struct wlr_vk_text VkImageViewCreateInfo view_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = texture->using_mutable_srgb ? texture->format->vk_srgb - : texture->format->vk, + .format = srgb ? texture->format->vk_srgb : texture->format->vk, .components.r = VK_COMPONENT_SWIZZLE_IDENTITY, .components.g = VK_COMPONENT_SWIZZLE_IDENTITY, .components.b = VK_COMPONENT_SWIZZLE_IDENTITY, @@ -353,10 +355,10 @@ struct wlr_vk_texture_view *vulkan_texture_get_or_create_view(struct wlr_vk_text static void texture_set_format(struct wlr_vk_texture *texture, const struct wlr_vk_format *format, bool has_mutable_srgb) { + assert(!(format->is_ycbcr && has_mutable_srgb)); + texture->format = format; texture->using_mutable_srgb = has_mutable_srgb; - texture->transform = !format->is_ycbcr && has_mutable_srgb ? - WLR_VK_TEXTURE_TRANSFORM_IDENTITY : WLR_VK_TEXTURE_TRANSFORM_SRGB; const struct wlr_pixel_format_info *format_info = drm_get_pixel_format_info(format->drm); From 4efec11721c9d779b5bf72fe3baac120e8da55c8 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 23 Feb 2025 15:09:34 +0100 Subject: [PATCH 414/519] scene: add transfer function support for wlr_scene_buffer --- include/wlr/types/wlr_scene.h | 4 ++++ types/scene/wlr_scene.c | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 114d5c0f3..9f27b4373 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -192,6 +192,7 @@ struct wlr_scene_buffer { int dst_width, dst_height; enum wl_output_transform transform; pixman_region32_t opaque_region; + enum wlr_color_transfer_function transfer_function; struct { uint64_t active_outputs; @@ -541,6 +542,9 @@ void wlr_scene_buffer_set_opacity(struct wlr_scene_buffer *scene_buffer, void wlr_scene_buffer_set_filter_mode(struct wlr_scene_buffer *scene_buffer, enum wlr_scale_filter_mode filter_mode); +void wlr_scene_buffer_set_transfer_function(struct wlr_scene_buffer *scene_buffer, + enum wlr_color_transfer_function transfer_function); + /** * Calls the buffer's frame_done signal. */ diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 1acbb5650..baa36bf3a 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1099,6 +1099,16 @@ void wlr_scene_buffer_set_filter_mode(struct wlr_scene_buffer *scene_buffer, scene_node_update(&scene_buffer->node, NULL); } +void wlr_scene_buffer_set_transfer_function(struct wlr_scene_buffer *scene_buffer, + enum wlr_color_transfer_function transfer_function) { + if (scene_buffer->transfer_function == transfer_function) { + return; + } + + scene_buffer->transfer_function = transfer_function; + scene_node_update(&scene_buffer->node, NULL); +} + static struct wlr_texture *scene_buffer_get_texture( struct wlr_scene_buffer *scene_buffer, struct wlr_renderer *renderer) { if (scene_buffer->buffer == NULL || scene_buffer->texture != NULL) { @@ -1446,6 +1456,7 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren .blend_mode = !data->output->scene->calculate_visibility || !pixman_region32_empty(&opaque) ? WLR_RENDER_BLEND_MODE_PREMULTIPLIED : WLR_RENDER_BLEND_MODE_NONE, + .transfer_function = scene_buffer->transfer_function, .wait_timeline = scene_buffer->wait_timeline, .wait_point = scene_buffer->wait_point, }); @@ -1936,6 +1947,9 @@ static enum scene_direct_scanout_result scene_entry_try_direct_scanout( if (buffer->transform != data->transform) { return SCANOUT_INELIGIBLE; } + if (buffer->transfer_function != 0 && buffer->transfer_function != WLR_COLOR_TRANSFER_FUNCTION_SRGB) { + return false; + } // We want to ensure optimal buffer selection, but as direct-scanout can be enabled and disabled // on a frame-by-frame basis, we wait for a few frames to send the new format recommendations. From 7a1161438c34233754f96ded95da681a3abae862 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 23 Feb 2025 15:09:59 +0100 Subject: [PATCH 415/519] scene: add support for color-management-v1 transfer functions --- types/scene/surface.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/types/scene/surface.c b/types/scene/surface.c index 1973abcaa..82ee12e07 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -186,11 +187,31 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { opacity = (float)alpha_modifier_state->multiplier; } + enum wlr_color_transfer_function tf = WLR_COLOR_TRANSFER_FUNCTION_SRGB; + const struct wlr_image_description_v1_data *img_desc = + wlr_surface_get_image_description_v1_data(surface); + if (img_desc != NULL) { + switch (img_desc->tf_named) { + case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB: + tf = WLR_COLOR_TRANSFER_FUNCTION_SRGB; + break; + case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ: + tf = WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ; + break; + case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR: + tf = WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR; + break; + default: + abort(); + } + } + wlr_scene_buffer_set_opaque_region(scene_buffer, &opaque); wlr_scene_buffer_set_source_box(scene_buffer, &src_box); wlr_scene_buffer_set_dest_size(scene_buffer, width, height); wlr_scene_buffer_set_transform(scene_buffer, state->transform); wlr_scene_buffer_set_opacity(scene_buffer, opacity); + wlr_scene_buffer_set_transfer_function(scene_buffer, tf); scene_buffer_unmark_client_buffer(scene_buffer); From ec422ac38914a3a642a5ff16ac264fe99b2ff1fb Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 27 Feb 2025 16:22:17 +0100 Subject: [PATCH 416/519] render/vulkan: prepare texture shader for new transforms --- render/vulkan/shaders/texture.frag | 36 +++++++++++++++++++----------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/render/vulkan/shaders/texture.frag b/render/vulkan/shaders/texture.frag index 6f2f347de..84b7678e4 100644 --- a/render/vulkan/shaders/texture.frag +++ b/render/vulkan/shaders/texture.frag @@ -21,27 +21,37 @@ float srgb_channel_to_linear(float x) { x > 0.04045); } -vec4 srgb_color_to_linear(vec4 color) { - if (color.a == 0) { - return vec4(0); - } - color.rgb /= color.a; - color.rgb = vec3( +vec3 srgb_color_to_linear(vec3 color) { + return vec3( srgb_channel_to_linear(color.r), srgb_channel_to_linear(color.g), srgb_channel_to_linear(color.b) ); - color.rgb *= color.a; - return color; } void main() { - vec4 val = textureLod(tex, uv, 0); - if (TEXTURE_TRANSFORM == TEXTURE_TRANSFORM_SRGB) { - out_color = srgb_color_to_linear(val); - } else { // TEXTURE_TRANSFORM_IDENTITY - out_color = val; + vec4 in_color = textureLod(tex, uv, 0); + + if (TEXTURE_TRANSFORM == TEXTURE_TRANSFORM_IDENTITY) { + out_color = in_color * data.alpha; + return; } + // Convert from pre-multiplied alpha to straight alpha + float alpha = in_color.a; + vec3 rgb; + if (alpha == 0) { + rgb = vec3(0); + } else { + rgb = in_color.rgb / alpha; + } + + if (TEXTURE_TRANSFORM == TEXTURE_TRANSFORM_SRGB) { + rgb = srgb_color_to_linear(rgb); + } + + // Back to pre-multiplied alpha + out_color = vec4(rgb * alpha, alpha); + out_color *= data.alpha; } From 56d95c2ecb2fb9f2d4ab103419b9d247ce171fc1 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 27 Feb 2025 16:48:32 +0100 Subject: [PATCH 417/519] render/vulkan: introduce wlr_vk_frag_texture_pcr_data Contains UBOs for texture.frag. --- include/render/vulkan.h | 4 ++++ render/vulkan/pass.c | 8 ++++++-- render/vulkan/shaders/texture.frag | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/include/render/vulkan.h b/include/render/vulkan.h index 651106042..c3fb6b2bf 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -339,6 +339,10 @@ struct wlr_vk_vert_pcr_data { float uv_size[2]; }; +struct wlr_vk_frag_texture_pcr_data { + float alpha; +}; + struct wlr_vk_frag_output_pcr_data { float matrix[4][4]; // only a 3x3 subset is used float luminance_multiplier; diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 95fbd319e..49435b0e8 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -816,6 +816,10 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, return; } + struct wlr_vk_frag_texture_pcr_data frag_pcr_data = { + .alpha = alpha, + }; + bind_pipeline(pass, pipe->vk); vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, @@ -824,8 +828,8 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, vkCmdPushConstants(cb, pipe->layout->vk, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(vert_pcr_data), &vert_pcr_data); vkCmdPushConstants(cb, pipe->layout->vk, - VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(vert_pcr_data), sizeof(float), - &alpha); + VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(vert_pcr_data), + sizeof(frag_pcr_data), &frag_pcr_data); pixman_region32_t clip; get_clip_region(pass, options->clip, &clip); diff --git a/render/vulkan/shaders/texture.frag b/render/vulkan/shaders/texture.frag index 84b7678e4..58ac59fea 100644 --- a/render/vulkan/shaders/texture.frag +++ b/render/vulkan/shaders/texture.frag @@ -5,6 +5,7 @@ layout(set = 0, binding = 0) uniform sampler2D tex; layout(location = 0) in vec2 uv; layout(location = 0) out vec4 out_color; +// struct wlr_vk_frag_texture_pcr_data layout(push_constant) uniform UBO { layout(offset = 80) float alpha; } data; From 3a51a5c6238fa5351cfc25d6a4af554b31877ec4 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 27 Feb 2025 16:57:00 +0100 Subject: [PATCH 418/519] render/vulkan: add texture color transformation matrix --- include/render/vulkan.h | 1 + render/vulkan/pass.c | 4 ++++ render/vulkan/shaders/texture.frag | 12 +++++------- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/include/render/vulkan.h b/include/render/vulkan.h index c3fb6b2bf..00cc33ea0 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -340,6 +340,7 @@ struct wlr_vk_vert_pcr_data { }; struct wlr_vk_frag_texture_pcr_data { + float matrix[4][4]; // only a 3x3 subset is used float alpha; }; diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 49435b0e8..68be473cf 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -816,9 +816,13 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, return; } + float color_matrix[9]; + wlr_matrix_identity(color_matrix); + struct wlr_vk_frag_texture_pcr_data frag_pcr_data = { .alpha = alpha, }; + encode_color_matrix(color_matrix, frag_pcr_data.matrix); bind_pipeline(pass, pipe->vk); diff --git a/render/vulkan/shaders/texture.frag b/render/vulkan/shaders/texture.frag index 58ac59fea..dc4f60cfe 100644 --- a/render/vulkan/shaders/texture.frag +++ b/render/vulkan/shaders/texture.frag @@ -6,8 +6,9 @@ layout(location = 0) in vec2 uv; layout(location = 0) out vec4 out_color; // struct wlr_vk_frag_texture_pcr_data -layout(push_constant) uniform UBO { - layout(offset = 80) float alpha; +layout(push_constant, row_major) uniform UBO { + layout(offset = 80) mat4 matrix; + float alpha; } data; layout (constant_id = 0) const int TEXTURE_TRANSFORM = 0; @@ -33,11 +34,6 @@ vec3 srgb_color_to_linear(vec3 color) { void main() { vec4 in_color = textureLod(tex, uv, 0); - if (TEXTURE_TRANSFORM == TEXTURE_TRANSFORM_IDENTITY) { - out_color = in_color * data.alpha; - return; - } - // Convert from pre-multiplied alpha to straight alpha float alpha = in_color.a; vec3 rgb; @@ -51,6 +47,8 @@ void main() { rgb = srgb_color_to_linear(rgb); } + rgb = mat3(data.matrix) * rgb; + // Back to pre-multiplied alpha out_color = vec4(rgb * alpha, alpha); From a8144088df077f60ce7b09c79c00eda5cb56683c Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 27 Feb 2025 16:37:00 +0100 Subject: [PATCH 419/519] render/vulkan: add support for PQ for textures --- include/render/vulkan.h | 1 + render/vulkan/pass.c | 30 +++++++++++++++++++++++------- render/vulkan/shaders/texture.frag | 14 ++++++++++++++ 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/include/render/vulkan.h b/include/render/vulkan.h index 00cc33ea0..1ed69d6e4 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -152,6 +152,7 @@ struct wlr_vk_pipeline_layout { enum wlr_vk_texture_transform { WLR_VK_TEXTURE_TRANSFORM_IDENTITY = 0, WLR_VK_TEXTURE_TRANSFORM_SRGB = 1, + WLR_VK_TEXTURE_TRANSFORM_ST2084_PQ = 2, }; enum wlr_vk_shader_source { diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 68be473cf..30cd0fe54 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -781,13 +781,29 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, }; encode_proj_matrix(matrix, vert_pcr_data.mat4); - bool srgb = options->transfer_function == 0 || - options->transfer_function == WLR_COLOR_TRANSFER_FUNCTION_SRGB; - bool srgb_image_view = srgb && texture->using_mutable_srgb; - enum wlr_vk_texture_transform tex_transform = - srgb_image_view || options->transfer_function == WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR ? - WLR_VK_TEXTURE_TRANSFORM_IDENTITY : - WLR_VK_TEXTURE_TRANSFORM_SRGB; + enum wlr_color_transfer_function tf = options->transfer_function; + if (tf == 0) { + tf = WLR_COLOR_TRANSFER_FUNCTION_SRGB; + } + + bool srgb_image_view = false; + enum wlr_vk_texture_transform tex_transform = 0; + switch (tf) { + case WLR_COLOR_TRANSFER_FUNCTION_SRGB: + if (texture->using_mutable_srgb) { + tex_transform = WLR_VK_TEXTURE_TRANSFORM_IDENTITY; + srgb_image_view = true; + } else { + tex_transform = WLR_VK_TEXTURE_TRANSFORM_SRGB; + } + break; + case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR: + tex_transform = WLR_VK_TEXTURE_TRANSFORM_IDENTITY; + break; + case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ: + tex_transform = WLR_VK_TEXTURE_TRANSFORM_ST2084_PQ; + break; + } struct wlr_vk_render_format_setup *setup = pass->srgb_pathway ? pass->render_buffer->srgb.render_setup : diff --git a/render/vulkan/shaders/texture.frag b/render/vulkan/shaders/texture.frag index dc4f60cfe..e1467023b 100644 --- a/render/vulkan/shaders/texture.frag +++ b/render/vulkan/shaders/texture.frag @@ -16,6 +16,7 @@ layout (constant_id = 0) const int TEXTURE_TRANSFORM = 0; // Matches enum wlr_vk_texture_transform #define TEXTURE_TRANSFORM_IDENTITY 0 #define TEXTURE_TRANSFORM_SRGB 1 +#define TEXTURE_TRANSFORM_ST2084_PQ 2 float srgb_channel_to_linear(float x) { return mix(x / 12.92, @@ -31,6 +32,17 @@ vec3 srgb_color_to_linear(vec3 color) { ); } +vec3 pq_color_to_linear(vec3 color) { + float inv_m1 = 1 / 0.1593017578125; + float inv_m2 = 1 / 78.84375; + float c1 = 0.8359375; + float c2 = 18.8515625; + float c3 = 18.6875; + vec3 num = max(pow(color, vec3(inv_m2)) - c1, 0); + vec3 denom = c2 - c3 * pow(color, vec3(inv_m2)); + return pow(num / denom, vec3(inv_m1)); +} + void main() { vec4 in_color = textureLod(tex, uv, 0); @@ -45,6 +57,8 @@ void main() { if (TEXTURE_TRANSFORM == TEXTURE_TRANSFORM_SRGB) { rgb = srgb_color_to_linear(rgb); + } else if (TEXTURE_TRANSFORM == TEXTURE_TRANSFORM_ST2084_PQ) { + rgb = pq_color_to_linear(rgb); } rgb = mat3(data.matrix) * rgb; From fa1feb447f6d417ff4a73262709aaccf94efaa2c Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 27 Feb 2025 17:52:37 +0100 Subject: [PATCH 420/519] render, render/vulkan: add primaries to wlr_render_texture_options --- include/wlr/render/pass.h | 2 ++ render/vulkan/pass.c | 16 +++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/include/wlr/render/pass.h b/include/wlr/render/pass.h index b8854bbf9..8e22bdf8f 100644 --- a/include/wlr/render/pass.h +++ b/include/wlr/render/pass.h @@ -104,6 +104,8 @@ struct wlr_render_texture_options { enum wlr_render_blend_mode blend_mode; /* Transfer function the source texture is encoded with */ enum wlr_color_transfer_function transfer_function; + /* Primaries describing the color volume of the source texture */ + const struct wlr_color_primaries *primaries; /* Wait for a timeline synchronization point before texturing. * diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 30cd0fe54..0ff4f52c6 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -833,7 +833,21 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, } float color_matrix[9]; - wlr_matrix_identity(color_matrix); + if (options->primaries != NULL) { + struct wlr_color_primaries srgb; + wlr_color_primaries_from_named(&srgb, WLR_COLOR_NAMED_PRIMARIES_SRGB); + + float src_primaries_to_xyz[9]; + wlr_color_primaries_to_xyz(options->primaries, src_primaries_to_xyz); + float srgb_to_xyz[9]; + wlr_color_primaries_to_xyz(&srgb, srgb_to_xyz); + float xyz_to_srgb[9]; + matrix_invert(xyz_to_srgb, srgb_to_xyz); + + wlr_matrix_multiply(color_matrix, xyz_to_srgb, src_primaries_to_xyz); + } else { + wlr_matrix_identity(color_matrix); + } struct wlr_vk_frag_texture_pcr_data frag_pcr_data = { .alpha = alpha, From ae85c31176728d1b3944844572bf6c7c690b9698 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 1 Mar 2025 12:43:08 +0100 Subject: [PATCH 421/519] render/vulkan: add luminance multiplier for texture shader --- include/render/vulkan.h | 1 + render/vulkan/pass.c | 10 ++++++++++ render/vulkan/shaders/texture.frag | 3 +++ 3 files changed, 14 insertions(+) diff --git a/include/render/vulkan.h b/include/render/vulkan.h index 1ed69d6e4..deff0eac3 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -343,6 +343,7 @@ struct wlr_vk_vert_pcr_data { struct wlr_vk_frag_texture_pcr_data { float matrix[4][4]; // only a 3x3 subset is used float alpha; + float luminance_multiplier; }; struct wlr_vk_frag_output_pcr_data { diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 0ff4f52c6..4fd357113 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -849,8 +849,18 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, wlr_matrix_identity(color_matrix); } + float luminance_multiplier = 1; + if (tf != WLR_COLOR_TRANSFER_FUNCTION_SRGB) { + struct wlr_color_luminances src_lum, srgb_lum; + wlr_color_transfer_function_get_default_luminance(tf, &src_lum); + wlr_color_transfer_function_get_default_luminance( + WLR_COLOR_TRANSFER_FUNCTION_SRGB, &srgb_lum); + luminance_multiplier = get_luminance_multiplier(&src_lum, &srgb_lum); + } + struct wlr_vk_frag_texture_pcr_data frag_pcr_data = { .alpha = alpha, + .luminance_multiplier = luminance_multiplier, }; encode_color_matrix(color_matrix, frag_pcr_data.matrix); diff --git a/render/vulkan/shaders/texture.frag b/render/vulkan/shaders/texture.frag index e1467023b..2a7e2c517 100644 --- a/render/vulkan/shaders/texture.frag +++ b/render/vulkan/shaders/texture.frag @@ -9,6 +9,7 @@ layout(location = 0) out vec4 out_color; layout(push_constant, row_major) uniform UBO { layout(offset = 80) mat4 matrix; float alpha; + float luminance_multiplier; } data; layout (constant_id = 0) const int TEXTURE_TRANSFORM = 0; @@ -61,6 +62,8 @@ void main() { rgb = pq_color_to_linear(rgb); } + rgb *= data.luminance_multiplier; + rgb = mat3(data.matrix) * rgb; // Back to pre-multiplied alpha From 071773cb27bc628302b6adb0c2ab5bedb85788cb Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 27 Feb 2025 18:07:43 +0100 Subject: [PATCH 422/519] scene: add primaries support to wlr_scene_buffer --- include/wlr/types/wlr_scene.h | 4 ++++ types/scene/wlr_scene.c | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 9f27b4373..5d4a056ce 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -193,6 +193,7 @@ struct wlr_scene_buffer { enum wl_output_transform transform; pixman_region32_t opaque_region; enum wlr_color_transfer_function transfer_function; + enum wlr_color_named_primaries primaries; struct { uint64_t active_outputs; @@ -545,6 +546,9 @@ void wlr_scene_buffer_set_filter_mode(struct wlr_scene_buffer *scene_buffer, void wlr_scene_buffer_set_transfer_function(struct wlr_scene_buffer *scene_buffer, enum wlr_color_transfer_function transfer_function); +void wlr_scene_buffer_set_primaries(struct wlr_scene_buffer *scene_buffer, + enum wlr_color_named_primaries primaries); + /** * Calls the buffer's frame_done signal. */ diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index baa36bf3a..144329491 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -14,6 +14,7 @@ #include #include #include +#include "render/color.h" #include "types/wlr_output.h" #include "types/wlr_scene.h" #include "util/array.h" @@ -1109,6 +1110,16 @@ void wlr_scene_buffer_set_transfer_function(struct wlr_scene_buffer *scene_buffe scene_node_update(&scene_buffer->node, NULL); } +void wlr_scene_buffer_set_primaries(struct wlr_scene_buffer *scene_buffer, + enum wlr_color_named_primaries primaries) { + if (scene_buffer->primaries == primaries) { + return; + } + + scene_buffer->primaries = primaries; + scene_node_update(&scene_buffer->node, NULL); +} + static struct wlr_texture *scene_buffer_get_texture( struct wlr_scene_buffer *scene_buffer, struct wlr_renderer *renderer) { if (scene_buffer->buffer == NULL || scene_buffer->texture != NULL) { @@ -1445,6 +1456,11 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren wlr_output_transform_invert(scene_buffer->transform); transform = wlr_output_transform_compose(transform, data->transform); + struct wlr_color_primaries primaries = {0}; + if (scene_buffer->primaries != 0) { + wlr_color_primaries_from_named(&primaries, scene_buffer->primaries); + } + wlr_render_pass_add_texture(data->render_pass, &(struct wlr_render_texture_options) { .texture = texture, .src_box = scene_buffer->src_box, @@ -1457,6 +1473,7 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren !pixman_region32_empty(&opaque) ? WLR_RENDER_BLEND_MODE_PREMULTIPLIED : WLR_RENDER_BLEND_MODE_NONE, .transfer_function = scene_buffer->transfer_function, + .primaries = scene_buffer->primaries != 0 ? &primaries : NULL, .wait_timeline = scene_buffer->wait_timeline, .wait_point = scene_buffer->wait_point, }); @@ -1950,6 +1967,9 @@ static enum scene_direct_scanout_result scene_entry_try_direct_scanout( if (buffer->transfer_function != 0 && buffer->transfer_function != WLR_COLOR_TRANSFER_FUNCTION_SRGB) { return false; } + if (buffer->primaries != 0 && buffer->primaries != WLR_COLOR_NAMED_PRIMARIES_SRGB) { + return false; + } // We want to ensure optimal buffer selection, but as direct-scanout can be enabled and disabled // on a frame-by-frame basis, we wait for a few frames to send the new format recommendations. From 0c272a38423679e980d4cd39affc202fd48c0f6d Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 27 Feb 2025 18:13:50 +0100 Subject: [PATCH 423/519] scene: add support for color-management-v1 primaries --- types/scene/surface.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/types/scene/surface.c b/types/scene/surface.c index 82ee12e07..07ef7ebe4 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -188,6 +188,7 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { } enum wlr_color_transfer_function tf = WLR_COLOR_TRANSFER_FUNCTION_SRGB; + enum wlr_color_named_primaries primaries = WLR_COLOR_NAMED_PRIMARIES_SRGB; const struct wlr_image_description_v1_data *img_desc = wlr_surface_get_image_description_v1_data(surface); if (img_desc != NULL) { @@ -204,6 +205,17 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { default: abort(); } + + switch (img_desc->primaries_named) { + case WP_COLOR_MANAGER_V1_PRIMARIES_SRGB: + primaries = WLR_COLOR_NAMED_PRIMARIES_SRGB; + break; + case WP_COLOR_MANAGER_V1_PRIMARIES_BT2020: + primaries = WLR_COLOR_NAMED_PRIMARIES_BT2020; + break; + default: + abort(); + } } wlr_scene_buffer_set_opaque_region(scene_buffer, &opaque); @@ -212,6 +224,7 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { wlr_scene_buffer_set_transform(scene_buffer, state->transform); wlr_scene_buffer_set_opacity(scene_buffer, opacity); wlr_scene_buffer_set_transfer_function(scene_buffer, tf); + wlr_scene_buffer_set_primaries(scene_buffer, primaries); scene_buffer_unmark_client_buffer(scene_buffer); From 98af3371755338ccbcd021c1bbacec2ca7b47806 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 20 Jun 2025 19:02:54 +0200 Subject: [PATCH 424/519] output: shorten output enabled checks Use a more concise loop instead of repeated logic. No behavior change. --- types/output/output.c | 46 ++++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/types/output/output.c b/types/output/output.c index 8576bb0e6..4f268c78d 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -627,33 +627,25 @@ static bool output_basic_test(struct wlr_output *output, } } - if (!enabled && state->committed & WLR_OUTPUT_STATE_BUFFER) { - wlr_log(WLR_DEBUG, "Tried to commit a buffer on a disabled output"); - return false; - } - if (!enabled && state->committed & WLR_OUTPUT_STATE_MODE) { - wlr_log(WLR_DEBUG, "Tried to modeset a disabled output"); - return false; - } - if (!enabled && state->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) { - wlr_log(WLR_DEBUG, "Tried to enable adaptive sync on a disabled output"); - return false; - } - if (!enabled && state->committed & WLR_OUTPUT_STATE_RENDER_FORMAT) { - wlr_log(WLR_DEBUG, "Tried to set format for a disabled output"); - return false; - } - if (!enabled && state->committed & WLR_OUTPUT_STATE_SUBPIXEL) { - wlr_log(WLR_DEBUG, "Tried to set the subpixel layout on a disabled output"); - return false; - } - if (!enabled && state->committed & WLR_OUTPUT_STATE_COLOR_TRANSFORM) { - wlr_log(WLR_DEBUG, "Tried to set a color transform on a disabled output"); - return false; - } - if (!enabled && state->committed & WLR_OUTPUT_STATE_IMAGE_DESCRIPTION) { - wlr_log(WLR_DEBUG, "Tried to set the image description on a disabled output"); - return false; + const struct { + enum wlr_output_state_field field; + const char *name; + } needs_enabled[] = { + { WLR_OUTPUT_STATE_BUFFER, "buffer" }, + { WLR_OUTPUT_STATE_MODE, "mode" }, + { WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED, "adaptive sync" }, + { WLR_OUTPUT_STATE_RENDER_FORMAT, "render format" }, + { WLR_OUTPUT_STATE_SUBPIXEL, "subpixel" }, + { WLR_OUTPUT_STATE_COLOR_TRANSFORM, "color transform" }, + { WLR_OUTPUT_STATE_IMAGE_DESCRIPTION, "image description" }, + }; + if (!enabled) { + for (size_t i = 0; i < sizeof(needs_enabled) / sizeof(needs_enabled[0]); i++) { + if (state->committed & needs_enabled[i].field) { + wlr_log(WLR_DEBUG, "Tried to set %s on a disabled output", needs_enabled[i].name); + return false; + } + } } if (state->committed & WLR_OUTPUT_STATE_LAYERS) { From f5e7caf59994cfa08650cade41374e23779a24a4 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Wed, 18 Jun 2025 13:30:21 +0200 Subject: [PATCH 425/519] util/box: set dest to empty if boxes don't intersect Currently if both box_a and box_b are non-empty but do not intersect, this function does not set dest to an empty box. This contradicts the doc comments and is surprising for users. --- util/box.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/util/box.c b/util/box.c index 6a6becf33..a9b42579a 100644 --- a/util/box.c +++ b/util/box.c @@ -67,7 +67,12 @@ bool wlr_box_intersection(struct wlr_box *dest, const struct wlr_box *box_a, dest->width = x2 - x1; dest->height = y2 - y1; - return !wlr_box_empty(dest); + if (wlr_box_empty(dest)) { + *dest = (struct wlr_box){0}; + return false; + } + + return true; } bool wlr_box_contains_point(const struct wlr_box *box, double x, double y) { From 6d8bb66f98700472d05e57b8199d621e1cefe338 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sun, 25 May 2025 16:50:27 +0900 Subject: [PATCH 426/519] xwm: add support for _NET_WM_ICON --- include/wlr/xwayland/xwayland.h | 10 ++++++++ include/xwayland/xwm.h | 1 + xwayland/xwm.c | 44 ++++++++++++++++++++++++++++++--- 3 files changed, 51 insertions(+), 4 deletions(-) diff --git a/include/wlr/xwayland/xwayland.h b/include/wlr/xwayland/xwayland.h index 92a45a493..1c52b3558 100644 --- a/include/wlr/xwayland/xwayland.h +++ b/include/wlr/xwayland/xwayland.h @@ -221,6 +221,7 @@ struct wlr_xwayland_surface { struct wl_signal set_override_redirect; struct wl_signal set_geometry; struct wl_signal set_opacity; + struct wl_signal set_icon; struct wl_signal focus_in; struct wl_signal grab_focus; /* can be used to set initial maximized/fullscreen geometry */ @@ -401,6 +402,15 @@ enum wlr_xwayland_icccm_input_model wlr_xwayland_surface_icccm_input_model( void wlr_xwayland_set_workareas(struct wlr_xwayland *wlr_xwayland, const struct wlr_box *workareas, size_t num_workareas); +/** + * Fetches the icon set via the _NET_WM_ICON property. + * + * Returns true on success. The caller is responsible for freeing the reply + * using xcb_ewmh_get_wm_icon_reply_wipe(). + */ +bool wlr_xwayland_surface_fetch_icon( + const struct wlr_xwayland_surface *xsurface, + xcb_ewmh_get_wm_icon_reply_t *icon_reply); /** * Get the XCB connection of the XWM. diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h index d38e5cc91..e460bbb63 100644 --- a/include/xwayland/xwm.h +++ b/include/xwayland/xwm.h @@ -36,6 +36,7 @@ enum atom_name { NET_WM_STATE, NET_WM_STRUT_PARTIAL, NET_WM_WINDOW_TYPE, + NET_WM_ICON, WM_TAKE_FOCUS, WINDOW, NET_ACTIVE_WINDOW, diff --git a/xwayland/xwm.c b/xwayland/xwm.c index bcdcd218c..a82e8b145 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -34,6 +34,7 @@ static const char *const atom_map[ATOM_LAST] = { [NET_WM_STATE] = "_NET_WM_STATE", [NET_WM_STRUT_PARTIAL] = "_NET_WM_STRUT_PARTIAL", [NET_WM_WINDOW_TYPE] = "_NET_WM_WINDOW_TYPE", + [NET_WM_ICON] = "_NET_WM_ICON", [WM_TAKE_FOCUS] = "WM_TAKE_FOCUS", [WINDOW] = "WINDOW", [NET_ACTIVE_WINDOW] = "_NET_ACTIVE_WINDOW", @@ -241,6 +242,7 @@ static struct wlr_xwayland_surface *xwayland_surface_create( wl_signal_init(&surface->events.set_override_redirect); wl_signal_init(&surface->events.set_geometry); wl_signal_init(&surface->events.set_opacity); + wl_signal_init(&surface->events.set_icon); wl_signal_init(&surface->events.focus_in); wl_signal_init(&surface->events.grab_focus); wl_signal_init(&surface->events.map_request); @@ -602,6 +604,7 @@ static void xwayland_surface_destroy(struct wlr_xwayland_surface *xsurface) { assert(wl_list_empty(&xsurface->events.set_override_redirect.listener_list)); assert(wl_list_empty(&xsurface->events.set_geometry.listener_list)); assert(wl_list_empty(&xsurface->events.set_opacity.listener_list)); + assert(wl_list_empty(&xsurface->events.set_icon.listener_list)); assert(wl_list_empty(&xsurface->events.focus_in.listener_list)); assert(wl_list_empty(&xsurface->events.grab_focus.listener_list)); assert(wl_list_empty(&xsurface->events.map_request.listener_list)); @@ -1078,6 +1081,8 @@ static void read_surface_property(struct wlr_xwm *xwm, // intentionally ignored } else if (property == xwm->atoms[NET_WM_WINDOW_TYPE]) { read_surface_window_type(xwm, xsurface, reply); + } else if (property == xwm->atoms[NET_WM_ICON]) { + wl_signal_emit_mutable(&xsurface->events.set_icon, NULL); } else if (property == xwm->atoms[WM_PROTOCOLS]) { read_surface_protocols(xwm, xsurface, reply); } else if (property == xwm->atoms[NET_WM_STATE]) { @@ -1131,6 +1136,38 @@ static const struct wlr_addon_interface surface_addon_impl = { .destroy = xwayland_surface_handle_addon_destroy, }; +bool wlr_xwayland_surface_fetch_icon( + const struct wlr_xwayland_surface *xsurface, + xcb_ewmh_get_wm_icon_reply_t *icon_reply) { + struct wlr_xwm *xwm = xsurface->xwm; + + xcb_get_property_cookie_t cookie = xcb_get_property(xwm->xcb_conn, 0, + xsurface->window_id, xwm->atoms[NET_WM_ICON], XCB_ATOM_CARDINAL, + 0, UINT32_MAX); + xcb_get_property_reply_t *reply = + xcb_get_property_reply(xwm->xcb_conn, cookie, NULL); + if (!reply) { + return false; + } + + if (!xcb_ewmh_get_wm_icon_from_reply(icon_reply, reply)) { + free(reply); + return false; + } + + return true; +} + +static xcb_get_property_cookie_t get_property(struct wlr_xwm *xwm, + xcb_window_t window_id, xcb_atom_t atom) { + uint32_t len = 2048; + if (atom == xwm->atoms[NET_WM_ICON]) { + /* Compositors need to fetch icon data wlr_xwayland_surface_fetch_icon() */ + len = 0; + } + return xcb_get_property(xwm->xcb_conn, 0, window_id, atom, XCB_ATOM_ANY, 0, len); +} + static void xwayland_surface_associate(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, struct wlr_surface *surface) { assert(xsurface->surface == NULL); @@ -1165,12 +1202,12 @@ static void xwayland_surface_associate(struct wlr_xwm *xwm, xwm->atoms[NET_WM_STRUT_PARTIAL], xwm->atoms[NET_WM_WINDOW_TYPE], xwm->atoms[NET_WM_NAME], + xwm->atoms[NET_WM_ICON], }; xcb_get_property_cookie_t cookies[sizeof(props) / sizeof(props[0])] = {0}; for (size_t i = 0; i < sizeof(props) / sizeof(props[0]); i++) { - cookies[i] = xcb_get_property(xwm->xcb_conn, 0, xsurface->window_id, - props[i], XCB_ATOM_ANY, 0, 2048); + cookies[i] = get_property(xwm, xsurface->window_id, props[i]); } for (size_t i = 0; i < sizeof(props) / sizeof(props[0]); i++) { @@ -1398,8 +1435,7 @@ static void xwm_handle_property_notify(struct wlr_xwm *xwm, return; } - xcb_get_property_cookie_t cookie = - xcb_get_property(xwm->xcb_conn, 0, xsurface->window_id, ev->atom, XCB_ATOM_ANY, 0, 2048); + xcb_get_property_cookie_t cookie = get_property(xwm, xsurface->window_id, ev->atom); xcb_get_property_reply_t *reply = xcb_get_property_reply(xwm->xcb_conn, cookie, NULL); if (reply == NULL) { From e76f8ac2b3407fa7d027831a81324c6cb5a5301f Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 19 Jun 2025 13:34:19 +0200 Subject: [PATCH 427/519] output: add wlr_output.image_description Stores the current image description. --- include/wlr/types/wlr_output.h | 2 ++ types/output/output.c | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index e6cd58824..1ef289008 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -193,6 +193,7 @@ struct wlr_output { enum wl_output_transform transform; enum wlr_output_adaptive_sync_status adaptive_sync_status; uint32_t render_format; + const struct wlr_output_image_description *image_description; // Indicates whether making changes to adaptive sync status is supported. // If false, changes to adaptive sync status is guaranteed to fail. If @@ -255,6 +256,7 @@ struct wlr_output { struct { struct wl_listener display_destroy; + struct wlr_output_image_description image_description_value; } WLR_PRIVATE; }; diff --git a/types/output/output.c b/types/output/output.c index 4f268c78d..3410138c1 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -233,6 +233,15 @@ static void output_apply_state(struct wlr_output *output, output->transform = state->transform; } + if (state->committed & WLR_OUTPUT_STATE_IMAGE_DESCRIPTION) { + if (state->image_description != NULL) { + output->image_description_value = *state->image_description; + output->image_description = &output->image_description_value; + } else { + output->image_description = NULL; + } + } + bool geometry_updated = state->committed & (WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_TRANSFORM | WLR_OUTPUT_STATE_SUBPIXEL); From 2498036e673af7e82b79de85a529c07dcdaed712 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 19 Jun 2025 14:09:28 +0200 Subject: [PATCH 428/519] output: add output_pending_image_description() --- include/types/wlr_output.h | 2 ++ types/output/output.c | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/include/types/wlr_output.h b/include/types/wlr_output.h index 2dc979c66..f901505af 100644 --- a/include/types/wlr_output.h +++ b/include/types/wlr_output.h @@ -8,6 +8,8 @@ void output_pending_resolution(struct wlr_output *output, const struct wlr_output_state *state, int *width, int *height); bool output_pending_enabled(struct wlr_output *output, const struct wlr_output_state *state); +const struct wlr_output_image_description *output_pending_image_description( + struct wlr_output *output, const struct wlr_output_state *state); bool output_pick_format(struct wlr_output *output, const struct wlr_drm_format_set *display_formats, diff --git a/types/output/output.c b/types/output/output.c index 3410138c1..636d155d2 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -500,6 +500,14 @@ bool output_pending_enabled(struct wlr_output *output, return output->enabled; } +const struct wlr_output_image_description *output_pending_image_description( + struct wlr_output *output, const struct wlr_output_state *state) { + if (state->committed & WLR_OUTPUT_STATE_IMAGE_DESCRIPTION) { + return state->image_description; + } + return output->image_description; +} + /** * Compare a struct wlr_output_state with the current state of a struct * wlr_output. From bf40f396bfd0934b4927265ae1c05a16649551df Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 19 Jun 2025 14:09:51 +0200 Subject: [PATCH 429/519] scene: grab image description from output state Alternative to https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5089 --- include/wlr/types/wlr_scene.h | 5 +++++ types/scene/wlr_scene.c | 27 +++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 5d4a056ce..0f4aa4c27 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -574,6 +574,11 @@ void wlr_scene_output_set_position(struct wlr_scene_output *scene_output, struct wlr_scene_output_state_options { struct wlr_scene_timer *timer; + + /** + * Color transform to apply before the output's color transform. Cannot be + * used when the output has a non-NULL image description set. + */ struct wlr_color_transform *color_transform; /** diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 144329491..8c58ea2b1 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1964,6 +1964,17 @@ static enum scene_direct_scanout_result scene_entry_try_direct_scanout( if (buffer->transform != data->transform) { return SCANOUT_INELIGIBLE; } + + const struct wlr_output_image_description *img_desc = output_pending_image_description(scene_output->output, state); + if (buffer->transfer_function != 0 || buffer->primaries != 0) { + if (img_desc == NULL || img_desc->transfer_function != buffer->transfer_function || + img_desc->primaries != buffer->primaries) { + return false; + } + } else if (img_desc != NULL) { + return false; + } + if (buffer->transfer_function != 0 && buffer->transfer_function != WLR_COLOR_TRANSFER_FUNCTION_SRGB) { return false; } @@ -2269,14 +2280,30 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, timer->pre_render_duration = timespec_to_nsec(&duration); } + struct wlr_color_transform *color_transform = NULL; + const struct wlr_color_primaries *primaries = NULL; + struct wlr_color_primaries primaries_value; + const struct wlr_output_image_description *img_desc = output_pending_image_description(output, state); + if (img_desc != NULL) { + color_transform = wlr_color_transform_init_linear_to_inverse_eotf(img_desc->transfer_function); + wlr_color_primaries_from_named(&primaries_value, img_desc->primaries); + primaries = &primaries_value; + } + if (options->color_transform != NULL) { + assert(color_transform == NULL); + color_transform = wlr_color_transform_ref(options->color_transform); + } + scene_output->in_point++; struct wlr_render_pass *render_pass = wlr_renderer_begin_buffer_pass(output->renderer, buffer, &(struct wlr_buffer_pass_options){ .timer = timer ? timer->render_timer : NULL, .color_transform = options->color_transform, + .primaries = primaries, .signal_timeline = scene_output->in_timeline, .signal_point = scene_output->in_point, }); + wlr_color_transform_unref(color_transform); if (render_pass == NULL) { wlr_buffer_unlock(buffer); return false; From aecb867098f575fc97809c652b92c78b078b533b Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 25 Jun 2025 08:49:19 +0200 Subject: [PATCH 430/519] output: add full HDR metadata to wlr_output_image_description This allows sinks to improve their tone mapping. --- include/wlr/types/wlr_output.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 1ef289008..6a0dd0455 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -91,10 +91,20 @@ enum wlr_output_state_mode_type { * Supported primaries are advertised in wlr_output.supported_primaries. * Supported transfer functions are advertised in * wlr_output.supported_transfer_functions. + * + * mastering_display_primaries, mastering_luminance, max_cll and max_fall are + * optional. Luminances are given in cd/m². */ struct wlr_output_image_description { enum wlr_color_named_primaries primaries; enum wlr_color_transfer_function transfer_function; + + struct wlr_color_primaries mastering_display_primaries; + struct { + double min, max; + } mastering_luminance; + double max_cll; // max content light level + double max_fall; // max frame-average light level }; /** From 8c7041c4e842c9fb029a6371eb53f73aa98e7b31 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 25 Jun 2025 08:54:02 +0200 Subject: [PATCH 431/519] backend/drm: relay full HDR metadata --- backend/drm/atomic.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index 66569232b..33ce6f42d 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -190,6 +190,16 @@ static uint8_t convert_cta861_eotf(enum wlr_color_transfer_function tf) { abort(); // unreachable } +static uint16_t convert_cta861_color_coord(double v) { + if (v < 0) { + v = 0; + } + if (v > 1) { + v = 1; + } + return (uint16_t)round(v * 50000); +} + static bool create_hdr_output_metadata_blob(struct wlr_drm_backend *drm, const struct wlr_output_image_description *img_desc, uint32_t *blob_id) { if (img_desc == NULL) { @@ -202,6 +212,28 @@ static bool create_hdr_output_metadata_blob(struct wlr_drm_backend *drm, .hdmi_metadata_type1 = { .eotf = convert_cta861_eotf(img_desc->transfer_function), .metadata_type = 0, + .display_primaries = { + { + .x = convert_cta861_color_coord(img_desc->mastering_display_primaries.red.x), + .y = convert_cta861_color_coord(img_desc->mastering_display_primaries.red.y), + }, + { + .x = convert_cta861_color_coord(img_desc->mastering_display_primaries.green.x), + .y = convert_cta861_color_coord(img_desc->mastering_display_primaries.green.y), + }, + { + .x = convert_cta861_color_coord(img_desc->mastering_display_primaries.blue.x), + .y = convert_cta861_color_coord(img_desc->mastering_display_primaries.blue.y), + }, + }, + .white_point = { + .x = convert_cta861_color_coord(img_desc->mastering_display_primaries.white.x), + .y = convert_cta861_color_coord(img_desc->mastering_display_primaries.white.y), + }, + .max_display_mastering_luminance = img_desc->mastering_luminance.max, + .min_display_mastering_luminance = img_desc->mastering_luminance.min * 0.0001, + .max_cll = img_desc->max_cll, + .max_fall = img_desc->max_fall, }, }; if (drmModeCreatePropertyBlob(drm->fd, &metadata, sizeof(metadata), blob_id) != 0) { From 48bd1831feff89ac94aacc2218601c2b569ef70c Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 29 Jun 2025 10:57:56 +0200 Subject: [PATCH 432/519] render/egl: fix software rendering check Commit b4ce0d8b39a4 ("render/egl: accept negative DRM FD to select software rendering") added an EXT_device_drm check to figure out whether the user selected a device with a DRM FD or without one. However, for KMS-only devices, Mesa will never advertise the selected KMS node: https://gitlab.freedesktop.org/mesa/mesa/-/blob/3f1d40d230c98d4ca9d2e20b214723a7b7d265d2/src/egl/main/egldevice.c#L109 Instead, pass down a parameter to indicate whether a DRM FD was passed in. Fixes: b4ce0d8b39a4 ("render/egl: accept negative DRM FD to select software rendering") --- render/egl.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/render/egl.c b/render/egl.c index 07d3a1425..6f3e9c8ca 100644 --- a/render/egl.c +++ b/render/egl.c @@ -260,7 +260,8 @@ static struct wlr_egl *egl_create(void) { return egl; } -static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display) { +static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display, + bool allow_software) { egl->display = display; EGLint major, minor; @@ -326,9 +327,8 @@ static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display) { // The only way a non-DRM device is selected is when the user // explicitly picks software rendering - if (check_egl_ext(device_exts_str, "EGL_MESA_device_software") && - egl->exts.EXT_device_drm) { - if (env_parse_bool("WLR_RENDERER_ALLOW_SOFTWARE")) { + if (check_egl_ext(device_exts_str, "EGL_MESA_device_software")) { + if (allow_software || env_parse_bool("WLR_RENDERER_ALLOW_SOFTWARE")) { wlr_log(WLR_INFO, "Using software rendering"); } else { wlr_log(WLR_ERROR, "Software rendering detected, please use " @@ -382,7 +382,7 @@ static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display) { } static bool egl_init(struct wlr_egl *egl, EGLenum platform, - void *remote_display) { + void *remote_display, bool allow_software) { EGLint display_attribs[3] = {0}; size_t display_attribs_len = 0; @@ -401,7 +401,7 @@ static bool egl_init(struct wlr_egl *egl, EGLenum platform, return false; } - if (!egl_init_display(egl, display)) { + if (!egl_init_display(egl, display, allow_software)) { if (egl->exts.KHR_display_reference) { eglTerminate(display); } @@ -556,6 +556,8 @@ static int open_render_node(int drm_fd) { } struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) { + bool allow_software = drm_fd < 0; + struct wlr_egl *egl = egl_create(); if (egl == NULL) { wlr_log(WLR_ERROR, "Failed to create EGL context"); @@ -569,7 +571,7 @@ struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) { */ EGLDeviceEXT egl_device = get_egl_device_from_drm_fd(egl, drm_fd); if (egl_device != EGL_NO_DEVICE_EXT) { - if (egl_init(egl, EGL_PLATFORM_DEVICE_EXT, egl_device)) { + if (egl_init(egl, EGL_PLATFORM_DEVICE_EXT, egl_device, allow_software)) { wlr_log(WLR_DEBUG, "Using EGL_PLATFORM_DEVICE_EXT"); return egl; } @@ -594,7 +596,7 @@ struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) { goto error; } - if (egl_init(egl, EGL_PLATFORM_GBM_KHR, egl->gbm_device)) { + if (egl_init(egl, EGL_PLATFORM_GBM_KHR, egl->gbm_device, allow_software)) { wlr_log(WLR_DEBUG, "Using EGL_PLATFORM_GBM_KHR"); return egl; } @@ -633,7 +635,7 @@ struct wlr_egl *wlr_egl_create_with_context(EGLDisplay display, return NULL; } - if (!egl_init_display(egl, display)) { + if (!egl_init_display(egl, display, true)) { free(egl); return NULL; } From 58c3680d96bffa1b8f59c274050a6ecfa27e7c23 Mon Sep 17 00:00:00 2001 From: David Turner Date: Mon, 30 Jun 2025 15:45:03 +0100 Subject: [PATCH 433/519] scene: Block damage on single-pixel buffer textures We cache whether buffers are single-pixel buffers (and if so what color they are) to allow rendering optimizations. But this breaks if the client changes out the single-pixel buffer for one with a different color, because this updates the texture in-place instead of actually changing the buffer. We can fix this by blocking in-place texture updates for single pixel buffers. Original bug: https://codeberg.org/ifreund/waylock/issues/121 See also: !5092 --- types/scene/surface.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/types/scene/surface.c b/types/scene/surface.c index 07ef7ebe4..1ee0e3134 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -127,8 +127,11 @@ static void scene_buffer_unmark_client_buffer(struct wlr_scene_buffer *scene_buf return; } - assert(buffer->n_ignore_locks > 0); - buffer->n_ignore_locks--; + // If the buffer was a single-pixel buffer where we cached its color + // then it won't have been marked as damage-allowed. + if (buffer->n_ignore_locks > 0) { + buffer->n_ignore_locks--; + } } static int min(int a, int b) { @@ -229,7 +232,22 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { scene_buffer_unmark_client_buffer(scene_buffer); if (surface->buffer) { - client_buffer_mark_next_can_damage(surface->buffer); + // If we've cached the buffer's single-pixel buffer color + // then any in-place updates to the texture wouldn't be + // reflected in rendering. So only allow in-place texture + // updates if it's not a single pixel buffer. Note that we + // can't use the cached scene_buffer->is_single_pixel_buffer + // because that's only set later on. + bool is_single_pixel_buffer = false; + struct wlr_client_buffer *client_buffer = wlr_client_buffer_get(&surface->buffer->base); + if (client_buffer != NULL && client_buffer->source != NULL) { + struct wlr_single_pixel_buffer_v1 *spb = + wlr_single_pixel_buffer_v1_try_from_buffer(client_buffer->source); + is_single_pixel_buffer = spb != NULL; + } + if (!is_single_pixel_buffer) { + client_buffer_mark_next_can_damage(surface->buffer); + } struct wlr_linux_drm_syncobj_surface_v1_state *syncobj_surface_state = wlr_linux_drm_syncobj_v1_get_surface_state(surface); From 31b78a4f3afe86b0406bcde89ca8db1b5f338d85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Poisot?= Date: Thu, 3 Jul 2025 12:01:07 +0000 Subject: [PATCH 434/519] scene: fix output transfer functions fixes: bf40f396b --- types/scene/wlr_scene.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 8c58ea2b1..d2327eda2 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -2298,7 +2298,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, struct wlr_render_pass *render_pass = wlr_renderer_begin_buffer_pass(output->renderer, buffer, &(struct wlr_buffer_pass_options){ .timer = timer ? timer->render_timer : NULL, - .color_transform = options->color_transform, + .color_transform = color_transform, .primaries = primaries, .signal_timeline = scene_output->in_timeline, .signal_point = scene_output->in_point, From 6aa654b728e2f30e2f3ca3c2057fc10161c78161 Mon Sep 17 00:00:00 2001 From: Consolatis <40171-Consolatis@users.noreply.gitlab.freedesktop.org> Date: Sun, 6 Jul 2025 14:08:12 +0200 Subject: [PATCH 435/519] wlr_text_input_v3: remove event arguments from header Fixes 2d5492c73770c9de420527df1098fefabe43d689 --- include/wlr/types/wlr_text_input_v3.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/wlr/types/wlr_text_input_v3.h b/include/wlr/types/wlr_text_input_v3.h index f9eb1d7ed..29feb7e6c 100644 --- a/include/wlr/types/wlr_text_input_v3.h +++ b/include/wlr/types/wlr_text_input_v3.h @@ -57,10 +57,10 @@ struct wlr_text_input_v3 { struct wl_list link; struct { - struct wl_signal enable; // struct wlr_text_input_v3 - struct wl_signal commit; // struct wlr_text_input_v3 - struct wl_signal disable; // struct wlr_text_input_v3 - struct wl_signal destroy; // struct wlr_text_input_v3 + struct wl_signal enable; + struct wl_signal commit; + struct wl_signal disable; + struct wl_signal destroy; } events; struct { @@ -75,7 +75,7 @@ struct wlr_text_input_manager_v3 { struct { struct wl_signal new_text_input; // struct wlr_text_input_v3 - struct wl_signal destroy; // struct wlr_text_input_manager_v3 + struct wl_signal destroy; } events; struct { From f4327f52cf18fea41c08e054012b5a85967b18b1 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 3 Apr 2025 15:21:15 +0200 Subject: [PATCH 436/519] xdg-toplevel-tag-v1: new protocol References: https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/238 --- include/wlr/types/wlr_xdg_toplevel_tag_v1.h | 41 ++++++++ protocol/meson.build | 1 + types/meson.build | 1 + types/wlr_xdg_toplevel_tag_v1.c | 104 ++++++++++++++++++++ 4 files changed, 147 insertions(+) create mode 100644 include/wlr/types/wlr_xdg_toplevel_tag_v1.h create mode 100644 types/wlr_xdg_toplevel_tag_v1.c diff --git a/include/wlr/types/wlr_xdg_toplevel_tag_v1.h b/include/wlr/types/wlr_xdg_toplevel_tag_v1.h new file mode 100644 index 000000000..8318cab02 --- /dev/null +++ b/include/wlr/types/wlr_xdg_toplevel_tag_v1.h @@ -0,0 +1,41 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_TYPES_WLR_XDG_TOPLEVEL_TAG_V1_H +#define WLR_TYPES_WLR_XDG_TOPLEVEL_TAG_V1_H + +#include + +struct wlr_xdg_toplevel_tag_manager_v1 { + struct wl_global *global; + + struct { + struct wl_signal set_tag; // struct wlr_xdg_toplevel_tag_manager_v1_set_tag_event + struct wl_signal set_description; // struct wlr_xdg_toplevel_tag_manager_v1_set_description_event + struct wl_signal destroy; + } events; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; +}; + +struct wlr_xdg_toplevel_tag_manager_v1_set_tag_event { + struct wlr_xdg_toplevel *toplevel; + const char *tag; +}; + +struct wlr_xdg_toplevel_tag_manager_v1_set_description_event { + struct wlr_xdg_toplevel *toplevel; + const char *description; +}; + +struct wlr_xdg_toplevel_tag_manager_v1 *wlr_xdg_toplevel_tag_manager_v1_create( + struct wl_display *display, uint32_t version); + +#endif diff --git a/protocol/meson.build b/protocol/meson.build index d092687bc..d6a2eb1f5 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -42,6 +42,7 @@ protocols = { 'xdg-dialog-v1': wl_protocol_dir / 'staging/xdg-dialog/xdg-dialog-v1.xml', 'xdg-system-bell-v1': wl_protocol_dir / 'staging/xdg-system-bell/xdg-system-bell-v1.xml', 'xdg-toplevel-icon-v1': wl_protocol_dir / 'staging/xdg-toplevel-icon/xdg-toplevel-icon-v1.xml', + 'xdg-toplevel-tag-v1': wl_protocol_dir / 'staging/xdg-toplevel-tag/xdg-toplevel-tag-v1.xml', 'xwayland-shell-v1': wl_protocol_dir / 'staging/xwayland-shell/xwayland-shell-v1.xml', 'tearing-control-v1': wl_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml', diff --git a/types/meson.build b/types/meson.build index 43c4eb2ad..0f664d0d8 100644 --- a/types/meson.build +++ b/types/meson.build @@ -103,6 +103,7 @@ wlr_files += files( 'wlr_xdg_output_v1.c', 'wlr_xdg_system_bell_v1.c', 'wlr_xdg_toplevel_icon_v1.c', + 'wlr_xdg_toplevel_tag_v1.c', ) if features.get('drm-backend') diff --git a/types/wlr_xdg_toplevel_tag_v1.c b/types/wlr_xdg_toplevel_tag_v1.c new file mode 100644 index 000000000..da8cfa9a0 --- /dev/null +++ b/types/wlr_xdg_toplevel_tag_v1.c @@ -0,0 +1,104 @@ +#include +#include + +#include +#include + +#include "xdg-toplevel-tag-v1-protocol.h" + +#define MANAGER_VERSION 1 + +static const struct xdg_toplevel_tag_manager_v1_interface manager_impl; + +static struct wlr_xdg_toplevel_tag_manager_v1 *manager_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &xdg_toplevel_tag_manager_v1_interface, &manager_impl)); + return wl_resource_get_user_data(resource); +} + +static void manager_handle_set_tag(struct wl_client *client, struct wl_resource *manager_resource, + struct wl_resource *toplevel_resource, const char *tag) { + struct wlr_xdg_toplevel_tag_manager_v1 *manager = manager_from_resource(manager_resource); + struct wlr_xdg_toplevel *toplevel = wlr_xdg_toplevel_from_resource(toplevel_resource); + + struct wlr_xdg_toplevel_tag_manager_v1_set_tag_event event = { + .toplevel = toplevel, + .tag = tag, + }; + wl_signal_emit_mutable(&manager->events.set_tag, &event); +} + +static void manager_handle_set_description(struct wl_client *client, struct wl_resource *manager_resource, + struct wl_resource *toplevel_resource, const char *description) { + struct wlr_xdg_toplevel_tag_manager_v1 *manager = manager_from_resource(manager_resource); + struct wlr_xdg_toplevel *toplevel = wlr_xdg_toplevel_from_resource(toplevel_resource); + + struct wlr_xdg_toplevel_tag_manager_v1_set_description_event event = { + .toplevel = toplevel, + .description = description, + }; + wl_signal_emit_mutable(&manager->events.set_description, &event); +} + +static void manager_handle_destroy(struct wl_client *client, struct wl_resource *manager_resource) { + wl_resource_destroy(manager_resource); +} + +static const struct xdg_toplevel_tag_manager_v1_interface manager_impl = { + .destroy = manager_handle_destroy, + .set_toplevel_tag = manager_handle_set_tag, + .set_toplevel_description = manager_handle_set_description, +}; + +static void manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { + struct wlr_xdg_toplevel_tag_manager_v1 *manager = data; + + struct wl_resource *resource = wl_resource_create(client, + &xdg_toplevel_tag_manager_v1_interface, version, id); + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &manager_impl, manager, NULL); +} + +static void manager_handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_xdg_toplevel_tag_manager_v1 *manager = + wl_container_of(listener, manager, display_destroy); + + wl_signal_emit_mutable(&manager->events.destroy, NULL); + + assert(wl_list_empty(&manager->events.set_tag.listener_list)); + assert(wl_list_empty(&manager->events.set_description.listener_list)); + assert(wl_list_empty(&manager->events.destroy.listener_list)); + + wl_list_remove(&manager->display_destroy.link); + wl_global_destroy(manager->global); + free(manager); +} + +struct wlr_xdg_toplevel_tag_manager_v1 *wlr_xdg_toplevel_tag_manager_v1_create( + struct wl_display *display, uint32_t version) { + assert(version <= MANAGER_VERSION); + + struct wlr_xdg_toplevel_tag_manager_v1 *manager = calloc(1, sizeof(*manager)); + if (manager == NULL) { + return NULL; + } + + manager->global = wl_global_create(display, &xdg_toplevel_tag_manager_v1_interface, + version, manager, manager_bind); + if (manager->global == NULL) { + free(manager); + return NULL; + } + + wl_signal_init(&manager->events.set_tag); + wl_signal_init(&manager->events.set_description); + wl_signal_init(&manager->events.destroy); + + manager->display_destroy.notify = manager_handle_display_destroy; + wl_display_add_destroy_listener(display, &manager->display_destroy); + + return manager; +} From c39b3ce7a3c4380d7a77f4cb97b3604c227c04d0 Mon Sep 17 00:00:00 2001 From: Consolatis <40171-Consolatis@users.noreply.gitlab.freedesktop.org> Date: Thu, 10 Jul 2025 19:46:17 +0200 Subject: [PATCH 437/519] transient_seat: initialize seat destroy listener This fixes a `wl_list_remove()` from an uninitialized listener when using `wlr_transient_seat_v1_deny()` in a `create_seat` handler. --- types/wlr_transient_seat_v1.c | 1 + 1 file changed, 1 insertion(+) diff --git a/types/wlr_transient_seat_v1.c b/types/wlr_transient_seat_v1.c index af8f87ccf..d99ee6edb 100644 --- a/types/wlr_transient_seat_v1.c +++ b/types/wlr_transient_seat_v1.c @@ -64,6 +64,7 @@ static void manager_create_transient_seat(struct wl_client *client, wl_resource_set_implementation(seat->resource, &transient_seat_impl, seat, transient_seat_handle_resource_destroy); + wl_list_init(&seat->seat_destroy.link); wl_signal_emit_mutable(&manager->events.create_seat, seat); return; From c14aa1d0b82b5e1853a800534f76b90bc55df392 Mon Sep 17 00:00:00 2001 From: YaoBing Xiao Date: Sun, 13 Jul 2025 23:31:00 +0800 Subject: [PATCH 438/519] render/vulkan: destroy vulkan instance when drm phdev mismatch --- render/vulkan/renderer.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index a0e456580..266ac61cf 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -2514,14 +2514,13 @@ struct wlr_renderer *wlr_vk_renderer_create_with_drm_fd(int drm_fd) { if (!phdev) { // We rather fail here than doing some guesswork wlr_log(WLR_ERROR, "Could not match drm and vulkan device"); - return NULL; + goto error; } struct wlr_vk_device *dev = vulkan_device_create(ini, phdev); if (!dev) { wlr_log(WLR_ERROR, "Failed to create vulkan device"); - vulkan_instance_destroy(ini); - return NULL; + goto error; } // Do not use the drm_fd that was passed in: we should prefer the render @@ -2529,6 +2528,10 @@ struct wlr_renderer *wlr_vk_renderer_create_with_drm_fd(int drm_fd) { dev->drm_fd = vulkan_open_phdev_drm_fd(phdev); return vulkan_renderer_create_for_device(dev); + +error: + vulkan_instance_destroy(ini); + return NULL; } VkInstance wlr_vk_renderer_get_instance(struct wlr_renderer *renderer) { From f5dc6416f0f7142145fbe63913d4dcdeb1305149 Mon Sep 17 00:00:00 2001 From: David Turner Date: Tue, 15 Jul 2025 14:01:54 +0100 Subject: [PATCH 439/519] util/mem: Move memdup to new util/mem.c file This seems handy and I want to use this in wlr_color_representation. --- include/util/mem.h | 14 ++++++++++++++ types/wlr_color_management_v1.c | 13 ++----------- util/mem.c | 16 ++++++++++++++++ util/meson.build | 1 + 4 files changed, 33 insertions(+), 11 deletions(-) create mode 100644 include/util/mem.h create mode 100644 util/mem.c diff --git a/include/util/mem.h b/include/util/mem.h new file mode 100644 index 000000000..8971d2087 --- /dev/null +++ b/include/util/mem.h @@ -0,0 +1,14 @@ +#ifndef UTIL_MEM_H +#define UTIL_MEM_H + +#include +#include + +/** + * Allocate a new block of memory and copy *src to it, then store the address + * of the new allocation in *out. Returns true if it worked, or false if + * allocation failed. + */ +bool memdup(void *out, const void *src, size_t size); + +#endif // UTIL_MEM_H diff --git a/types/wlr_color_management_v1.c b/types/wlr_color_management_v1.c index b5e9a65fa..d9a3aba1b 100644 --- a/types/wlr_color_management_v1.c +++ b/types/wlr_color_management_v1.c @@ -1,12 +1,14 @@ #include #include #include + #include #include #include #include #include "render/color.h" +#include "util/mem.h" #define COLOR_MANAGEMENT_V1_VERSION 1 @@ -898,17 +900,6 @@ static void manager_handle_display_destroy(struct wl_listener *listener, void *d free(manager); } -static bool memdup(void *out, const void *src, size_t size) { - void *dst = malloc(size); - if (dst == NULL) { - return false; - } - memcpy(dst, src, size); - void **dst_ptr = out; - *dst_ptr = dst; - return true; -} - struct wlr_color_manager_v1 *wlr_color_manager_v1_create(struct wl_display *display, uint32_t version, const struct wlr_color_manager_v1_options *options) { assert(version <= COLOR_MANAGEMENT_V1_VERSION); diff --git a/util/mem.c b/util/mem.c new file mode 100644 index 000000000..cfd2f80e4 --- /dev/null +++ b/util/mem.c @@ -0,0 +1,16 @@ +#include +#include +#include + +#include "util/mem.h" + +bool memdup(void *out, const void *src, size_t size) { + void *dst = malloc(size); + if (dst == NULL) { + return false; + } + memcpy(dst, src, size); + void **dst_ptr = out; + *dst_ptr = dst; + return true; +} diff --git a/util/meson.build b/util/meson.build index fbdd54bbb..d67911e52 100644 --- a/util/meson.build +++ b/util/meson.build @@ -6,6 +6,7 @@ wlr_files += files( 'global.c', 'log.c', 'matrix.c', + 'mem.c', 'rect_union.c', 'region.c', 'set.c', From eff620770cca757b611e3b31812442e70dd7fbe7 Mon Sep 17 00:00:00 2001 From: David Turner Date: Tue, 4 Feb 2025 17:44:58 +0000 Subject: [PATCH 440/519] color-representation-v1: new protocol Add a minimal implementation of the color-representation-v1 protocol (not including anything to actually use the new properties in rendering/scanout) --- .../wlr/types/wlr_color_representation_v1.h | 85 +++++ protocol/meson.build | 1 + types/meson.build | 1 + types/wlr_color_representation_v1.c | 340 ++++++++++++++++++ 4 files changed, 427 insertions(+) create mode 100644 include/wlr/types/wlr_color_representation_v1.h create mode 100644 types/wlr_color_representation_v1.c diff --git a/include/wlr/types/wlr_color_representation_v1.h b/include/wlr/types/wlr_color_representation_v1.h new file mode 100644 index 000000000..251f4e6f9 --- /dev/null +++ b/include/wlr/types/wlr_color_representation_v1.h @@ -0,0 +1,85 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_TYPES_WLR_COLOR_REPRESENTATION_V1_H +#define WLR_TYPES_WLR_COLOR_REPRESENTATION_V1_H + +#include +#include + +#include "color-representation-v1-protocol.h" + +struct wlr_surface; + +// Supported coefficients and range are always paired together +struct wlr_color_representation_v1_coeffs_and_range { + enum wp_color_representation_surface_v1_coefficients coeffs; + enum wp_color_representation_surface_v1_range range; +}; + +struct wlr_color_representation_manager_v1 { + struct wl_global *global; + + struct { + // Manager is being destroyed + struct wl_signal destroy; + } events; + + struct { + enum wp_color_representation_surface_v1_alpha_mode + *supported_alpha_modes; + size_t supported_alpha_modes_len; + + struct wlr_color_representation_v1_coeffs_and_range + *supported_coeffs_and_ranges; + size_t supported_coeffs_and_ranges_len; + + struct wl_listener display_destroy; + } WLR_PRIVATE; +}; + +// Options used when initialising a wlr_color_representation_manager_v1 +struct wlr_color_representation_v1_options { + enum wp_color_representation_surface_v1_alpha_mode + *supported_alpha_modes; + size_t supported_alpha_modes_len; + + const struct wlr_color_representation_v1_coeffs_and_range + *supported_coeffs_and_ranges; + size_t supported_coeffs_and_ranges_len; +}; + +struct wlr_color_representation_manager_v1 *wlr_color_representation_manager_v1_create( + struct wl_display *display, uint32_t version, + const struct wlr_color_representation_v1_options *options); + +// This is all the color-representation state which can be attached to a +// surface, double-buffered and made current on commit +struct wlr_color_representation_v1_surface_state { + // The enum premultiplied_electrical has value zero and is defined + // to be the default if unspecified. + enum wp_color_representation_surface_v1_alpha_mode alpha_mode; + + // If zero then indicates unset, otherwise values correspond to + // enum wp_color_representation_surface_v1_coefficients + uint32_t coefficients; + + // If zero then indicates unset, otherwise values correspond to + // enum wp_color_representation_surface_v1_range + uint32_t range; + + // If zero then indicates unset, otherwise values correspond to + // enum wp_color_representation_surface_v1_chroma_location + uint32_t chroma_location; +}; + +// Get the current color representation state committed to a surface +const struct wlr_color_representation_v1_surface_state *wlr_color_representation_v1_get_surface_state( + struct wlr_surface *surface); + +#endif // WLR_TYPES_WLR_COLOR_REPRESENTATION_V1_H diff --git a/protocol/meson.build b/protocol/meson.build index d6a2eb1f5..c8547933c 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -25,6 +25,7 @@ protocols = { # Staging upstream protocols 'alpha-modifier-v1': wl_protocol_dir / 'staging/alpha-modifier/alpha-modifier-v1.xml', 'color-management-v1': wl_protocol_dir / 'staging/color-management/color-management-v1.xml', + 'color-representation-v1': wl_protocol_dir / 'staging/color-representation/color-representation-v1.xml', 'content-type-v1': wl_protocol_dir / 'staging/content-type/content-type-v1.xml', 'cursor-shape-v1': wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml', 'drm-lease-v1': wl_protocol_dir / 'staging/drm-lease/drm-lease-v1.xml', diff --git a/types/meson.build b/types/meson.build index 0f664d0d8..25a0d4434 100644 --- a/types/meson.build +++ b/types/meson.build @@ -48,6 +48,7 @@ wlr_files += files( 'wlr_drm.c', 'wlr_export_dmabuf_v1.c', 'wlr_foreign_toplevel_management_v1.c', + 'wlr_color_representation_v1.c', 'wlr_ext_image_copy_capture_v1.c', 'wlr_ext_foreign_toplevel_list_v1.c', 'wlr_ext_data_control_v1.c', diff --git a/types/wlr_color_representation_v1.c b/types/wlr_color_representation_v1.c new file mode 100644 index 000000000..294f946a0 --- /dev/null +++ b/types/wlr_color_representation_v1.c @@ -0,0 +1,340 @@ +#include +#include + +#include +#include +#include +#include + +#include "color-representation-v1-protocol.h" +#include "util/mem.h" + +#define WP_COLOR_REPRESENTATION_VERSION 1 + +struct wlr_color_representation_v1 { + struct wl_resource *resource; + struct wlr_surface *surface; + + struct wlr_color_representation_manager_v1 *manager; + + // Associate the wlr_color_representation_v1 with a wlr_surface + struct wlr_addon addon; + + struct wlr_surface_synced synced; + struct wlr_color_representation_v1_surface_state pending, current; +}; + +static const struct wp_color_representation_surface_v1_interface color_repr_impl; + +static struct wlr_color_representation_v1 *color_repr_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &wp_color_representation_surface_v1_interface, + &color_repr_impl)); + return wl_resource_get_user_data(resource); +} + +static void color_repr_handle_destroy(struct wl_client *client, + struct wl_resource *resource) { + // Actual destroying is done by the resource-destroy handler + wl_resource_destroy(resource); +} + +static void color_repr_handle_set_alpha_mode(struct wl_client *client, + struct wl_resource *resource, uint32_t alpha_mode) { + struct wlr_color_representation_v1 *color_repr = + color_repr_from_resource(resource); + if (color_repr == NULL) { + wl_resource_post_error(resource, WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_INERT, + "Associated surface has been destroyed, object is inert"); + return; + } + + bool found = false; + for (size_t i = 0; i < color_repr->manager->supported_alpha_modes_len; i++) { + if (color_repr->manager->supported_alpha_modes[i] == alpha_mode) { + found = true; + break; + } + } + if (!found) { + wl_resource_post_error(resource, + WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_ALPHA_MODE, + "Unsupported alpha mode"); + return; + } + + color_repr->pending.alpha_mode = alpha_mode; +} + +static void color_repr_handle_set_coefficients_and_range(struct wl_client *client, + struct wl_resource *resource, uint32_t coefficients, + uint32_t range) { + struct wlr_color_representation_v1 *color_repr = + color_repr_from_resource(resource); + if (color_repr == NULL) { + wl_resource_post_error(resource, WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_INERT, + "Associated surface has been destroyed, object is inert"); + return; + } + + bool found = false; + for (size_t i = 0; i < color_repr->manager->supported_coeffs_and_ranges_len; i++) { + struct wlr_color_representation_v1_coeffs_and_range *supported = + &color_repr->manager->supported_coeffs_and_ranges[i]; + if (supported->coeffs == coefficients && supported->range == range) { + found = true; + break; + } + } + if (!found) { + wl_resource_post_error(resource, + WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_COEFFICIENTS, + "Unsupported coefficients/range pair"); + return; + } + + color_repr->pending.coefficients = coefficients; + color_repr->pending.range = range; +} + +static void color_repr_handle_set_chroma_location(struct wl_client *client, + struct wl_resource *resource, uint32_t chroma_location) { + struct wlr_color_representation_v1 *color_repr = + color_repr_from_resource(resource); + if (color_repr == NULL) { + wl_resource_post_error(resource, WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_INERT, + "Associated surface has been destroyed, object is inert"); + return; + } + + uint32_t version = wl_resource_get_version(resource); + if (!wp_color_representation_surface_v1_chroma_location_is_valid( + version, chroma_location)) { + wlr_log(WLR_ERROR, "Client sent chroma location which isn't a valid enum value"); + // TODO: Post actual error once + // https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/429 + // is merged and wlroots depends on a new enough wayland-protocols. + wl_client_post_implementation_error(resource->client, + "Chroma location is not a valid enum value"); + return; + } + + // In this protocol there's no concept of supported chroma locations + // from a client point-of-view. The compositor should just ignore any + // chroma locations it doesn't know what to do with. + + color_repr->pending.chroma_location = chroma_location; +} + +static const struct wp_color_representation_surface_v1_interface color_repr_impl = { + .destroy = color_repr_handle_destroy, + .set_alpha_mode = color_repr_handle_set_alpha_mode, + .set_coefficients_and_range = color_repr_handle_set_coefficients_and_range, + .set_chroma_location = color_repr_handle_set_chroma_location, +}; + +static void color_repr_destroy(struct wlr_color_representation_v1 *color_repr) { + if (color_repr == NULL) { + return; + } + wlr_surface_synced_finish(&color_repr->synced); + wlr_addon_finish(&color_repr->addon); + wl_resource_set_user_data(color_repr->resource, NULL); + free(color_repr); +} + +static void color_repr_addon_destroy(struct wlr_addon *addon) { + struct wlr_color_representation_v1 *color_repr = + wl_container_of(addon, color_repr, addon); + color_repr_destroy(color_repr); +} + +static const struct wlr_addon_interface surface_addon_impl = { + .name = "wlr_color_representation_v1", + .destroy = color_repr_addon_destroy, +}; + +static void color_repr_handle_resource_destroy(struct wl_resource *resource) { + struct wlr_color_representation_v1 *color_repr = + color_repr_from_resource(resource); + color_repr_destroy(color_repr); +} + +static void color_repr_manager_handle_destroy(struct wl_client *client, + struct wl_resource *resource) { + // Actual destroying is done by the resource-destroy handler + wl_resource_destroy(resource); +} + +static const struct wlr_surface_synced_impl surface_synced_impl = { + .state_size = sizeof(struct wlr_color_representation_v1_surface_state), +}; + +static struct wlr_color_representation_v1 *color_repr_from_surface( + struct wlr_surface *surface) { + struct wlr_addon *addon = wlr_addon_find(&surface->addons, NULL, &surface_addon_impl); + if (addon == NULL) { + return NULL; + } + struct wlr_color_representation_v1 *color_repr = wl_container_of(addon, color_repr, addon); + return color_repr; +} + +static const struct wp_color_representation_manager_v1_interface color_repr_manager_impl; + +static struct wlr_color_representation_manager_v1 *manager_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &wp_color_representation_manager_v1_interface, + &color_repr_manager_impl)); + return wl_resource_get_user_data(resource); +} + +static void color_repr_manager_handle_get_surface(struct wl_client *client, + struct wl_resource *manager_resource, + uint32_t color_repr_id, + struct wl_resource *surface_resource) { + struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); + + // Check if there's already a color-representation attached to + // this surface + if (color_repr_from_surface(surface) != NULL) { + wl_resource_post_error(manager_resource, + WP_COLOR_REPRESENTATION_MANAGER_V1_ERROR_SURFACE_EXISTS, + "wp_color_representation_surface_v1 already exists for this surface"); + return; + } + + struct wlr_color_representation_v1 *color_repr = calloc(1, sizeof(*color_repr)); + if (!color_repr) { + wl_resource_post_no_memory(manager_resource); + return; + } + + color_repr->manager = manager_from_resource(manager_resource); + + if (!wlr_surface_synced_init(&color_repr->synced, surface, + &surface_synced_impl, &color_repr->pending, &color_repr->current)) { + free(color_repr); + wl_resource_post_no_memory(manager_resource); + return; + } + + uint32_t version = wl_resource_get_version(manager_resource); + color_repr->resource = wl_resource_create(client, + &wp_color_representation_surface_v1_interface, version, color_repr_id); + if (color_repr->resource == NULL) { + wlr_surface_synced_finish(&color_repr->synced); + free(color_repr); + wl_resource_post_no_memory(manager_resource); + return; + } + wl_resource_set_implementation(color_repr->resource, + &color_repr_impl, color_repr, color_repr_handle_resource_destroy); + + wlr_addon_init(&color_repr->addon, &surface->addons, NULL, &surface_addon_impl); +} + +static const struct wp_color_representation_manager_v1_interface color_repr_manager_impl = { + .destroy = color_repr_manager_handle_destroy, + .get_surface = color_repr_manager_handle_get_surface, +}; + +static void send_supported(struct wlr_color_representation_manager_v1 *manager, + struct wl_resource *resource) { + for (size_t i = 0; i < manager->supported_alpha_modes_len; i++) { + wp_color_representation_manager_v1_send_supported_alpha_mode( + resource, manager->supported_alpha_modes[i]); + } + + for (size_t i = 0; i < manager->supported_coeffs_and_ranges_len; i++) { + struct wlr_color_representation_v1_coeffs_and_range *supported = + &manager->supported_coeffs_and_ranges[i]; + wp_color_representation_manager_v1_send_supported_coefficients_and_ranges( + resource, supported->coeffs, supported->range); + } + + // Note that there is no event for supported chroma locations in the + // v1 protocol. + + wp_color_representation_manager_v1_send_done(resource); +} + +static void manager_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id) { + struct wlr_color_representation_manager_v1 *manager = data; + + struct wl_resource *resource = wl_resource_create(client, + &wp_color_representation_manager_v1_interface, + version, id); + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &color_repr_manager_impl, manager, NULL); + + send_supported(manager, resource); +} + +static void handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_color_representation_manager_v1 *manager = + wl_container_of(listener, manager, display_destroy); + + wl_signal_emit_mutable(&manager->events.destroy, NULL); + + assert(wl_list_empty(&manager->events.destroy.listener_list)); + + wl_list_remove(&manager->display_destroy.link); + wl_global_destroy(manager->global); + free(manager); +} + +struct wlr_color_representation_manager_v1 *wlr_color_representation_manager_v1_create( + struct wl_display *display, uint32_t version, + const struct wlr_color_representation_v1_options *options) { + assert(version <= WP_COLOR_REPRESENTATION_VERSION); + + struct wlr_color_representation_manager_v1 *manager = calloc(1, sizeof(*manager)); + if (manager == NULL) { + return NULL; + } + + manager->global = wl_global_create(display, + &wp_color_representation_manager_v1_interface, + version, manager, manager_bind); + if (manager->global == NULL) { + free(manager); + return NULL; + } + + bool ok = true; + ok &= memdup(&manager->supported_alpha_modes, + options->supported_alpha_modes, + sizeof(options->supported_alpha_modes[0]) * options->supported_alpha_modes_len); + ok &= memdup(&manager->supported_coeffs_and_ranges, + options->supported_coeffs_and_ranges, + sizeof(options->supported_coeffs_and_ranges[0]) * options->supported_coeffs_and_ranges_len); + if (!ok) { + goto err_options; + } + + manager->display_destroy.notify = handle_display_destroy; + wl_display_add_destroy_listener(display, &manager->display_destroy); + + return manager; + +err_options: + free(manager->supported_alpha_modes); + free(manager->supported_coeffs_and_ranges); + return NULL; +} + +const struct wlr_color_representation_v1_surface_state *wlr_color_representation_v1_get_surface_state( + struct wlr_surface *surface) { + struct wlr_color_representation_v1 *color_repr = color_repr_from_surface(surface); + if (color_repr == NULL) { + return NULL; + } + return &color_repr->current; +} From a4eb2cff46a9b07582873f26d97ddab9abc7fd92 Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 17 Jul 2025 10:55:11 +0100 Subject: [PATCH 441/519] color-representation-v1: Add wlr enums + converters Add wlr-internal enums for the properties specified in color-representation-v1 (encoding, range, chroma siting, alpha mode) so that other parts of wlroots can use these without depending on the protocol specifics and without needing to include the protocol headers. Also add conversion functions to convert the protocol enum values into the wlroots enum values. --- include/wlr/render/color.h | 51 ++++++++++++++ .../wlr/types/wlr_color_representation_v1.h | 9 +++ types/wlr_color_representation_v1.c | 66 +++++++++++++++++++ 3 files changed, 126 insertions(+) diff --git a/include/wlr/render/color.h b/include/wlr/render/color.h index 3341668a8..e2397b75a 100644 --- a/include/wlr/render/color.h +++ b/include/wlr/render/color.h @@ -30,6 +30,57 @@ enum wlr_color_transfer_function { WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR = 1 << 2, }; +/** + * Specifies alpha blending modes. Note that premultiplied_electrical + * is the default, so there is no "none" or "unset" value. + */ +enum wlr_alpha_mode { + WLR_COLOR_ALPHA_MODE_PREMULTIPLIED_ELECTRICAL, + WLR_COLOR_ALPHA_MODE_PREMULTIPLIED_OPTICAL, + WLR_COLOR_ALPHA_MODE_STRAIGHT, +}; + +/** + * Well-known color encodings, each representing a set of matrix coefficients + * used to convert that particular YCbCr encoding to RGB. NONE means the + * value is unset or unknown. + */ +enum wlr_color_encoding { + WLR_COLOR_ENCODING_NONE, + WLR_COLOR_ENCODING_IDENTITY, + WLR_COLOR_ENCODING_BT709, + WLR_COLOR_ENCODING_FCC, + WLR_COLOR_ENCODING_BT601, + WLR_COLOR_ENCODING_SMPTE240, + WLR_COLOR_ENCODING_BT2020, + WLR_COLOR_ENCODING_BT2020_CL, + WLR_COLOR_ENCODING_ICTCP, +}; + +/** + * Specifies whether a particular color-encoding uses full- or limited-range + * values. NONE means the value is unset or unknown. + */ +enum wlr_color_range { + WLR_COLOR_RANGE_NONE, + WLR_COLOR_RANGE_LIMITED, + WLR_COLOR_RANGE_FULL, +}; + +/** + * Chroma sample locations, corresponding to Chroma420SampleLocType code + * points in H.273. NONE means the value is unset or unknown. + */ +enum wlr_color_chroma_location { + WLR_COLOR_CHROMA_LOCATION_NONE, + WLR_COLOR_CHROMA_LOCATION_TYPE0, + WLR_COLOR_CHROMA_LOCATION_TYPE1, + WLR_COLOR_CHROMA_LOCATION_TYPE2, + WLR_COLOR_CHROMA_LOCATION_TYPE3, + WLR_COLOR_CHROMA_LOCATION_TYPE4, + WLR_COLOR_CHROMA_LOCATION_TYPE5, +}; + /** * CIE 1931 xy chromaticity coordinates. */ diff --git a/include/wlr/types/wlr_color_representation_v1.h b/include/wlr/types/wlr_color_representation_v1.h index 251f4e6f9..d575eedf5 100644 --- a/include/wlr/types/wlr_color_representation_v1.h +++ b/include/wlr/types/wlr_color_representation_v1.h @@ -82,4 +82,13 @@ struct wlr_color_representation_v1_surface_state { const struct wlr_color_representation_v1_surface_state *wlr_color_representation_v1_get_surface_state( struct wlr_surface *surface); +enum wlr_alpha_mode wlr_color_representation_v1_alpha_mode_to_wlr( + enum wp_color_representation_surface_v1_alpha_mode wp_val); +enum wlr_color_encoding wlr_color_representation_v1_color_encoding_to_wlr( + enum wp_color_representation_surface_v1_coefficients wp_val); +enum wlr_color_range wlr_color_representation_v1_color_range_to_wlr( + enum wp_color_representation_surface_v1_range wp_val); +enum wlr_color_chroma_location wlr_color_representation_v1_chroma_location_to_wlr( + enum wp_color_representation_surface_v1_chroma_location wp_val); + #endif // WLR_TYPES_WLR_COLOR_REPRESENTATION_V1_H diff --git a/types/wlr_color_representation_v1.c b/types/wlr_color_representation_v1.c index 294f946a0..0bf33d156 100644 --- a/types/wlr_color_representation_v1.c +++ b/types/wlr_color_representation_v1.c @@ -11,6 +11,72 @@ #define WP_COLOR_REPRESENTATION_VERSION 1 +enum wlr_alpha_mode wlr_color_representation_v1_alpha_mode_to_wlr( + enum wp_color_representation_surface_v1_alpha_mode wp_val) { + switch (wp_val) { + case WP_COLOR_REPRESENTATION_SURFACE_V1_ALPHA_MODE_PREMULTIPLIED_ELECTRICAL: + return WLR_COLOR_ALPHA_MODE_PREMULTIPLIED_ELECTRICAL; + case WP_COLOR_REPRESENTATION_SURFACE_V1_ALPHA_MODE_PREMULTIPLIED_OPTICAL: + return WLR_COLOR_ALPHA_MODE_PREMULTIPLIED_OPTICAL; + case WP_COLOR_REPRESENTATION_SURFACE_V1_ALPHA_MODE_STRAIGHT: + return WLR_COLOR_ALPHA_MODE_STRAIGHT; + } + abort(); // unreachable +} + +enum wlr_color_encoding wlr_color_representation_v1_color_encoding_to_wlr( + enum wp_color_representation_surface_v1_coefficients wp_val) { + switch (wp_val) { + case WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_IDENTITY: + return WLR_COLOR_ENCODING_IDENTITY; + case WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_BT709: + return WLR_COLOR_ENCODING_BT709; + case WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_FCC: + return WLR_COLOR_ENCODING_FCC; + case WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_BT601: + return WLR_COLOR_ENCODING_BT601; + case WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_SMPTE240: + return WLR_COLOR_ENCODING_SMPTE240; + case WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_BT2020: + return WLR_COLOR_ENCODING_BT2020; + case WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_BT2020_CL: + return WLR_COLOR_ENCODING_BT2020_CL; + case WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_ICTCP: + return WLR_COLOR_ENCODING_ICTCP; + } + abort(); // unreachable +} + +enum wlr_color_range wlr_color_representation_v1_color_range_to_wlr( + enum wp_color_representation_surface_v1_range wp_val) { + switch (wp_val) { + case WP_COLOR_REPRESENTATION_SURFACE_V1_RANGE_LIMITED: + return WLR_COLOR_RANGE_LIMITED; + case WP_COLOR_REPRESENTATION_SURFACE_V1_RANGE_FULL: + return WLR_COLOR_RANGE_FULL; + } + abort(); // unreachable +} + +enum wlr_color_chroma_location wlr_color_representation_v1_chroma_location_to_wlr( + enum wp_color_representation_surface_v1_chroma_location wp_val) { + switch (wp_val) { + case WP_COLOR_REPRESENTATION_SURFACE_V1_CHROMA_LOCATION_TYPE_0: + return WLR_COLOR_CHROMA_LOCATION_TYPE0; + case WP_COLOR_REPRESENTATION_SURFACE_V1_CHROMA_LOCATION_TYPE_1: + return WLR_COLOR_CHROMA_LOCATION_TYPE1; + case WP_COLOR_REPRESENTATION_SURFACE_V1_CHROMA_LOCATION_TYPE_2: + return WLR_COLOR_CHROMA_LOCATION_TYPE2; + case WP_COLOR_REPRESENTATION_SURFACE_V1_CHROMA_LOCATION_TYPE_3: + return WLR_COLOR_CHROMA_LOCATION_TYPE3; + case WP_COLOR_REPRESENTATION_SURFACE_V1_CHROMA_LOCATION_TYPE_4: + return WLR_COLOR_CHROMA_LOCATION_TYPE4; + case WP_COLOR_REPRESENTATION_SURFACE_V1_CHROMA_LOCATION_TYPE_5: + return WLR_COLOR_CHROMA_LOCATION_TYPE5; + } + abort(); // unreachable +} + struct wlr_color_representation_v1 { struct wl_resource *resource; struct wlr_surface *surface; From d2007d7dc1ffb38a24922a7842c7cebbdbab46ee Mon Sep 17 00:00:00 2001 From: Yixue Wang Date: Fri, 18 Jul 2025 17:56:22 +0800 Subject: [PATCH 442/519] types/color_representation: correctly cleanup in manager create Global should be created after all other initialization finished. Free manager in err_options. --- types/wlr_color_representation_v1.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/types/wlr_color_representation_v1.c b/types/wlr_color_representation_v1.c index 0bf33d156..dbf2ecd52 100644 --- a/types/wlr_color_representation_v1.c +++ b/types/wlr_color_representation_v1.c @@ -366,14 +366,6 @@ struct wlr_color_representation_manager_v1 *wlr_color_representation_manager_v1_ return NULL; } - manager->global = wl_global_create(display, - &wp_color_representation_manager_v1_interface, - version, manager, manager_bind); - if (manager->global == NULL) { - free(manager); - return NULL; - } - bool ok = true; ok &= memdup(&manager->supported_alpha_modes, options->supported_alpha_modes, @@ -385,6 +377,13 @@ struct wlr_color_representation_manager_v1 *wlr_color_representation_manager_v1_ goto err_options; } + manager->global = wl_global_create(display, + &wp_color_representation_manager_v1_interface, + version, manager, manager_bind); + if (manager->global == NULL) { + goto err_options; + } + manager->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &manager->display_destroy); @@ -393,6 +392,7 @@ struct wlr_color_representation_manager_v1 *wlr_color_representation_manager_v1_ err_options: free(manager->supported_alpha_modes); free(manager->supported_coeffs_and_ranges); + free(manager); return NULL; } From ccec4116b3a113e9a082f9712f675c30552a0b35 Mon Sep 17 00:00:00 2001 From: Yixue Wang Date: Sun, 20 Jul 2025 23:01:58 +0800 Subject: [PATCH 443/519] types/color_management: check on invalid image description Check if image description is valid. If not, post error to client. --- types/wlr_color_management_v1.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/types/wlr_color_management_v1.c b/types/wlr_color_management_v1.c index d9a3aba1b..fe0994e0b 100644 --- a/types/wlr_color_management_v1.c +++ b/types/wlr_color_management_v1.c @@ -310,6 +310,13 @@ static void cm_surface_handle_set_image_description(struct wl_client *client, struct wlr_image_description_v1 *image_desc = image_desc_from_resource(image_desc_resource); + if (image_desc == NULL) { + wl_resource_post_error(cm_surface_resource, + WP_COLOR_MANAGEMENT_SURFACE_V1_ERROR_IMAGE_DESCRIPTION, + "Image description to be set is invalid"); + return; + } + bool found = false; for (size_t i = 0; i < cm_surface->manager->render_intents_len; i++) { if (cm_surface->manager->render_intents[i] == render_intent) { From 80c7e0f772e38f56376e7953b148b480ca49ef3a Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Wed, 23 Jul 2025 15:05:54 +0000 Subject: [PATCH 444/519] ext-image-capture-source: output: Apply transform to cursor The cursor can be expected to also be transformed if the output is transformed. --- types/ext_image_capture_source_v1/output.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/types/ext_image_capture_source_v1/output.c b/types/ext_image_capture_source_v1/output.c index a2726c759..1112b64d5 100644 --- a/types/ext_image_capture_source_v1/output.c +++ b/types/ext_image_capture_source_v1/output.c @@ -283,7 +283,8 @@ static void output_cursor_source_copy_frame(struct wlr_ext_image_capture_source_ struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - wlr_ext_image_copy_capture_frame_v1_ready(frame, WL_OUTPUT_TRANSFORM_NORMAL, &now); + wlr_ext_image_copy_capture_frame_v1_ready(frame, + cursor_source->output->transform, &now); } static const struct wlr_ext_image_capture_source_v1_interface output_cursor_source_impl = { From be5e2662113ca40a21f48a5af35a219f50f28794 Mon Sep 17 00:00:00 2001 From: liupeng Date: Wed, 23 Jul 2025 10:53:42 +0800 Subject: [PATCH 445/519] cursor: update output cursor even if output is disabled During suspend, we first disable output and then remove the input device. This causes cursor->state->surface released while cursor->texture leaves. Which leads to use-after-free after resume. --- types/wlr_cursor.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/types/wlr_cursor.c b/types/wlr_cursor.c index 7f99fef0c..6dad11446 100644 --- a/types/wlr_cursor.c +++ b/types/wlr_cursor.c @@ -530,10 +530,6 @@ static void cursor_output_cursor_update(struct wlr_cursor_output_cursor *output_ struct wlr_cursor *cur = output_cursor->cursor; struct wlr_output *output = output_cursor->output_cursor->output; - if (!output->enabled) { - return; - } - cursor_output_cursor_reset_image(output_cursor); if (cur->state->buffer != NULL) { From efb17980a84f88098371d3f258e6eddcebe021b7 Mon Sep 17 00:00:00 2001 From: rewine Date: Mon, 21 Jul 2025 09:49:08 +0800 Subject: [PATCH 446/519] ext_image_capture_source_v1: remove unused struct definition Remove the redundant wlr_ext_foreign_toplevel_image_capture_source_v1 struct that was not used anywhere in the codebase. --- types/ext_image_capture_source_v1/foreign_toplevel.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/types/ext_image_capture_source_v1/foreign_toplevel.c b/types/ext_image_capture_source_v1/foreign_toplevel.c index c79156bce..e5c75e280 100644 --- a/types/ext_image_capture_source_v1/foreign_toplevel.c +++ b/types/ext_image_capture_source_v1/foreign_toplevel.c @@ -6,10 +6,6 @@ #define FOREIGN_TOPLEVEL_IMAGE_SOURCE_MANAGER_V1_VERSION 1 -struct wlr_ext_foreign_toplevel_image_capture_source_v1 { - struct wlr_ext_image_capture_source_v1 base; -}; - static const struct ext_foreign_toplevel_image_capture_source_manager_v1_interface foreign_toplevel_manager_impl; static struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1 * From db5e9ca04ce516d812c8af13672d6894c4341a3e Mon Sep 17 00:00:00 2001 From: llyyr Date: Mon, 21 Jul 2025 23:12:00 +0530 Subject: [PATCH 447/519] meson: bump minimum wayland-protocols version color-representation was added in 1.44. Fixes: eff620770cca757b611e3b31812442e70dd7fbe7 --- protocol/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/meson.build b/protocol/meson.build index c8547933c..5012753b5 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -1,5 +1,5 @@ wayland_protos = dependency('wayland-protocols', - version: '>=1.43', + version: '>=1.44', fallback: 'wayland-protocols', default_options: ['tests=false'], ) From 47a90d6f1a9b45f68f5ebfb5dcc1baac2476d15e Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 18 Jun 2025 22:43:24 +0200 Subject: [PATCH 448/519] color_management_v1: add helpers to convert TF/primaries enums This makes it easier for protocol implementers to tie everything together with wlroots backends and renderers. --- include/wlr/types/wlr_color_management_v1.h | 14 +++++++++++ types/wlr_color_management_v1.c | 26 +++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/include/wlr/types/wlr_color_management_v1.h b/include/wlr/types/wlr_color_management_v1.h index a369806bc..f369fb22c 100644 --- a/include/wlr/types/wlr_color_management_v1.h +++ b/include/wlr/types/wlr_color_management_v1.h @@ -89,4 +89,18 @@ void wlr_color_manager_v1_set_surface_preferred_image_description( struct wlr_color_manager_v1 *manager, struct wlr_surface *surface, const struct wlr_image_description_v1_data *data); +/** + * Convert a protocol transfer function to enum wlr_color_transfer_function. + * Aborts if there is no matching wlroots entry. + */ +enum wlr_color_transfer_function +wlr_color_manager_v1_transfer_function_to_wlr(enum wp_color_manager_v1_transfer_function tf); + +/** + * Convert a protocol named primaries to enum wlr_color_named_primaries. + * Aborts if there is no matching wlroots entry. + */ +enum wlr_color_named_primaries +wlr_color_manager_v1_primaries_to_wlr(enum wp_color_manager_v1_primaries primaries); + #endif diff --git a/types/wlr_color_management_v1.c b/types/wlr_color_management_v1.c index fe0994e0b..b84781f6e 100644 --- a/types/wlr_color_management_v1.c +++ b/types/wlr_color_management_v1.c @@ -992,3 +992,29 @@ void wlr_color_manager_v1_set_surface_preferred_image_description( } } } + +enum wlr_color_transfer_function +wlr_color_manager_v1_transfer_function_to_wlr(enum wp_color_manager_v1_transfer_function tf) { + switch (tf) { + case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB: + return WLR_COLOR_TRANSFER_FUNCTION_SRGB; + case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ: + return WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ; + case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR: + return WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR; + default: + abort(); + } +} + +enum wlr_color_named_primaries +wlr_color_manager_v1_primaries_to_wlr(enum wp_color_manager_v1_primaries primaries) { + switch (primaries) { + case WP_COLOR_MANAGER_V1_PRIMARIES_SRGB: + return WLR_COLOR_NAMED_PRIMARIES_SRGB; + case WP_COLOR_MANAGER_V1_PRIMARIES_BT2020: + return WLR_COLOR_NAMED_PRIMARIES_BT2020; + default: + abort(); + } +} From 2f2c0dfcc63e68915eaf01e5a89408ecc2c9443b Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 18 Jun 2025 22:45:04 +0200 Subject: [PATCH 449/519] scene: use helpers to convert TF/primaries enums --- types/scene/surface.c | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/types/scene/surface.c b/types/scene/surface.c index 1ee0e3134..7653323e3 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -195,30 +195,8 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { const struct wlr_image_description_v1_data *img_desc = wlr_surface_get_image_description_v1_data(surface); if (img_desc != NULL) { - switch (img_desc->tf_named) { - case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB: - tf = WLR_COLOR_TRANSFER_FUNCTION_SRGB; - break; - case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ: - tf = WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ; - break; - case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR: - tf = WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR; - break; - default: - abort(); - } - - switch (img_desc->primaries_named) { - case WP_COLOR_MANAGER_V1_PRIMARIES_SRGB: - primaries = WLR_COLOR_NAMED_PRIMARIES_SRGB; - break; - case WP_COLOR_MANAGER_V1_PRIMARIES_BT2020: - primaries = WLR_COLOR_NAMED_PRIMARIES_BT2020; - break; - default: - abort(); - } + tf = wlr_color_manager_v1_transfer_function_to_wlr(img_desc->tf_named); + primaries = wlr_color_manager_v1_primaries_to_wlr(img_desc->primaries_named); } wlr_scene_buffer_set_opaque_region(scene_buffer, &opaque); From 1beb25a1c8fb5bd1485a552b61424e9d416bce8e Mon Sep 17 00:00:00 2001 From: qaqland Date: Tue, 22 Jul 2025 23:49:00 +0800 Subject: [PATCH 450/519] tinywl: fix cursor disappears when focused window is closed Add listener for wlr_seat->pointer_state.events.focus_change Fix #3802 --- tinywl/tinywl.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tinywl/tinywl.c b/tinywl/tinywl.c index 89837b4fb..b3d902c7e 100644 --- a/tinywl/tinywl.c +++ b/tinywl/tinywl.c @@ -56,6 +56,7 @@ struct tinywl_server { struct wlr_seat *seat; struct wl_listener new_input; struct wl_listener request_cursor; + struct wl_listener pointer_focus_change; struct wl_listener request_set_selection; struct wl_list keyboards; enum tinywl_cursor_mode cursor_mode; @@ -333,6 +334,18 @@ static void seat_request_cursor(struct wl_listener *listener, void *data) { } } +static void seat_pointer_focus_change(struct wl_listener *listener, void *data) { + struct tinywl_server *server = wl_container_of( + listener, server, pointer_focus_change); + /* This event is raised when the pointer focus is changed, including when the + * client is closed. We set the cursor image to its default if target surface + * is NULL */ + struct wlr_seat_pointer_focus_change_event *event = data; + if (event->new_surface == NULL) { + wlr_cursor_set_xcursor(server->cursor, server->cursor_mgr, "default"); + } +} + static void seat_request_set_selection(struct wl_listener *listener, void *data) { /* This event is raised by the seat when a client wants to set the selection, * usually when the user copies something. wlroots allows compositors to @@ -1018,6 +1031,9 @@ int main(int argc, char *argv[]) { server.request_cursor.notify = seat_request_cursor; wl_signal_add(&server.seat->events.request_set_cursor, &server.request_cursor); + server.pointer_focus_change.notify = seat_pointer_focus_change; + wl_signal_add(&server.seat->pointer_state.events.focus_change, + &server.pointer_focus_change); server.request_set_selection.notify = seat_request_set_selection; wl_signal_add(&server.seat->events.request_set_selection, &server.request_set_selection); @@ -1069,6 +1085,7 @@ int main(int argc, char *argv[]) { wl_list_remove(&server.new_input.link); wl_list_remove(&server.request_cursor.link); + wl_list_remove(&server.pointer_focus_change.link); wl_list_remove(&server.request_set_selection.link); wl_list_remove(&server.new_output.link); From 51a78cb0ed30fb9193e940e77f25bdf13131fcee Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Sun, 27 Jul 2025 01:32:24 -0700 Subject: [PATCH 451/519] color_management_v1: set output color properties This reports the output properties according to the current image description. Firefox needs this to report HDR support to documents, at least. v2: Move abort() calls out of switch to eliminate default case. Rename functions so they don't use a wlr_ prefix like public functions do. Signed-off-by: Christopher Snowhill --- types/wlr_color_management_v1.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/types/wlr_color_management_v1.c b/types/wlr_color_management_v1.c index b84781f6e..3af0b51e8 100644 --- a/types/wlr_color_management_v1.c +++ b/types/wlr_color_management_v1.c @@ -76,6 +76,17 @@ static enum wlr_color_named_primaries named_primaries_to_wlr( } } +static enum wp_color_manager_v1_primaries named_primaries_from_wlr( + enum wlr_color_named_primaries primaries) { + switch (primaries) { + case WLR_COLOR_NAMED_PRIMARIES_SRGB: + return WP_COLOR_MANAGER_V1_PRIMARIES_SRGB; + case WLR_COLOR_NAMED_PRIMARIES_BT2020: + return WP_COLOR_MANAGER_V1_PRIMARIES_BT2020; + } + abort(); +} + static enum wlr_color_transfer_function transfer_function_to_wlr( enum wp_color_manager_v1_transfer_function tf) { switch (tf) { @@ -90,6 +101,19 @@ static enum wlr_color_transfer_function transfer_function_to_wlr( } } +static enum wp_color_manager_v1_transfer_function transfer_function_from_wlr( + enum wlr_color_transfer_function tf) { + switch (tf) { + case WLR_COLOR_TRANSFER_FUNCTION_SRGB: + return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB; + case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ: + return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ; + case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR: + return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR; + } + abort(); +} + static int32_t encode_cie1931_coord(float value) { return round(value * 1000 * 1000); } @@ -238,6 +262,11 @@ static void cm_output_handle_get_image_description(struct wl_client *client, .tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB, .primaries_named = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB, }; + const struct wlr_output_image_description *image_desc = cm_output->output->image_description; + if (image_desc != NULL) { + data.tf_named = transfer_function_from_wlr(image_desc->transfer_function); + data.primaries_named = named_primaries_from_wlr(image_desc->primaries); + } image_desc_create_ready(cm_output->manager, cm_output_resource, id, &data, true); } @@ -691,6 +720,7 @@ static void manager_handle_get_output(struct wl_client *client, } cm_output->manager = manager; + cm_output->output = output; uint32_t version = wl_resource_get_version(manager_resource); cm_output->resource = wl_resource_create(client, From c8b7600adc35a31fa86be1bebf1c9bf79246f08a Mon Sep 17 00:00:00 2001 From: rewine Date: Wed, 30 Jul 2025 18:36:07 +0800 Subject: [PATCH 452/519] wlr_ext_data_control_v1: Make all listeners private For more context, see: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4873 --- include/wlr/types/wlr_ext_data_control_v1.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/include/wlr/types/wlr_ext_data_control_v1.h b/include/wlr/types/wlr_ext_data_control_v1.h index e49dfb3f5..7cdc7e95b 100644 --- a/include/wlr/types/wlr_ext_data_control_v1.h +++ b/include/wlr/types/wlr_ext_data_control_v1.h @@ -21,7 +21,9 @@ struct wlr_ext_data_control_manager_v1 { struct wl_signal new_device; // wlr_ext_data_control_device_v1 } events; - struct wl_listener display_destroy; + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; }; struct wlr_ext_data_control_device_v1 { @@ -33,9 +35,11 @@ struct wlr_ext_data_control_device_v1 { struct wl_resource *selection_offer_resource; // current selection offer struct wl_resource *primary_selection_offer_resource; // current primary selection offer - struct wl_listener seat_destroy; - struct wl_listener seat_set_selection; - struct wl_listener seat_set_primary_selection; + struct { + struct wl_listener seat_destroy; + struct wl_listener seat_set_selection; + struct wl_listener seat_set_primary_selection; + } WLR_PRIVATE; }; struct wlr_ext_data_control_manager_v1 *wlr_ext_data_control_manager_v1_create( From dd3c63f5e6b411ed59efeb55f88f67cc6242a70a Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Thu, 31 Jul 2025 06:46:47 -0700 Subject: [PATCH 453/519] color-representation-v1: Fix missing destroy signal init Fixes #4001 Reported-by: CreeperFace / @dy-tea Signed-off-by: Christopher Snowhill --- types/wlr_color_representation_v1.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/types/wlr_color_representation_v1.c b/types/wlr_color_representation_v1.c index dbf2ecd52..ac804c60a 100644 --- a/types/wlr_color_representation_v1.c +++ b/types/wlr_color_representation_v1.c @@ -384,6 +384,8 @@ struct wlr_color_representation_manager_v1 *wlr_color_representation_manager_v1_ goto err_options; } + wl_signal_init(&manager->events.destroy); + manager->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &manager->display_destroy); From 12316417b033465114705430105785e72dfe345d Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 31 Jul 2025 15:21:28 +0200 Subject: [PATCH 454/519] ext_image_capture_source_v1: advertise fallback {A,X}RGB8888 formats We can't expect all clients to support all fancy formats. WebRTC's reference implementation doesn't support 10-bit formats yet. More generally, clients are limited by the libraries they use: for instance, Pixman doesn't implement all OpenGL/Vulkan formats. Another MR [1] suggests advertising all render formats. This is a bit heavy-handed because: - Upgrading a 8-bit buffer to a 10-bit buffer doesn't make a lot of sense. I don't think the compositor should expose arbitrary pixel format conversions. - The protocol has no preference order. Clients generally pick the first format they receive and support. As an alternative, only advertise two fallback formats, ARGB8888 and XRGB8888. These two are already hard-required by wl_shm and all clients should be able to handle them. [1]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5019 --- types/ext_image_capture_source_v1/base.c | 25 +++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/types/ext_image_capture_source_v1/base.c b/types/ext_image_capture_source_v1/base.c index 7b34030c8..eccbb2884 100644 --- a/types/ext_image_capture_source_v1/base.c +++ b/types/ext_image_capture_source_v1/base.c @@ -10,6 +10,7 @@ #include #include #include "ext-image-capture-source-v1-protocol.h" +#include "render/wlr_renderer.h" static void source_handle_destroy(struct wl_client *client, struct wl_resource *source_resource) { @@ -96,6 +97,12 @@ static uint32_t get_swapchain_shm_format(struct wlr_swapchain *swapchain, return format; } +static void add_drm_format(struct wlr_drm_format_set *set, const struct wlr_drm_format *fmt) { + for (size_t i = 0; i < fmt->len; i++) { + wlr_drm_format_set_add(set, fmt->format, fmt->modifiers[i]); + } +} + bool wlr_ext_image_capture_source_v1_set_constraints_from_swapchain(struct wlr_ext_image_capture_source_v1 *source, struct wlr_swapchain *swapchain, struct wlr_renderer *renderer) { source->width = swapchain->width; @@ -130,9 +137,21 @@ bool wlr_ext_image_capture_source_v1_set_constraints_from_swapchain(struct wlr_e wlr_drm_format_set_finish(&source->dmabuf_formats); source->dmabuf_formats = (struct wlr_drm_format_set){0}; - for (size_t i = 0; i < swapchain->format.len; i++) { - wlr_drm_format_set_add(&source->dmabuf_formats, - swapchain->format.format, swapchain->format.modifiers[i]); + add_drm_format(&source->dmabuf_formats, &swapchain->format); + + const struct wlr_drm_format_set *render_formats = + wlr_renderer_get_render_formats(renderer); + assert(render_formats != NULL); + + // Not all clients support fancy formats. Always ensure we provide + // support for ARGB8888 and XRGB8888 for simple clients. + uint32_t fallback_formats[] = { DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB8888 }; + for (size_t i = 0; i < sizeof(fallback_formats) / sizeof(fallback_formats[0]); i++) { + const struct wlr_drm_format *fmt = + wlr_drm_format_set_get(render_formats, fallback_formats[i]); + if (fmt != NULL && swapchain->format.format != fmt->format) { + add_drm_format(&source->dmabuf_formats, fmt); + } } } From 07e92fb86816783acb5a08d628b961398216ab8e Mon Sep 17 00:00:00 2001 From: Jesper Jensen Date: Tue, 5 Aug 2025 16:16:50 +0200 Subject: [PATCH 455/519] output/cursor: Fix double cursor bug When we fail to render the cursor (in my case because the cursor is too large) we bail out of the output_cursor_attempt_hardware function. This causes output_cursor_set_texture to clean up after us, but we've already cleared the hardware_cursor, and so output_disable_hardware_cursor thinks we don't have a hardware cursor to disable. We shouldn't modify the hardware_cursor variable before we've successfully changed the hardware cursor, this way the caller can clean up after us like it expect to. This was brought up by an actual bug when playing the game Kaizen. Which uses oddly sized cursors, that fell back to software cursors for me, and left the hardware cursor hanging around. This change has been tested to fix that. During the testing of this change, I have noticed that the previous code worked fine the first time the cursor was switch to software. It only failed on subsequent attempts. I haven't figured out why that is. --- types/output/cursor.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/types/output/cursor.c b/types/output/cursor.c index 154b91120..b3ec152ce 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -298,8 +298,6 @@ static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) { return false; } - output->hardware_cursor = NULL; - struct wlr_texture *texture = cursor->texture; // If the cursor was hidden or was a software cursor, the hardware From 7392b3313a7b483c61f4fea648ba8f2aa4ce8798 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 12 Aug 2025 19:00:11 +0200 Subject: [PATCH 456/519] backend, output: send commit events after applying all in wlr_backend_commit() We were iterating over involved outputs, applying the new state and sending the commit event for each one. This resulted in commit events being fired while we weren't done applying the new state for all outputs. Fix this by first applying all of the states, then firing all of the events. Closes: https://github.com/swaywm/sway/issues/8829 --- backend/backend.c | 5 +++++ include/types/wlr_output.h | 1 + types/output/output.c | 3 +++ 3 files changed, 9 insertions(+) diff --git a/backend/backend.c b/backend/backend.c index a130d9045..3d84aa636 100644 --- a/backend/backend.c +++ b/backend/backend.c @@ -485,5 +485,10 @@ bool wlr_backend_commit(struct wlr_backend *backend, output_apply_commit(state->output, &state->base); } + for (size_t i = 0; i < states_len; i++) { + const struct wlr_backend_output_state *state = &states[i]; + output_send_commit_event(state->output, &state->base); + } + return true; } diff --git a/include/types/wlr_output.h b/include/types/wlr_output.h index f901505af..d59b05f0b 100644 --- a/include/types/wlr_output.h +++ b/include/types/wlr_output.h @@ -27,6 +27,7 @@ void output_defer_present(struct wlr_output *output, struct wlr_output_event_pre bool output_prepare_commit(struct wlr_output *output, const struct wlr_output_state *state); void output_apply_commit(struct wlr_output *output, const struct wlr_output_state *state); +void output_send_commit_event(struct wlr_output *output, const struct wlr_output_state *state); void output_state_get_buffer_src_box(const struct wlr_output_state *state, struct wlr_fbox *out); diff --git a/types/output/output.c b/types/output/output.c index 636d155d2..1fb0347a0 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -759,7 +759,9 @@ void output_apply_commit(struct wlr_output *output, const struct wlr_output_stat } output_apply_state(output, state); +} +void output_send_commit_event(struct wlr_output *output, const struct wlr_output_state *state) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); struct wlr_output_event_commit event = { @@ -801,6 +803,7 @@ bool wlr_output_commit_state(struct wlr_output *output, } output_apply_commit(output, &pending); + output_send_commit_event(output, &pending); if (new_back_buffer) { wlr_buffer_unlock(pending.buffer); From 812675ba34ce612e9294e8a9814b1baf4b4775d4 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Sun, 12 May 2024 11:53:09 +0300 Subject: [PATCH 457/519] fixes: add implementation --- include/wlr/types/wlr_fixes.h | 28 +++++++++++++++ meson.build | 2 +- types/meson.build | 11 +++--- types/wlr_fixes.c | 65 +++++++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+), 6 deletions(-) create mode 100644 include/wlr/types/wlr_fixes.h create mode 100644 types/wlr_fixes.c diff --git a/include/wlr/types/wlr_fixes.h b/include/wlr/types/wlr_fixes.h new file mode 100644 index 000000000..b227f7e28 --- /dev/null +++ b/include/wlr/types/wlr_fixes.h @@ -0,0 +1,28 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_TYPES_WLR_FIXES_H +#define WLR_TYPES_WLR_FIXES_H + +#include + +struct wlr_fixes { + struct wl_global *global; + + struct { + struct wl_signal destroy; + } events; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; +}; + +struct wlr_fixes *wlr_fixes_create(struct wl_display *display, uint32_t version); + +#endif diff --git a/meson.build b/meson.build index 8319eff69..4b24e5046 100644 --- a/meson.build +++ b/meson.build @@ -86,7 +86,7 @@ internal_features = { internal_config = configuration_data() wayland_kwargs = { - 'version': '>=1.23.1', + 'version': '>=1.24.0', 'fallback': 'wayland', 'default_options': [ 'tests=false', diff --git a/types/meson.build b/types/meson.build index 25a0d4434..402fd3e11 100644 --- a/types/meson.build +++ b/types/meson.build @@ -39,19 +39,20 @@ wlr_files += files( 'buffer/resource.c', 'wlr_alpha_modifier_v1.c', 'wlr_color_management_v1.c', + 'wlr_color_representation_v1.c', 'wlr_compositor.c', 'wlr_content_type_v1.c', - 'wlr_cursor_shape_v1.c', 'wlr_cursor.c', + 'wlr_cursor_shape_v1.c', 'wlr_damage_ring.c', 'wlr_data_control_v1.c', 'wlr_drm.c', 'wlr_export_dmabuf_v1.c', - 'wlr_foreign_toplevel_management_v1.c', - 'wlr_color_representation_v1.c', - 'wlr_ext_image_copy_capture_v1.c', - 'wlr_ext_foreign_toplevel_list_v1.c', 'wlr_ext_data_control_v1.c', + 'wlr_ext_foreign_toplevel_list_v1.c', + 'wlr_ext_image_copy_capture_v1.c', + 'wlr_fixes.c', + 'wlr_foreign_toplevel_management_v1.c', 'wlr_fractional_scale_v1.c', 'wlr_gamma_control_v1.c', 'wlr_idle_inhibit_v1.c', diff --git a/types/wlr_fixes.c b/types/wlr_fixes.c new file mode 100644 index 000000000..b5435aaf8 --- /dev/null +++ b/types/wlr_fixes.c @@ -0,0 +1,65 @@ +#include +#include +#include + +#include + +#define FIXES_VERSION 1 + +static void fixes_destroy(struct wl_client *client, struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static void fixes_destroy_registry(struct wl_client *client, struct wl_resource *resource, + struct wl_resource *registry) { + wl_resource_destroy(registry); +} + +static const struct wl_fixes_interface fixes_impl = { + .destroy = fixes_destroy, + .destroy_registry = fixes_destroy_registry, +}; + +static void fixes_bind(struct wl_client *wl_client, void *data, uint32_t version, uint32_t id) { + struct wlr_fixes *fixes = data; + + struct wl_resource *resource = wl_resource_create(wl_client, &wl_fixes_interface, version, id); + if (resource == NULL) { + wl_client_post_no_memory(wl_client); + return; + } + wl_resource_set_implementation(resource, &fixes_impl, fixes, NULL); +} + +static void fixes_handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_fixes *fixes = wl_container_of(listener, fixes, display_destroy); + wl_signal_emit_mutable(&fixes->events.destroy, NULL); + + assert(wl_list_empty(&fixes->events.destroy.listener_list)); + + wl_list_remove(&fixes->display_destroy.link); + wl_global_destroy(fixes->global); + free(fixes); +} + +struct wlr_fixes *wlr_fixes_create(struct wl_display *display, uint32_t version) { + assert(version <= FIXES_VERSION); + + struct wlr_fixes *fixes = calloc(1, sizeof(*fixes)); + if (fixes == NULL) { + return NULL; + } + + fixes->global = wl_global_create(display, &wl_fixes_interface, version, fixes, fixes_bind); + if (fixes->global == NULL) { + free(fixes); + return NULL; + } + + wl_signal_init(&fixes->events.destroy); + + fixes->display_destroy.notify = fixes_handle_display_destroy; + wl_display_add_destroy_listener(display, &fixes->display_destroy); + + return fixes; +} From ad1b2f28199f41cfa5218eb1456500716801894f Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Tue, 20 May 2025 21:52:17 +0300 Subject: [PATCH 458/519] Avoid including generated headers publicly where possible This is possible now that w-p ships enum headers. The remaining includes are from wlr-protocols. --- include/wlr/types/wlr_color_management_v1.h | 6 +++--- include/wlr/types/wlr_color_representation_v1.h | 3 +-- include/wlr/types/wlr_content_type_v1.h | 2 +- include/wlr/types/wlr_cursor_shape_v1.h | 2 +- include/wlr/types/wlr_ext_image_copy_capture_v1.h | 4 ++-- include/wlr/types/wlr_pointer_constraints_v1.h | 2 +- include/wlr/types/wlr_tablet_v2.h | 3 +-- include/wlr/types/wlr_tearing_control_v1.h | 4 +--- include/wlr/types/wlr_xdg_shell.h | 2 +- types/wlr_color_management_v1.c | 1 + types/wlr_content_type_v1.c | 2 ++ types/wlr_cursor_shape_v1.c | 2 ++ types/wlr_ext_image_copy_capture_v1.c | 1 + types/wlr_pointer_constraints_v1.c | 2 ++ 14 files changed, 20 insertions(+), 16 deletions(-) diff --git a/include/wlr/types/wlr_color_management_v1.h b/include/wlr/types/wlr_color_management_v1.h index f369fb22c..4a50e94e3 100644 --- a/include/wlr/types/wlr_color_management_v1.h +++ b/include/wlr/types/wlr_color_management_v1.h @@ -9,10 +9,10 @@ #ifndef WLR_TYPES_WLR_COLOR_MANAGEMENT_V1_H #define WLR_TYPES_WLR_COLOR_MANAGEMENT_V1_H -#include -#include +#include +#include -#include "color-management-v1-protocol.h" +#include struct wlr_surface; diff --git a/include/wlr/types/wlr_color_representation_v1.h b/include/wlr/types/wlr_color_representation_v1.h index d575eedf5..0a1958dec 100644 --- a/include/wlr/types/wlr_color_representation_v1.h +++ b/include/wlr/types/wlr_color_representation_v1.h @@ -10,10 +10,9 @@ #define WLR_TYPES_WLR_COLOR_REPRESENTATION_V1_H #include +#include #include -#include "color-representation-v1-protocol.h" - struct wlr_surface; // Supported coefficients and range are always paired together diff --git a/include/wlr/types/wlr_content_type_v1.h b/include/wlr/types/wlr_content_type_v1.h index 31c51ac4c..1bea899eb 100644 --- a/include/wlr/types/wlr_content_type_v1.h +++ b/include/wlr/types/wlr_content_type_v1.h @@ -10,7 +10,7 @@ #define WLR_TYPES_WLR_CONTENT_TYPE_V1_H #include -#include "content-type-v1-protocol.h" +#include struct wlr_surface; diff --git a/include/wlr/types/wlr_cursor_shape_v1.h b/include/wlr/types/wlr_cursor_shape_v1.h index 26048a52e..d0a21aeb9 100644 --- a/include/wlr/types/wlr_cursor_shape_v1.h +++ b/include/wlr/types/wlr_cursor_shape_v1.h @@ -10,7 +10,7 @@ #define WLR_TYPES_WLR_CURSOR_SHAPE_V1_H #include -#include "cursor-shape-v1-protocol.h" +#include /** * Manager for the cursor-shape-v1 protocol. diff --git a/include/wlr/types/wlr_ext_image_copy_capture_v1.h b/include/wlr/types/wlr_ext_image_copy_capture_v1.h index 51c4af8c4..9b1ab3df1 100644 --- a/include/wlr/types/wlr_ext_image_copy_capture_v1.h +++ b/include/wlr/types/wlr_ext_image_copy_capture_v1.h @@ -10,9 +10,9 @@ #define WLR_TYPES_WLR_EXT_IMAGE_COPY_CAPTURE_V1_H #include -#include +#include +#include #include -#include "ext-image-copy-capture-v1-protocol.h" struct wlr_renderer; diff --git a/include/wlr/types/wlr_pointer_constraints_v1.h b/include/wlr/types/wlr_pointer_constraints_v1.h index bccb23090..0d74ce56d 100644 --- a/include/wlr/types/wlr_pointer_constraints_v1.h +++ b/include/wlr/types/wlr_pointer_constraints_v1.h @@ -11,10 +11,10 @@ #include #include +#include #include #include #include -#include "pointer-constraints-unstable-v1-protocol.h" struct wlr_seat; diff --git a/include/wlr/types/wlr_tablet_v2.h b/include/wlr/types/wlr_tablet_v2.h index dc911f101..599e88019 100644 --- a/include/wlr/types/wlr_tablet_v2.h +++ b/include/wlr/types/wlr_tablet_v2.h @@ -10,10 +10,9 @@ #define WLR_TYPES_WLR_TABLET_V2_H #include +#include #include -#include "tablet-v2-protocol.h" - /* This can probably be even lower,the tools don't have a lot of buttons */ #define WLR_TABLET_V2_TOOL_BUTTONS_CAP 16 diff --git a/include/wlr/types/wlr_tearing_control_v1.h b/include/wlr/types/wlr_tearing_control_v1.h index ff981e414..f2d4a1478 100644 --- a/include/wlr/types/wlr_tearing_control_v1.h +++ b/include/wlr/types/wlr_tearing_control_v1.h @@ -11,11 +11,9 @@ #include #include -#include +#include #include -#include "tearing-control-v1-protocol.h" - struct wlr_tearing_control_v1 { struct wl_client *client; struct wl_list link; diff --git a/include/wlr/types/wlr_xdg_shell.h b/include/wlr/types/wlr_xdg_shell.h index a835bb107..517d77442 100644 --- a/include/wlr/types/wlr_xdg_shell.h +++ b/include/wlr/types/wlr_xdg_shell.h @@ -10,10 +10,10 @@ #define WLR_TYPES_WLR_XDG_SHELL_H #include +#include #include #include #include -#include "xdg-shell-protocol.h" struct wlr_xdg_shell { struct wl_global *global; diff --git a/types/wlr_color_management_v1.c b/types/wlr_color_management_v1.c index 3af0b51e8..75da825cb 100644 --- a/types/wlr_color_management_v1.c +++ b/types/wlr_color_management_v1.c @@ -7,6 +7,7 @@ #include #include +#include "color-management-v1-protocol.h" #include "render/color.h" #include "util/mem.h" diff --git a/types/wlr_content_type_v1.c b/types/wlr_content_type_v1.c index 0c59859df..d3302aef1 100644 --- a/types/wlr_content_type_v1.c +++ b/types/wlr_content_type_v1.c @@ -3,6 +3,8 @@ #include #include +#include "content-type-v1-protocol.h" + #define CONTENT_TYPE_VERSION 1 struct wlr_content_type_v1_surface { diff --git a/types/wlr_cursor_shape_v1.c b/types/wlr_cursor_shape_v1.c index b8ae07136..563b326e0 100644 --- a/types/wlr_cursor_shape_v1.c +++ b/types/wlr_cursor_shape_v1.c @@ -5,6 +5,8 @@ #include #include #include + +#include "cursor-shape-v1-protocol.h" #include "types/wlr_tablet_v2.h" #define CURSOR_SHAPE_MANAGER_V1_VERSION 2 diff --git a/types/wlr_ext_image_copy_capture_v1.c b/types/wlr_ext_image_copy_capture_v1.c index 6f5556139..c4cfd743e 100644 --- a/types/wlr_ext_image_copy_capture_v1.c +++ b/types/wlr_ext_image_copy_capture_v1.c @@ -6,6 +6,7 @@ #include #include #include +#include "ext-image-copy-capture-v1-protocol.h" #include "render/pixel_format.h" #define IMAGE_COPY_CAPTURE_MANAGER_V1_VERSION 1 diff --git a/types/wlr_pointer_constraints_v1.c b/types/wlr_pointer_constraints_v1.c index 6bc5efc1b..51a2304d8 100644 --- a/types/wlr_pointer_constraints_v1.c +++ b/types/wlr_pointer_constraints_v1.c @@ -9,6 +9,8 @@ #include #include +#include "pointer-constraints-unstable-v1-protocol.h" + static const struct zwp_locked_pointer_v1_interface locked_pointer_impl; static const struct zwp_confined_pointer_v1_interface confined_pointer_impl; static const struct zwp_pointer_constraints_v1_interface pointer_constraints_impl; From bb1f8673b3a4883b8c0f3185a6ec3bbe410d9307 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Fri, 9 Aug 2024 23:33:14 +0300 Subject: [PATCH 459/519] compositor: use wl_resource_post_error_vargs() --- types/wlr_compositor.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/types/wlr_compositor.c b/types/wlr_compositor.c index d2c881e2a..6b31ab857 100644 --- a/types/wlr_compositor.c +++ b/types/wlr_compositor.c @@ -882,11 +882,7 @@ void wlr_surface_reject_pending(struct wlr_surface *surface, struct wl_resource va_list args; va_start(args, msg); - // XXX: libwayland could expose wl_resource_post_error_vargs() instead - char buffer[128]; // Matches the size of the buffer used in libwayland - vsnprintf(buffer, sizeof(buffer), msg, args); - - wl_resource_post_error(resource, code, "%s", buffer); + wl_resource_post_error_vargs(resource, code, msg, args); surface->pending_rejected = true; va_end(args); From 7431d840d0c29dd90b21c87698b39769e4f2f004 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 18 Aug 2025 14:09:13 +0200 Subject: [PATCH 460/519] color-management-v1: handle inert outputs in get_output wlr_output_from_resource() can return NULL if the outputs no longer exists on the compositor side. Closes: https://github.com/swaywm/sway/issues/8847 --- types/wlr_color_management_v1.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/types/wlr_color_management_v1.c b/types/wlr_color_management_v1.c index 75da825cb..55faaacaa 100644 --- a/types/wlr_color_management_v1.c +++ b/types/wlr_color_management_v1.c @@ -714,30 +714,35 @@ static void manager_handle_get_output(struct wl_client *client, struct wlr_color_manager_v1 *manager = manager_from_resource(manager_resource); struct wlr_output *output = wlr_output_from_resource(output_resource); + uint32_t version = wl_resource_get_version(manager_resource); + struct wl_resource *cm_output_resource = wl_resource_create(client, + &wp_color_management_output_v1_interface, version, id); + if (!cm_output_resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(cm_output_resource, &cm_output_impl, + NULL, cm_output_handle_resource_destroy); + + if (output == NULL) { + return; // leave the wp_color_management_output_v1 resource inert + } + struct wlr_color_management_output_v1 *cm_output = calloc(1, sizeof(*cm_output)); if (cm_output == NULL) { wl_client_post_no_memory(client); return; } + cm_output->resource = cm_output_resource; cm_output->manager = manager; cm_output->output = output; - uint32_t version = wl_resource_get_version(manager_resource); - cm_output->resource = wl_resource_create(client, - &wp_color_management_output_v1_interface, version, id); - if (!cm_output->resource) { - wl_client_post_no_memory(client); - free(cm_output); - return; - } - wl_resource_set_implementation(cm_output->resource, &cm_output_impl, - cm_output, cm_output_handle_resource_destroy); - cm_output->output_destroy.notify = cm_output_handle_output_destroy; wl_signal_add(&output->events.destroy, &cm_output->output_destroy); wl_list_insert(&manager->outputs, &cm_output->link); + wl_resource_set_user_data(cm_output->resource, cm_output); } static struct wlr_color_management_surface_v1 *cm_surface_from_surface(struct wlr_surface *surface) { From b0c886ec77dba9486beae90f4007c38987f3d4f8 Mon Sep 17 00:00:00 2001 From: xurui Date: Mon, 25 Aug 2025 13:58:03 +0800 Subject: [PATCH 461/519] render/allocator/gbm: insert buffer after export gbm bo Signed-off-by: xurui --- render/allocator/gbm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/render/allocator/gbm.c b/render/allocator/gbm.c index ca16177d1..fadfac9f8 100644 --- a/render/allocator/gbm.c +++ b/render/allocator/gbm.c @@ -97,7 +97,6 @@ static struct wlr_gbm_buffer *create_buffer(struct wlr_gbm_allocator *alloc, } wlr_buffer_init(&buffer->base, &buffer_impl, width, height); buffer->gbm_bo = bo; - wl_list_insert(&alloc->buffers, &buffer->link); if (!export_gbm_bo(bo, &buffer->dmabuf)) { free(buffer); @@ -112,6 +111,8 @@ static struct wlr_gbm_buffer *create_buffer(struct wlr_gbm_allocator *alloc, buffer->dmabuf.modifier = fallback_modifier; } + wl_list_insert(&alloc->buffers, &buffer->link); + char *format_name = drmGetFormatName(buffer->dmabuf.format); char *modifier_name = drmGetFormatModifierName(buffer->dmabuf.modifier); wlr_log(WLR_DEBUG, "Allocated %dx%d GBM buffer " From 7bf5ff4c0286e43fc0a40b79ce8fe3f80e52ace7 Mon Sep 17 00:00:00 2001 From: xurui Date: Wed, 27 Aug 2025 20:01:50 +0800 Subject: [PATCH 462/519] wlr_xdg_toplevel_icon_v1: check the correct resource Signed-off-by: xurui --- types/wlr_xdg_toplevel_icon_v1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/wlr_xdg_toplevel_icon_v1.c b/types/wlr_xdg_toplevel_icon_v1.c index 5927b8168..e67ad5378 100644 --- a/types/wlr_xdg_toplevel_icon_v1.c +++ b/types/wlr_xdg_toplevel_icon_v1.c @@ -153,7 +153,7 @@ static void manager_handle_create_icon(struct wl_client *client, struct wl_resou struct wl_resource *icon_resource = wl_resource_create(client, &xdg_toplevel_icon_v1_interface, wl_resource_get_version(resource), id); - if (resource == NULL) { + if (icon_resource == NULL) { wl_client_post_no_memory(client); free(icon); return; From bbd9a49bdf1449886b8e8e12a96455a1e2228143 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 16 Aug 2025 19:21:17 +0200 Subject: [PATCH 463/519] tinywl: stop generating xdg-shell header We don't need to do this anymore for wayland-protocols. --- tinywl/Makefile | 13 ++----------- tinywl/meson.build | 2 +- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/tinywl/Makefile b/tinywl/Makefile index 70dc671ca..9c7af540e 100644 --- a/tinywl/Makefile +++ b/tinywl/Makefile @@ -1,6 +1,4 @@ PKG_CONFIG?=pkg-config -WAYLAND_PROTOCOLS!=$(PKG_CONFIG) --variable=pkgdatadir wayland-protocols -WAYLAND_SCANNER!=$(PKG_CONFIG) --variable=wayland_scanner wayland-scanner PKGS="wlroots-0.20" wayland-server xkbcommon CFLAGS_PKG_CONFIG!=$(PKG_CONFIG) --cflags $(PKGS) @@ -9,19 +7,12 @@ LIBS!=$(PKG_CONFIG) --libs $(PKGS) all: tinywl -# wayland-scanner is a tool which generates C headers and rigging for Wayland -# protocols, which are specified in XML. wlroots requires you to rig these up -# to your build system yourself and provide them in the include path. -xdg-shell-protocol.h: - $(WAYLAND_SCANNER) server-header \ - $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@ - -tinywl.o: tinywl.c xdg-shell-protocol.h +tinywl.o: tinywl.c $(CC) -c $< -g -Werror $(CFLAGS) -I. -DWLR_USE_UNSTABLE -o $@ tinywl: tinywl.o $(CC) $^ $> -g -Werror $(CFLAGS) $(LDFLAGS) $(LIBS) -o $@ clean: - rm -f tinywl tinywl.o xdg-shell-protocol.h + rm -f tinywl tinywl.o .PHONY: all clean diff --git a/tinywl/meson.build b/tinywl/meson.build index e7271458b..07b4a5e99 100644 --- a/tinywl/meson.build +++ b/tinywl/meson.build @@ -1,5 +1,5 @@ executable( 'tinywl', - ['tinywl.c', protocols_server_header['xdg-shell']], + 'tinywl.c', dependencies: wlroots, ) From 1a18e47efa7878d985c5522e1df2100f0430ddb1 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 16 Aug 2025 11:44:24 +0200 Subject: [PATCH 464/519] render/vulkan: fix VkPushConstantRange for wlr_vk_frag_texture_pcr_data We pass an alpha multiplier plus a luminance multiplier now. Fixes the following validation layer error: vkCmdPushConstants(): is called with stageFlags (VK_SHADER_STAGE_FRAGMENT_BIT), offset (80), size (72) but the VkPipelineLayout 0x510000000051 doesn't have a VkPushConstantRange with VK_SHADER_STAGE_FRAGMENT_BIT. The Vulkan spec states: For each byte in the range specified by offset and size and for each shader stage in stageFlags, there must be a push constant range in layout that includes that byte and that stage (https://docs.vulkan.org/spec/latest/chapters/descriptorsets.html#VUID-vkCmdPushConstants-offset-01795) (VUID-vkCmdPushConstants-offset-01795) Fixes: 56d95c2ecb2f ("render/vulkan: introduce wlr_vk_frag_texture_pcr_data") --- render/vulkan/renderer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index 266ac61cf..6a166a546 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -1473,7 +1473,7 @@ static bool init_tex_layouts(struct wlr_vk_renderer *renderer, }, { .offset = pc_ranges[0].size, - .size = sizeof(float) * 4, // alpha or color + .size = sizeof(struct wlr_vk_frag_texture_pcr_data), .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, }, }; From e95117b700f2079493a2cd2eb4b8883b431adc94 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 16 Aug 2025 11:53:12 +0200 Subject: [PATCH 465/519] render/vulkan: remove hardcoded counts Use the array size instead. --- render/vulkan/renderer.c | 50 ++++++++++++++++++++-------------------- render/vulkan/texture.c | 8 +++---- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index 6a166a546..08d99bb7c 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -771,13 +771,13 @@ bool vulkan_setup_plain_framebuffer(struct wlr_vk_render_buffer *buffer, }; vkUpdateDescriptorSets(dev, 1, &ds_write, 0, NULL); - VkImageView attachments[2] = { + VkImageView attachments[] = { buffer->plain.blend_image_view, buffer->plain.image_view }; VkFramebufferCreateInfo fb_info = { .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, - .attachmentCount = 2, + .attachmentCount = sizeof(attachments) / sizeof(attachments[0]), .pAttachments = attachments, .flags = 0u, .width = dmabuf->width, @@ -1466,7 +1466,7 @@ static bool init_tex_layouts(struct wlr_vk_renderer *renderer, return false; } - VkPushConstantRange pc_ranges[2] = { + VkPushConstantRange pc_ranges[] = { { .size = sizeof(struct wlr_vk_vert_pcr_data), .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, @@ -1482,7 +1482,7 @@ static bool init_tex_layouts(struct wlr_vk_renderer *renderer, .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, .setLayoutCount = 1, .pSetLayouts = out_ds_layout, - .pushConstantRangeCount = 2, + .pushConstantRangeCount = sizeof(pc_ranges) / sizeof(pc_ranges[0]), .pPushConstantRanges = pc_ranges, }; @@ -1560,7 +1560,7 @@ static bool init_blend_to_output_layouts(struct wlr_vk_renderer *renderer) { } // pipeline layout -- standard vertex uniforms, no shader uniforms - VkPushConstantRange pc_ranges[2] = { + VkPushConstantRange pc_ranges[] = { { .offset = 0, .size = sizeof(struct wlr_vk_vert_pcr_data), @@ -1573,16 +1573,16 @@ static bool init_blend_to_output_layouts(struct wlr_vk_renderer *renderer) { }, }; - VkDescriptorSetLayout out_ds_layouts[2] = { + VkDescriptorSetLayout out_ds_layouts[] = { renderer->output_ds_srgb_layout, renderer->output_ds_lut3d_layout, }; VkPipelineLayoutCreateInfo pl_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, - .setLayoutCount = 2, + .setLayoutCount = sizeof(out_ds_layouts) / sizeof(out_ds_layouts[0]), .pSetLayouts = out_ds_layouts, - .pushConstantRangeCount = 2, + .pushConstantRangeCount = sizeof(pc_ranges) / sizeof(pc_ranges[0]), .pPushConstantRanges = pc_ranges, }; @@ -1755,14 +1755,14 @@ struct wlr_vk_pipeline *setup_get_or_create_pipeline( .scissorCount = 1, }; - VkDynamicState dynStates[2] = { + VkDynamicState dyn_states[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, }; VkPipelineDynamicStateCreateInfo dynamic = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, - .pDynamicStates = dynStates, - .dynamicStateCount = 2, + .pDynamicStates = dyn_states, + .dynamicStateCount = sizeof(dyn_states) / sizeof(dyn_states[0]), }; VkPipelineVertexInputStateCreateInfo vertex = { @@ -1774,7 +1774,7 @@ struct wlr_vk_pipeline *setup_get_or_create_pipeline( .layout = pipeline_layout->vk, .renderPass = setup->render_pass, .subpass = 0, - .stageCount = 2, + .stageCount = sizeof(stages) / sizeof(stages[0]), .pStages = stages, .pInputAssemblyState = &assembly, @@ -1817,7 +1817,7 @@ static bool init_blend_to_output_pipeline(struct wlr_vk_renderer *renderer, .pData = &output_transform_type, }; - VkPipelineShaderStageCreateInfo tex_stages[2] = { + VkPipelineShaderStageCreateInfo tex_stages[] = { { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .stage = VK_SHADER_STAGE_VERTEX_BIT, @@ -1872,14 +1872,14 @@ static bool init_blend_to_output_pipeline(struct wlr_vk_renderer *renderer, .scissorCount = 1, }; - VkDynamicState dynStates[2] = { + VkDynamicState dyn_states[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, }; VkPipelineDynamicStateCreateInfo dynamic = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, - .pDynamicStates = dynStates, - .dynamicStateCount = 2, + .pDynamicStates = dyn_states, + .dynamicStateCount = sizeof(dyn_states) / sizeof(dyn_states[0]), }; VkPipelineVertexInputStateCreateInfo vertex = { @@ -1892,7 +1892,7 @@ static bool init_blend_to_output_pipeline(struct wlr_vk_renderer *renderer, .layout = pipe_layout, .renderPass = rp, .subpass = 1, // second subpass! - .stageCount = 2, + .stageCount = sizeof(tex_stages) / sizeof(tex_stages[0]), .pStages = tex_stages, .pInputAssemblyState = &assembly, .pRasterizationState = &rasterization, @@ -2185,7 +2185,7 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup( VkResult res; if (use_blending_buffer) { - VkAttachmentDescription attachments[2] = { + VkAttachmentDescription attachments[] = { { .format = VK_FORMAT_R16G16B16A16_SFLOAT, .samples = VK_SAMPLE_COUNT_1_BIT, @@ -2223,7 +2223,7 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup( .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, }; - VkSubpassDescription subpasses[2] = { + VkSubpassDescription subpasses[] = { { .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, .colorAttachmentCount = 1, @@ -2238,7 +2238,7 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup( } }; - VkSubpassDependency deps[3] = { + VkSubpassDependency deps[] = { { .srcSubpass = VK_SUBPASS_EXTERNAL, .srcStageMask = VK_PIPELINE_STAGE_HOST_BIT | @@ -2280,11 +2280,11 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup( .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, .pNext = NULL, .flags = 0, - .attachmentCount = 2u, + .attachmentCount = sizeof(attachments) / sizeof(attachments[0]), .pAttachments = attachments, - .subpassCount = 2u, + .subpassCount = sizeof(subpasses) / sizeof(subpasses[0]), .pSubpasses = subpasses, - .dependencyCount = 3u, + .dependencyCount = sizeof(deps) / sizeof(deps[0]), .pDependencies = deps, }; @@ -2339,7 +2339,7 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup( .pColorAttachments = &color_ref, }; - VkSubpassDependency deps[2] = { + VkSubpassDependency deps[] = { { .srcSubpass = VK_SUBPASS_EXTERNAL, .srcStageMask = VK_PIPELINE_STAGE_HOST_BIT | @@ -2374,7 +2374,7 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup( .pAttachments = &attachment, .subpassCount = 1, .pSubpasses = &subpass, - .dependencyCount = 2u, + .dependencyCount = sizeof(deps) / sizeof(deps[0]), .pDependencies = deps, }; diff --git a/render/vulkan/texture.c b/render/vulkan/texture.c index 2b21d458c..499178f5d 100644 --- a/render/vulkan/texture.c +++ b/render/vulkan/texture.c @@ -399,14 +399,14 @@ static struct wlr_texture *vulkan_texture_from_pixels( texture_set_format(texture, &fmt->format, fmt->shm.has_mutable_srgb); - VkFormat view_formats[2] = { + VkFormat view_formats[] = { fmt->format.vk, fmt->format.vk_srgb, }; VkImageFormatListCreateInfoKHR list_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR, .pViewFormats = view_formats, - .viewFormatCount = 2, + .viewFormatCount = sizeof(view_formats) / sizeof(view_formats[0]), }; VkImageCreateInfo img_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, @@ -600,14 +600,14 @@ VkImage vulkan_import_dmabuf(struct wlr_vk_renderer *renderer, }; eimg.pNext = &mod_info; - VkFormat view_formats[2] = { + VkFormat view_formats[] = { fmt->format.vk, fmt->format.vk_srgb, }; VkImageFormatListCreateInfoKHR list_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR, .pViewFormats = view_formats, - .viewFormatCount = 2, + .viewFormatCount = sizeof(view_formats) / sizeof(view_formats[0]), }; if (mod->has_mutable_srgb) { mod_info.pNext = &list_info; From b799ffc6aee6466b4e735b34bcd1d5cb8be476b1 Mon Sep 17 00:00:00 2001 From: rewine Date: Wed, 30 Jul 2025 18:25:22 +0800 Subject: [PATCH 466/519] docs: deprecate legacy wlr_data_control_v1 interface Add deprecation notice for wlr_data_control_v1, indicating that it's superseded by ext-data-control-v1. Related: https://gitlab.freedesktop.org/wlroots/wlr-protocols/-/merge_requests/136 --- include/wlr/types/wlr_data_control_v1.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/wlr/types/wlr_data_control_v1.h b/include/wlr/types/wlr_data_control_v1.h index 9ef86d7cf..4dd642bfe 100644 --- a/include/wlr/types/wlr_data_control_v1.h +++ b/include/wlr/types/wlr_data_control_v1.h @@ -12,6 +12,12 @@ #include #include +/** + * Deprecated: this protocol is legacy and superseded by ext-data-control-v1. + * The implementation will be dropped in a future wlroots version. + * + * Consider using `wlr_ext_data_control_manager_v1` as a replacement. + */ struct wlr_data_control_manager_v1 { struct wl_global *global; struct wl_list devices; // wlr_data_control_device_v1.link From 122310a2de35a24b5c886112e8cca7db1cf84ade Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 16 Aug 2025 19:19:52 +0200 Subject: [PATCH 467/519] build: add wayland-protocols to dependencies array We grab header files from there, ensure include directories are properly set up when building wlroots. Fixes missing header files when a wayland-protocols subproject is used. --- protocol/meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/protocol/meson.build b/protocol/meson.build index 5012753b5..613d18018 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -4,6 +4,7 @@ wayland_protos = dependency('wayland-protocols', default_options: ['tests=false'], ) wl_protocol_dir = wayland_protos.get_variable('pkgdatadir') +wlr_deps += wayland_protos wayland_scanner_dep = dependency('wayland-scanner', kwargs: wayland_kwargs, From 423afc3fc97e03f7ec523cb4fa6621119378ae4b Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Wed, 27 Aug 2025 15:01:43 -0400 Subject: [PATCH 468/519] types: deprecate wlr-screencopy-unstable-v1 --- include/wlr/types/wlr_screencopy_v1.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/wlr/types/wlr_screencopy_v1.h b/include/wlr/types/wlr_screencopy_v1.h index 3db958ad1..4f53a603c 100644 --- a/include/wlr/types/wlr_screencopy_v1.h +++ b/include/wlr/types/wlr_screencopy_v1.h @@ -14,6 +14,13 @@ #include #include +/** + * Deprecated: this protocol is deprecated and superseded by ext-image-copy-capture-v1. + * The implementation will be dropped in a future wlroots version. + * + * Consider using `wlr_ext_image_capture_source_v1` instead. + */ + struct wlr_screencopy_manager_v1 { struct wl_global *global; struct wl_list frames; // wlr_screencopy_frame_v1.link From 0166fd9eb778761295ea14fdff0515ada1a1cb17 Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Fri, 4 Jul 2025 15:34:14 -0400 Subject: [PATCH 469/519] drm-lease-v1: remove connector active_lease & lease connectors Upon leasing, the wlr_drm_lease_connector_v1 will be automatically clean up by the wlr_output destroy handler. There is no need for the wlr_drm_lease_manager to keep track of leased connectors. --- include/wlr/types/wlr_drm_lease_v1.h | 5 ---- types/wlr_drm_lease_v1.c | 37 ---------------------------- 2 files changed, 42 deletions(-) diff --git a/include/wlr/types/wlr_drm_lease_v1.h b/include/wlr/types/wlr_drm_lease_v1.h index 752f6efa7..b29a5f6dc 100644 --- a/include/wlr/types/wlr_drm_lease_v1.h +++ b/include/wlr/types/wlr_drm_lease_v1.h @@ -62,8 +62,6 @@ struct wlr_drm_lease_connector_v1 { struct wlr_output *output; struct wlr_drm_lease_device_v1 *device; - /** NULL if no client is currently leasing this connector */ - struct wlr_drm_lease_v1 *active_lease; struct wl_list link; // wlr_drm_lease_device_v1.connectors @@ -93,9 +91,6 @@ struct wlr_drm_lease_v1 { struct wlr_drm_lease_device_v1 *device; - struct wlr_drm_lease_connector_v1 **connectors; - size_t n_connectors; - struct wl_list link; // wlr_drm_lease_device_v1.leases void *data; diff --git a/types/wlr_drm_lease_v1.c b/types/wlr_drm_lease_v1.c index c64bb0896..892a401b3 100644 --- a/types/wlr_drm_lease_v1.c +++ b/types/wlr_drm_lease_v1.c @@ -68,10 +68,6 @@ static void drm_lease_connector_v1_destroy( wlr_log(WLR_DEBUG, "Destroying connector %s", connector->output->name); - if (connector->active_lease) { - wlr_drm_lease_terminate(connector->active_lease->drm_lease); - } - struct wl_resource *resource, *tmp; wl_resource_for_each_safe(resource, tmp, &connector->resources) { wp_drm_lease_connector_v1_send_withdrawn(resource); @@ -140,14 +136,9 @@ static void lease_handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&lease->destroy.link); - for (size_t i = 0; i < lease->n_connectors; ++i) { - lease->connectors[i]->active_lease = NULL; - } - wl_list_remove(&lease->link); wl_resource_set_user_data(lease->resource, NULL); - free(lease->connectors); free(lease); } @@ -180,20 +171,6 @@ struct wlr_drm_lease_v1 *wlr_drm_lease_request_v1_grant( return NULL; } - lease->connectors = calloc(request->n_connectors, sizeof(*lease->connectors)); - if (!lease->connectors) { - wlr_log(WLR_ERROR, "Failed to allocate lease connectors list"); - close(fd); - wp_drm_lease_v1_send_finished(lease->resource); - free(lease); - return NULL; - } - lease->n_connectors = request->n_connectors; - for (size_t i = 0; i < request->n_connectors; ++i) { - lease->connectors[i] = request->connectors[i]; - lease->connectors[i]->active_lease = lease; - } - lease->destroy.notify = lease_handle_destroy; wl_signal_add(&lease->drm_lease->events.destroy, &lease->destroy); @@ -338,16 +315,6 @@ static void drm_lease_request_v1_handle_submit( return; } - for (size_t i = 0; i < request->n_connectors; ++i) { - struct wlr_drm_lease_connector_v1 *conn = request->connectors[i]; - if (conn->active_lease) { - wlr_log(WLR_ERROR, "Failed to create lease, connector %s has " - "already been leased", conn->output->name); - wp_drm_lease_v1_send_finished(lease_resource); - return; - } - } - request->lease_resource = lease_resource; wl_signal_emit_mutable(&request->device->manager->events.request, @@ -440,10 +407,6 @@ static struct wp_drm_lease_connector_v1_interface lease_connector_impl = { static void drm_lease_connector_v1_send_to_client( struct wlr_drm_lease_connector_v1 *connector, struct wl_resource *resource) { - if (connector->active_lease) { - return; - } - struct wl_client *client = wl_resource_get_client(resource); uint32_t version = wl_resource_get_version(resource); From 06aacb2a6fd237a5e1062d611909432bbcf5b566 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Mon, 7 Jul 2025 05:58:48 +0900 Subject: [PATCH 470/519] input-method: rename input_method event to new_input_method --- include/wlr/types/wlr_input_method_v2.h | 2 +- types/wlr_input_method_v2.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/wlr/types/wlr_input_method_v2.h b/include/wlr/types/wlr_input_method_v2.h index 77460b571..885b3fe80 100644 --- a/include/wlr/types/wlr_input_method_v2.h +++ b/include/wlr/types/wlr_input_method_v2.h @@ -94,7 +94,7 @@ struct wlr_input_method_manager_v2 { struct wl_list input_methods; // struct wlr_input_method_v2.link struct { - struct wl_signal input_method; // struct wlr_input_method_v2 + struct wl_signal new_input_method; // struct wlr_input_method_v2 struct wl_signal destroy; // struct wlr_input_method_manager_v2 } events; diff --git a/types/wlr_input_method_v2.c b/types/wlr_input_method_v2.c index ae50d784c..ed2a4ebeb 100644 --- a/types/wlr_input_method_v2.c +++ b/types/wlr_input_method_v2.c @@ -575,7 +575,7 @@ static void manager_get_input_method(struct wl_client *client, wl_resource_set_user_data(im_resource, input_method); wl_list_insert(&im_manager->input_methods, wl_resource_get_link(input_method->resource)); - wl_signal_emit_mutable(&im_manager->events.input_method, input_method); + wl_signal_emit_mutable(&im_manager->events.new_input_method, input_method); } static void manager_destroy(struct wl_client *client, @@ -608,7 +608,7 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); - assert(wl_list_empty(&manager->events.input_method.listener_list)); + assert(wl_list_empty(&manager->events.new_input_method.listener_list)); assert(wl_list_empty(&manager->events.destroy.listener_list)); wl_list_remove(&manager->display_destroy.link); @@ -623,7 +623,7 @@ struct wlr_input_method_manager_v2 *wlr_input_method_manager_v2_create( return NULL; } - wl_signal_init(&im_manager->events.input_method); + wl_signal_init(&im_manager->events.new_input_method); wl_signal_init(&im_manager->events.destroy); wl_list_init(&im_manager->input_methods); From 102a6bd415d4b4071d96bffdfc7c92626eacf3b3 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Mon, 7 Jul 2025 05:59:40 +0900 Subject: [PATCH 471/519] input-method: use `NULL` when emitting signals --- include/wlr/types/wlr_input_method_v2.h | 6 +++--- types/wlr_input_method_v2.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/wlr/types/wlr_input_method_v2.h b/include/wlr/types/wlr_input_method_v2.h index 885b3fe80..aaafbf5f2 100644 --- a/include/wlr/types/wlr_input_method_v2.h +++ b/include/wlr/types/wlr_input_method_v2.h @@ -48,10 +48,10 @@ struct wlr_input_method_v2 { struct wl_list link; struct { - struct wl_signal commit; // struct wlr_input_method_v2 + struct wl_signal commit; struct wl_signal new_popup_surface; // struct wlr_input_popup_surface_v2 struct wl_signal grab_keyboard; // struct wlr_input_method_keyboard_grab_v2 - struct wl_signal destroy; // struct wlr_input_method_v2 + struct wl_signal destroy; } events; struct { @@ -95,7 +95,7 @@ struct wlr_input_method_manager_v2 { struct { struct wl_signal new_input_method; // struct wlr_input_method_v2 - struct wl_signal destroy; // struct wlr_input_method_manager_v2 + struct wl_signal destroy; } events; struct { diff --git a/types/wlr_input_method_v2.c b/types/wlr_input_method_v2.c index ed2a4ebeb..0a521df48 100644 --- a/types/wlr_input_method_v2.c +++ b/types/wlr_input_method_v2.c @@ -56,7 +56,7 @@ static void input_method_destroy(struct wlr_input_method_v2 *input_method) { popup_surface, tmp, &input_method->popup_surfaces, link) { popup_surface_destroy(popup_surface); } - wl_signal_emit_mutable(&input_method->events.destroy, input_method); + wl_signal_emit_mutable(&input_method->events.destroy, NULL); assert(wl_list_empty(&input_method->events.commit.listener_list)); assert(wl_list_empty(&input_method->events.new_popup_surface.listener_list)); @@ -102,7 +102,7 @@ static void im_commit(struct wl_client *client, struct wl_resource *resource, input_method->current = input_method->pending; input_method->pending = (struct wlr_input_method_v2_state){0}; - wl_signal_emit_mutable(&input_method->events.commit, input_method); + wl_signal_emit_mutable(&input_method->events.commit, NULL); } static void im_commit_string(struct wl_client *client, @@ -606,7 +606,7 @@ static void input_method_manager_bind(struct wl_client *wl_client, void *data, static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_input_method_manager_v2 *manager = wl_container_of(listener, manager, display_destroy); - wl_signal_emit_mutable(&manager->events.destroy, manager); + wl_signal_emit_mutable(&manager->events.destroy, NULL); assert(wl_list_empty(&manager->events.new_input_method.listener_list)); assert(wl_list_empty(&manager->events.destroy.listener_list)); From 905465b0fa5e64cb966afda7d2e5d3c04a0438d2 Mon Sep 17 00:00:00 2001 From: David Turner Date: Tue, 9 Sep 2025 14:35:25 +0100 Subject: [PATCH 472/519] color-representation-v1: Actually set supported_*_len --- types/wlr_color_representation_v1.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/types/wlr_color_representation_v1.c b/types/wlr_color_representation_v1.c index ac804c60a..6590ec4e7 100644 --- a/types/wlr_color_representation_v1.c +++ b/types/wlr_color_representation_v1.c @@ -370,9 +370,11 @@ struct wlr_color_representation_manager_v1 *wlr_color_representation_manager_v1_ ok &= memdup(&manager->supported_alpha_modes, options->supported_alpha_modes, sizeof(options->supported_alpha_modes[0]) * options->supported_alpha_modes_len); + manager->supported_alpha_modes_len = options->supported_alpha_modes_len; ok &= memdup(&manager->supported_coeffs_and_ranges, options->supported_coeffs_and_ranges, sizeof(options->supported_coeffs_and_ranges[0]) * options->supported_coeffs_and_ranges_len); + manager->supported_coeffs_and_ranges_len = options->supported_coeffs_and_ranges_len; if (!ok) { goto err_options; } From cdd2c7e0064efee7fd398b2935923508b2fa9842 Mon Sep 17 00:00:00 2001 From: rewine Date: Tue, 5 Aug 2025 15:56:27 +0800 Subject: [PATCH 473/519] protocols: sync with wlr-protocols, apply non-breaking updates and doc improvements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This sync includes minor non-breaking updates from recent years: - Fix typos and grammatical issues (e.g. "a an" → "an", "inexistent" → "nonexistent") - Improve description consistency with `wl_output` (e.g. name, description, make, model, serial) - Add `destructor` annotation to relevant events (e.g. `finished` in foreign-toplevel) - Clarify event emission timing and behavior for output management - No functional or semantic protocol changes introduced These changes improve the accuracy and consistency of protocol descriptions without impacting compatibility. --- protocol/wlr-export-dmabuf-unstable-v1.xml | 4 +-- ...oreign-toplevel-management-unstable-v1.xml | 2 +- protocol/wlr-gamma-control-unstable-v1.xml | 2 +- .../wlr-output-management-unstable-v1.xml | 30 ++++++++++++------- ...lr-output-power-management-unstable-v1.xml | 4 +-- protocol/wlr-screencopy-unstable-v1.xml | 9 +++--- 6 files changed, 30 insertions(+), 21 deletions(-) diff --git a/protocol/wlr-export-dmabuf-unstable-v1.xml b/protocol/wlr-export-dmabuf-unstable-v1.xml index 751f7efbf..80ea012f5 100644 --- a/protocol/wlr-export-dmabuf-unstable-v1.xml +++ b/protocol/wlr-export-dmabuf-unstable-v1.xml @@ -43,7 +43,7 @@ - Capture the next frame of a an entire output. + Capture the next frame of an entire output. + summary="index of the plane the data in the object applies to"/> diff --git a/protocol/wlr-foreign-toplevel-management-unstable-v1.xml b/protocol/wlr-foreign-toplevel-management-unstable-v1.xml index 108133715..44505bbb6 100644 --- a/protocol/wlr-foreign-toplevel-management-unstable-v1.xml +++ b/protocol/wlr-foreign-toplevel-management-unstable-v1.xml @@ -58,7 +58,7 @@ - + This event indicates that the compositor is done sending events to the zwlr_foreign_toplevel_manager_v1. The server will destroy the object diff --git a/protocol/wlr-gamma-control-unstable-v1.xml b/protocol/wlr-gamma-control-unstable-v1.xml index a9db76240..16e0be8b1 100644 --- a/protocol/wlr-gamma-control-unstable-v1.xml +++ b/protocol/wlr-gamma-control-unstable-v1.xml @@ -72,7 +72,7 @@ tables. At any time the compositor can send a failed event indicating that this object is no longer valid. - There must always be at most one gamma control object per output, which + There can only be at most one gamma control object per output, which has exclusive access to this particular output. When the gamma control object is destroyed, the gamma table is restored to its original value. diff --git a/protocol/wlr-output-management-unstable-v1.xml b/protocol/wlr-output-management-unstable-v1.xml index 411e2f049..541284a8c 100644 --- a/protocol/wlr-output-management-unstable-v1.xml +++ b/protocol/wlr-output-management-unstable-v1.xml @@ -156,8 +156,8 @@ not assume that the name is a reflection of an underlying DRM connector, X11 connection, etc. - If the compositor implements the xdg-output protocol and this head is - enabled, the xdg_output.name event must report the same name. + If this head matches a wl_output, the wl_output.name event must report + the same name. The name event is sent after a wlr_output_head object is created. This event is only sent once per object, and the name does not change over @@ -176,8 +176,8 @@ the make, model, serial of the underlying DRM connector or the display name of the underlying X11 connection, etc. - If the compositor implements xdg-output and this head is enabled, - the xdg_output.description must report the same description. + If this head matches a wl_output, the wl_output.description event must + report the same name. The description event is sent after a wlr_output_head object is created. This event is only sent once per object, and the description does not @@ -191,6 +191,10 @@ This event describes the physical size of the head. This event is only sent if the head has a physical size (e.g. is not a projector or a virtual device). + + The physical size event is sent after a wlr_output_head object is created. This + event is only sent once per object, and the physical size does not change over + the lifetime of the wlr_output_head object. @@ -264,9 +268,6 @@ This event describes the manufacturer of the head. - This must report the same make as the wl_output interface does in its - geometry event. - Together with the model and serial_number events the purpose is to allow clients to recognize heads from previous sessions and for example load head-specific configurations back. @@ -278,6 +279,10 @@ identify the head by available information from other events but should be aware that there is an increased risk of false positives. + If sent, the make event is sent after a wlr_output_head object is + created and only sent once per object. The make does not change over + the lifetime of the wlr_output_head object. + It is not recommended to display the make string in UI to users. For that the string provided by the description event should be preferred. @@ -288,9 +293,6 @@ This event describes the model of the head. - This must report the same model as the wl_output interface does in its - geometry event. - Together with the make and serial_number events the purpose is to allow clients to recognize heads from previous sessions and for example load head-specific configurations back. @@ -302,6 +304,10 @@ identify the head by available information from other events but should be aware that there is an increased risk of false positives. + If sent, the model event is sent after a wlr_output_head object is + created and only sent once per object. The model does not change over + the lifetime of the wlr_output_head object. + It is not recommended to display the model string in UI to users. For that the string provided by the description event should be preferred. @@ -323,6 +329,10 @@ available information from other events but should be aware that there is an increased risk of false positives. + If sent, the serial number event is sent after a wlr_output_head object + is created and only sent once per object. The serial number does not + change over the lifetime of the wlr_output_head object. + It is not recommended to display the serial_number string in UI to users. For that the string provided by the description event should be preferred. diff --git a/protocol/wlr-output-power-management-unstable-v1.xml b/protocol/wlr-output-power-management-unstable-v1.xml index a97783991..20dbb7760 100644 --- a/protocol/wlr-output-power-management-unstable-v1.xml +++ b/protocol/wlr-output-power-management-unstable-v1.xml @@ -50,7 +50,7 @@ - Create a output power management mode control that can be used to + Create an output power management mode control that can be used to adjust the power management mode for a given output. @@ -79,7 +79,7 @@ - + diff --git a/protocol/wlr-screencopy-unstable-v1.xml b/protocol/wlr-screencopy-unstable-v1.xml index 50b1b7d2a..85b57d7ea 100644 --- a/protocol/wlr-screencopy-unstable-v1.xml +++ b/protocol/wlr-screencopy-unstable-v1.xml @@ -88,7 +88,7 @@ supported buffer type. The "buffer_done" event is sent afterwards to indicate that all supported buffer types have been enumerated. The client will then be able to send a "copy" request. If the capture is successful, - the compositor will send a "flags" followed by a "ready" event. + the compositor will send a "flags" event followed by a "ready" event. For objects version 2 or lower, wl_shm buffers are always supported, ie. the "buffer" event is guaranteed to be sent. @@ -114,12 +114,12 @@ - Copy the frame to the supplied buffer. The buffer must have a the + Copy the frame to the supplied buffer. The buffer must have the correct size, see zwlr_screencopy_frame_v1.buffer and zwlr_screencopy_frame_v1.linux_dmabuf. The buffer needs to have a supported format. - If the frame is successfully copied, a "flags" and a "ready" events are + If the frame is successfully copied, "flags" and "ready" events are sent. Otherwise, a "failed" event is sent. @@ -147,8 +147,7 @@ Called as soon as the frame is copied, indicating it is available - for reading. This event includes the time at which presentation happened - at. + for reading. This event includes the time at which the presentation took place. The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples, each component being an unsigned 32-bit value. Whole seconds are in From 5e5842cb1a8e11a7f274638e124ca473025734bc Mon Sep 17 00:00:00 2001 From: liupeng Date: Sat, 30 Aug 2025 14:42:14 +0800 Subject: [PATCH 474/519] drm_lease_v1: initialize device resource link during abnormal exit Signed-off-by: liupeng --- types/wlr_drm_lease_v1.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/types/wlr_drm_lease_v1.c b/types/wlr_drm_lease_v1.c index 892a401b3..14846f21d 100644 --- a/types/wlr_drm_lease_v1.c +++ b/types/wlr_drm_lease_v1.c @@ -453,10 +453,12 @@ static void lease_device_bind(struct wl_client *wl_client, void *data, if (!device) { wlr_log(WLR_DEBUG, "Failed to bind lease device, " "the wlr_drm_lease_device_v1 has been destroyed"); + wl_list_init(wl_resource_get_link(device_resource)); return; } wl_resource_set_user_data(device_resource, device); + wl_list_insert(&device->resources, wl_resource_get_link(device_resource)); int fd = wlr_drm_backend_get_non_master_fd(device->backend); if (fd < 0) { @@ -468,8 +470,6 @@ static void lease_device_bind(struct wl_client *wl_client, void *data, wp_drm_lease_device_v1_send_drm_fd(device_resource, fd); close(fd); - wl_list_insert(&device->resources, wl_resource_get_link(device_resource)); - struct wlr_drm_lease_connector_v1 *connector; wl_list_for_each(connector, &device->connectors, link) { drm_lease_connector_v1_send_to_client(connector, device_resource); From fd069ad4f2793c812bd47e40f52f92eb8af7ca34 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 7 Aug 2025 11:56:49 +0200 Subject: [PATCH 475/519] output/cursor: fix missing second cursor When attaching more than one cursor to wlr_output, the first one will pick the output's hardware cursor, then for the second one output_set_hardware_cursor() would fail (since the hardware cursor was already taken), but we still ended up resetting the current hardware cursor (by calling output_disable_hardware_cursor() below). As a result only the second cursor would be displayed. To fix this, move the current hardware cursor check to the caller. Fixes: 510664e79bfc ("output: disable hardware cursor when falling back to software") --- types/output/cursor.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/types/output/cursor.c b/types/output/cursor.c index b3ec152ce..70647afb7 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -288,13 +288,7 @@ static struct wlr_buffer *render_cursor_buffer(struct wlr_output_cursor *cursor) static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) { struct wlr_output *output = cursor->output; - if (!output->impl->set_cursor || - output->software_cursor_locks > 0) { - return false; - } - - struct wlr_output_cursor *hwcur = output->hardware_cursor; - if (hwcur != NULL && hwcur != cursor) { + if (!output->impl->set_cursor || output->software_cursor_locks > 0) { return false; } @@ -422,12 +416,15 @@ bool output_cursor_set_texture(struct wlr_output_cursor *cursor, wl_list_init(&cursor->renderer_destroy.link); } - if (output_cursor_attempt_hardware(cursor)) { - return true; + if (output->hardware_cursor == NULL || output->hardware_cursor == cursor) { + if (output_cursor_attempt_hardware(cursor)) { + return true; + } + + wlr_log(WLR_DEBUG, "Falling back to software cursor on output '%s'", output->name); + output_disable_hardware_cursor(output); } - wlr_log(WLR_DEBUG, "Falling back to software cursor on output '%s'", output->name); - output_disable_hardware_cursor(output); output_cursor_damage_whole(cursor); return true; } From b62c6878e116015d34827f66a8c4fee986e4ebfc Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 3 Aug 2025 17:25:05 +0200 Subject: [PATCH 476/519] scene/surface: simplify single-pixel-buffer check in surface_reconfigure() No need to call wlr_client_buffer_get() on wlr_client_buffer.base: we're already manipulating a wlr_client_buffer. --- types/scene/surface.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/types/scene/surface.c b/types/scene/surface.c index 7653323e3..e1c424ccd 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -217,10 +217,9 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { // can't use the cached scene_buffer->is_single_pixel_buffer // because that's only set later on. bool is_single_pixel_buffer = false; - struct wlr_client_buffer *client_buffer = wlr_client_buffer_get(&surface->buffer->base); - if (client_buffer != NULL && client_buffer->source != NULL) { + if (surface->buffer->source != NULL) { struct wlr_single_pixel_buffer_v1 *spb = - wlr_single_pixel_buffer_v1_try_from_buffer(client_buffer->source); + wlr_single_pixel_buffer_v1_try_from_buffer(surface->buffer->source); is_single_pixel_buffer = spb != NULL; } if (!is_single_pixel_buffer) { From bd566225eacdda2b72b967fb5168314924871052 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 3 Aug 2025 17:28:13 +0200 Subject: [PATCH 477/519] scene/surface: fix NULL deref when source buffer is destroyed Fixes the following crash, witnessed after a GPU reset: #0 0x00007fba9a32774c n/a (libc.so.6 + 0x9774c) #1 0x00007fba9a2cddc0 raise (libc.so.6 + 0x3ddc0) #2 0x00007fba9a2b557a abort (libc.so.6 + 0x2557a) #3 0x00007fba9a2b54e3 n/a (libc.so.6 + 0x254e3) #4 0x00007fba9a53fb78 wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer (libwlroots-0.20.so + 0x26b78) #5 0x00007fba9a590846 surface_reconfigure (libwlroots-0.20.so + 0x77846) #6 0x00007fba9a590cbb scene_surface_set_clip (libwlroots-0.20.so + 0x77cbb) #7 0x00007fba9a590efa subsurface_tree_set_clip (libwlroots-0.20.so + 0x77efa) #8 0x00007fba9a590f1f subsurface_tree_set_clip (libwlroots-0.20.so + 0x77f1f) #9 0x00007fba9a590f1f subsurface_tree_set_clip (libwlroots-0.20.so + 0x77f1f) #10 0x00007fba9a590f8d wlr_scene_subsurface_tree_set_clip (libwlroots-0.20.so + 0x77f8d) Reported-by: Hubert Hirtz --- types/scene/surface.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/types/scene/surface.c b/types/scene/surface.c index e1c424ccd..135ded6fd 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -245,7 +245,8 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { &surface->buffer->base, &options); if (syncobj_surface_state != NULL && - (surface->current.committed & WLR_SURFACE_STATE_BUFFER)) { + (surface->current.committed & WLR_SURFACE_STATE_BUFFER) && + surface->buffer->source != NULL) { wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer(syncobj_surface_state, surface->buffer->source); } From 462046ffdcdaacfc38ac606ed74397c360e23606 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 3 Aug 2025 17:30:07 +0200 Subject: [PATCH 478/519] cursor: use source buffer to signal release timeline point Same as 128cd07e9156 ("scene/surface: use source buffer to signal release timeline point"), but for the cursor. --- types/wlr_cursor.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/types/wlr_cursor.c b/types/wlr_cursor.c index 6dad11446..5f4aac398 100644 --- a/types/wlr_cursor.c +++ b/types/wlr_cursor.c @@ -585,10 +585,11 @@ static void cursor_output_cursor_update(struct wlr_cursor_output_cursor *output_ &src_box, dst_width, dst_height, surface->current.transform, hotspot_x, hotspot_y, wait_timeline, wait_point); - if (syncobj_surface_state != NULL && surface->buffer != NULL && + if (syncobj_surface_state != NULL && + surface->buffer != NULL && surface->buffer->source != NULL && (surface->current.committed & WLR_SURFACE_STATE_BUFFER)) { wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer(syncobj_surface_state, - &surface->buffer->base); + surface->buffer->source); } if (output_cursor->output_cursor->visible) { From d7ae9a866bf2372e0e86a9cf51a963b4fbc30c08 Mon Sep 17 00:00:00 2001 From: JiDe Zhang Date: Mon, 15 Sep 2025 15:58:51 +0800 Subject: [PATCH 479/519] xwayland: fix assertion failure in wlr_xwayland_shell_v1 The issue occurred when `wlr_xwayland_shell_v1` was destroyed before `wlr_xwayland`. This happened because `wlr_xwayland` didn't remove the listener for the shell's destroy event in `handle_shell_destroy`. --- xwayland/xwayland.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/xwayland/xwayland.c b/xwayland/xwayland.c index d25a132b5..5d51df074 100644 --- a/xwayland/xwayland.c +++ b/xwayland/xwayland.c @@ -69,6 +69,11 @@ static void handle_shell_destroy(struct wl_listener *listener, void *data) { struct wlr_xwayland *xwayland = wl_container_of(listener, xwayland, shell_destroy); xwayland->shell_v1 = NULL; + wl_list_remove(&xwayland->shell_destroy.link); + // Will remove this list in handle_shell_destroy(). + // This ensures the link is always initialized and + // avoids the need to keep check conditions in sync. + wl_list_init(&xwayland->shell_destroy.link); } void wlr_xwayland_destroy(struct wlr_xwayland *xwayland) { From dd7f5431891cad6b3980c8a7d5fdc1c1f668c1b1 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 8 Sep 2025 15:40:55 +0200 Subject: [PATCH 480/519] render/vulkan: Handle multi-descriptor sets A combined image sampler may need several descriptors in a descriptor set. We are not currently checking how many descriptors are required, nor is it presumably guaranteed that such multi-descriptor allocation will not fail due to fragmentation. If the pool free counter is not zero, try to allocate but continue with the next pool and fall back to creating a new pool if the allocation failed. Fixes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/4010 --- render/vulkan/renderer.c | 109 ++++++++++++++++++++++----------------- 1 file changed, 61 insertions(+), 48 deletions(-) diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index 08d99bb7c..f80435c99 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -66,59 +66,72 @@ static struct wlr_vk_descriptor_pool *alloc_ds( struct wl_list *pool_list, size_t *last_pool_size) { VkResult res; - bool found = false; - struct wlr_vk_descriptor_pool *pool; - wl_list_for_each(pool, pool_list, link) { - if (pool->free > 0) { - found = true; - break; - } - } - - if (!found) { // create new pool - pool = calloc(1, sizeof(*pool)); - if (!pool) { - wlr_log_errno(WLR_ERROR, "allocation failed"); - return NULL; - } - - size_t count = 2 * (*last_pool_size); - if (!count) { - count = start_descriptor_pool_size; - } - - pool->free = count; - VkDescriptorPoolSize pool_size = { - .descriptorCount = count, - .type = type, - }; - - VkDescriptorPoolCreateInfo dpool_info = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, - .maxSets = count, - .poolSizeCount = 1, - .pPoolSizes = &pool_size, - .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, - }; - - res = vkCreateDescriptorPool(renderer->dev->dev, &dpool_info, NULL, - &pool->pool); - if (res != VK_SUCCESS) { - wlr_vk_error("vkCreateDescriptorPool", res); - free(pool); - return NULL; - } - - *last_pool_size = count; - wl_list_insert(pool_list, &pool->link); - } - VkDescriptorSetAllocateInfo ds_info = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, .descriptorSetCount = 1, .pSetLayouts = layout, - .descriptorPool = pool->pool, }; + + struct wlr_vk_descriptor_pool *pool; + wl_list_for_each(pool, pool_list, link) { + if (pool->free > 0) { + ds_info.descriptorPool = pool->pool; + res = vkAllocateDescriptorSets(renderer->dev->dev, &ds_info, ds); + switch (res) { + case VK_ERROR_FRAGMENTED_POOL: + case VK_ERROR_OUT_OF_POOL_MEMORY: + // Descriptor sets with more than one descriptor can cause us + // to run out of pool memory early or lead to fragmentation + // that makes the pool unable to service our allocation + // request. Try the next pool or allocate a new one. + continue; + case VK_SUCCESS: + --pool->free; + return pool; + default: + wlr_vk_error("vkAllocateDescriptorSets", res); + return NULL; + } + } + } + + pool = calloc(1, sizeof(*pool)); + if (!pool) { + wlr_log_errno(WLR_ERROR, "allocation failed"); + return NULL; + } + + size_t count = 2 * (*last_pool_size); + if (!count) { + count = start_descriptor_pool_size; + } + + pool->free = count; + VkDescriptorPoolSize pool_size = { + .descriptorCount = count, + .type = type, + }; + + VkDescriptorPoolCreateInfo dpool_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .maxSets = count, + .poolSizeCount = 1, + .pPoolSizes = &pool_size, + .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, + }; + + res = vkCreateDescriptorPool(renderer->dev->dev, &dpool_info, NULL, + &pool->pool); + if (res != VK_SUCCESS) { + wlr_vk_error("vkCreateDescriptorPool", res); + free(pool); + return NULL; + } + + *last_pool_size = count; + wl_list_insert(pool_list, &pool->link); + + ds_info.descriptorPool = pool->pool; res = vkAllocateDescriptorSets(renderer->dev->dev, &ds_info, ds); if (res != VK_SUCCESS) { wlr_vk_error("vkAllocateDescriptorSets", res); From 54374b6fe69ab2f481ab83d54a38e355b63f047f Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 21 Jun 2025 16:57:51 +0200 Subject: [PATCH 481/519] render/vulkan: rename plain to two_pass We will introduce a new subpass without any post-processing step. Rename "plain" so that there's no confusion. --- include/render/vulkan.h | 4 +-- render/vulkan/pass.c | 32 ++++++++++----------- render/vulkan/renderer.c | 60 ++++++++++++++++++++-------------------- 3 files changed, 48 insertions(+), 48 deletions(-) diff --git a/include/render/vulkan.h b/include/render/vulkan.h index deff0eac3..52be0ba3d 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -241,10 +241,10 @@ struct wlr_vk_render_buffer { VkDescriptorSet blend_descriptor_set; struct wlr_vk_descriptor_pool *blend_attachment_pool; bool blend_transitioned; - } plain; + } two_pass; }; -bool vulkan_setup_plain_framebuffer(struct wlr_vk_render_buffer *buffer, +bool vulkan_setup_two_pass_framebuffer(struct wlr_vk_render_buffer *buffer, const struct wlr_dmabuf_attributes *dmabuf); struct wlr_vk_command_buffer { diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 4fd357113..b35ab257d 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -227,7 +227,7 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { VkPipeline pipeline = VK_NULL_HANDLE; if (pass->color_transform && pass->color_transform->type != COLOR_TRANSFORM_INVERSE_EOTF) { - pipeline = render_buffer->plain.render_setup->output_pipe_lut3d; + pipeline = render_buffer->two_pass.render_setup->output_pipe_lut3d; } else { enum wlr_color_transfer_function tf = WLR_COLOR_TRANSFER_FUNCTION_SRGB; if (pass->color_transform && pass->color_transform->type == COLOR_TRANSFORM_INVERSE_EOTF) { @@ -238,13 +238,13 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { switch (tf) { case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR: - pipeline = render_buffer->plain.render_setup->output_pipe_identity; + pipeline = render_buffer->two_pass.render_setup->output_pipe_identity; break; case WLR_COLOR_TRANSFER_FUNCTION_SRGB: - pipeline = render_buffer->plain.render_setup->output_pipe_srgb; + pipeline = render_buffer->two_pass.render_setup->output_pipe_srgb; break; case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ: - pipeline = render_buffer->plain.render_setup->output_pipe_pq; + pipeline = render_buffer->two_pass.render_setup->output_pipe_pq; break; } @@ -268,7 +268,7 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { lut_ds = renderer->output_ds_lut3d_dummy; } VkDescriptorSet ds[] = { - render_buffer->plain.blend_descriptor_set, // set 0 + render_buffer->two_pass.blend_descriptor_set, // set 0 lut_ds, // set 1 }; size_t ds_len = sizeof(ds) / sizeof(ds[0]); @@ -404,24 +404,24 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { render_buffer->srgb.transitioned = true; } } else { - if (!render_buffer->plain.transitioned) { + if (!render_buffer->two_pass.transitioned) { src_layout = VK_IMAGE_LAYOUT_PREINITIALIZED; - render_buffer->plain.transitioned = true; + render_buffer->two_pass.transitioned = true; } // The render pass changes the blend image layout from // color attachment to read only, so on each frame, before // the render pass starts, we change it back VkImageLayout blend_src_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - if (!render_buffer->plain.blend_transitioned) { + if (!render_buffer->two_pass.blend_transitioned) { blend_src_layout = VK_IMAGE_LAYOUT_UNDEFINED; - render_buffer->plain.blend_transitioned = true; + render_buffer->two_pass.blend_transitioned = true; } VkImageMemoryBarrier blend_acq_barrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = render_buffer->plain.blend_image, + .image = render_buffer->two_pass.blend_image, .oldLayout = blend_src_layout, .newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, .srcAccessMask = VK_ACCESS_SHADER_READ_BIT, @@ -680,7 +680,7 @@ static void render_pass_add_rect(struct wlr_render_pass *wlr_pass, struct wlr_vk_render_format_setup *setup = pass->srgb_pathway ? pass->render_buffer->srgb.render_setup : - pass->render_buffer->plain.render_setup; + pass->render_buffer->two_pass.render_setup; struct wlr_vk_pipeline *pipe = setup_get_or_create_pipeline( setup, &(struct wlr_vk_pipeline_key) { @@ -807,7 +807,7 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, struct wlr_vk_render_format_setup *setup = pass->srgb_pathway ? pass->render_buffer->srgb.render_setup : - pass->render_buffer->plain.render_setup; + pass->render_buffer->two_pass.render_setup; struct wlr_vk_pipeline *pipe = setup_get_or_create_pipeline( setup, &(struct wlr_vk_pipeline_key) { @@ -1193,10 +1193,10 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend using_srgb_pathway = buffer->srgb.framebuffer != VK_NULL_HANDLE; } - if (!using_srgb_pathway && !buffer->plain.image_view) { + if (!using_srgb_pathway && !buffer->two_pass.image_view) { struct wlr_dmabuf_attributes attribs; wlr_buffer_get_dmabuf(buffer->wlr_buffer, &attribs); - if (!vulkan_setup_plain_framebuffer(buffer, &attribs)) { + if (!vulkan_setup_two_pass_framebuffer(buffer, &attribs)) { wlr_log(WLR_ERROR, "Failed to set up blend image"); return NULL; } @@ -1262,8 +1262,8 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend rp_info.renderPass = buffer->srgb.render_setup->render_pass; rp_info.framebuffer = buffer->srgb.framebuffer; } else { - rp_info.renderPass = buffer->plain.render_setup->render_pass; - rp_info.framebuffer = buffer->plain.framebuffer; + rp_info.renderPass = buffer->two_pass.render_setup->render_pass; + rp_info.framebuffer = buffer->two_pass.framebuffer; } vkCmdBeginRenderPass(cb->vk, &rp_info, VK_SUBPASS_CONTENTS_INLINE); diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index f80435c99..62168d979 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -620,14 +620,14 @@ static void destroy_render_buffer(struct wlr_vk_render_buffer *buffer) { vkDestroyFramebuffer(dev, buffer->srgb.framebuffer, NULL); vkDestroyImageView(dev, buffer->srgb.image_view, NULL); - vkDestroyFramebuffer(dev, buffer->plain.framebuffer, NULL); - vkDestroyImageView(dev, buffer->plain.image_view, NULL); - vkDestroyImage(dev, buffer->plain.blend_image, NULL); - vkFreeMemory(dev, buffer->plain.blend_memory, NULL); - vkDestroyImageView(dev, buffer->plain.blend_image_view, NULL); - if (buffer->plain.blend_attachment_pool) { - vulkan_free_ds(buffer->renderer, buffer->plain.blend_attachment_pool, - buffer->plain.blend_descriptor_set); + vkDestroyFramebuffer(dev, buffer->two_pass.framebuffer, NULL); + vkDestroyImageView(dev, buffer->two_pass.image_view, NULL); + vkDestroyImage(dev, buffer->two_pass.blend_image, NULL); + vkFreeMemory(dev, buffer->two_pass.blend_memory, NULL); + vkDestroyImageView(dev, buffer->two_pass.blend_image_view, NULL); + if (buffer->two_pass.blend_attachment_pool) { + vulkan_free_ds(buffer->renderer, buffer->two_pass.blend_attachment_pool, + buffer->two_pass.blend_descriptor_set); } vkDestroyImage(dev, buffer->image, NULL); @@ -648,7 +648,7 @@ static struct wlr_addon_interface render_buffer_addon_impl = { .destroy = handle_render_buffer_destroy, }; -bool vulkan_setup_plain_framebuffer(struct wlr_vk_render_buffer *buffer, +bool vulkan_setup_two_pass_framebuffer(struct wlr_vk_render_buffer *buffer, const struct wlr_dmabuf_attributes *dmabuf) { struct wlr_vk_renderer *renderer = buffer->renderer; VkResult res; @@ -676,15 +676,15 @@ bool vulkan_setup_plain_framebuffer(struct wlr_vk_render_buffer *buffer, }, }; - res = vkCreateImageView(dev, &view_info, NULL, &buffer->plain.image_view); + res = vkCreateImageView(dev, &view_info, NULL, &buffer->two_pass.image_view); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateImageView failed", res); goto error; } - buffer->plain.render_setup = find_or_create_render_setup( + buffer->two_pass.render_setup = find_or_create_render_setup( renderer, &fmt->format, true); - if (!buffer->plain.render_setup) { + if (!buffer->two_pass.render_setup) { goto error; } @@ -704,14 +704,14 @@ bool vulkan_setup_plain_framebuffer(struct wlr_vk_render_buffer *buffer, .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT, }; - res = vkCreateImage(dev, &img_info, NULL, &buffer->plain.blend_image); + res = vkCreateImage(dev, &img_info, NULL, &buffer->two_pass.blend_image); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateImage failed", res); goto error; } VkMemoryRequirements mem_reqs; - vkGetImageMemoryRequirements(dev, buffer->plain.blend_image, &mem_reqs); + vkGetImageMemoryRequirements(dev, buffer->two_pass.blend_image, &mem_reqs); int mem_type_index = vulkan_find_mem_type(renderer->dev, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mem_reqs.memoryTypeBits); @@ -726,13 +726,13 @@ bool vulkan_setup_plain_framebuffer(struct wlr_vk_render_buffer *buffer, .memoryTypeIndex = mem_type_index, }; - res = vkAllocateMemory(dev, &mem_info, NULL, &buffer->plain.blend_memory); + res = vkAllocateMemory(dev, &mem_info, NULL, &buffer->two_pass.blend_memory); if (res != VK_SUCCESS) { wlr_vk_error("vkAllocatorMemory failed", res); goto error; } - res = vkBindImageMemory(dev, buffer->plain.blend_image, buffer->plain.blend_memory, 0); + res = vkBindImageMemory(dev, buffer->two_pass.blend_image, buffer->two_pass.blend_memory, 0); if (res != VK_SUCCESS) { wlr_vk_error("vkBindMemory failed", res); goto error; @@ -740,7 +740,7 @@ bool vulkan_setup_plain_framebuffer(struct wlr_vk_render_buffer *buffer, VkImageViewCreateInfo blend_view_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .image = buffer->plain.blend_image, + .image = buffer->two_pass.blend_image, .viewType = VK_IMAGE_VIEW_TYPE_2D, .format = img_info.format, .components.r = VK_COMPONENT_SWIZZLE_IDENTITY, @@ -756,37 +756,37 @@ bool vulkan_setup_plain_framebuffer(struct wlr_vk_render_buffer *buffer, }, }; - res = vkCreateImageView(dev, &blend_view_info, NULL, &buffer->plain.blend_image_view); + res = vkCreateImageView(dev, &blend_view_info, NULL, &buffer->two_pass.blend_image_view); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateImageView failed", res); goto error; } - buffer->plain.blend_attachment_pool = vulkan_alloc_blend_ds(renderer, - &buffer->plain.blend_descriptor_set); - if (!buffer->plain.blend_attachment_pool) { + buffer->two_pass.blend_attachment_pool = vulkan_alloc_blend_ds(renderer, + &buffer->two_pass.blend_descriptor_set); + if (!buffer->two_pass.blend_attachment_pool) { wlr_log(WLR_ERROR, "failed to allocate descriptor"); goto error; } VkDescriptorImageInfo ds_attach_info = { .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - .imageView = buffer->plain.blend_image_view, + .imageView = buffer->two_pass.blend_image_view, .sampler = VK_NULL_HANDLE, }; VkWriteDescriptorSet ds_write = { .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .descriptorCount = 1, .descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, - .dstSet = buffer->plain.blend_descriptor_set, + .dstSet = buffer->two_pass.blend_descriptor_set, .dstBinding = 0, .pImageInfo = &ds_attach_info, }; vkUpdateDescriptorSets(dev, 1, &ds_write, 0, NULL); VkImageView attachments[] = { - buffer->plain.blend_image_view, - buffer->plain.image_view + buffer->two_pass.blend_image_view, + buffer->two_pass.image_view, }; VkFramebufferCreateInfo fb_info = { .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, @@ -796,10 +796,10 @@ bool vulkan_setup_plain_framebuffer(struct wlr_vk_render_buffer *buffer, .width = dmabuf->width, .height = dmabuf->height, .layers = 1u, - .renderPass = buffer->plain.render_setup->render_pass, + .renderPass = buffer->two_pass.render_setup->render_pass, }; - res = vkCreateFramebuffer(dev, &fb_info, NULL, &buffer->plain.framebuffer); + res = vkCreateFramebuffer(dev, &fb_info, NULL, &buffer->two_pass.framebuffer); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateFramebuffer", res); goto error; @@ -824,7 +824,7 @@ static bool vulkan_setup_srgb_framebuffer(struct wlr_vk_render_buffer *buffer, assert(fmt); assert(fmt->format.vk_srgb); - // Set up the srgb framebuffer by default; plain framebuffer and + // Set up the srgb framebuffer by default; two-pass framebuffer and // blending image will be set up later if necessary VkImageViewCreateInfo view_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, @@ -920,8 +920,8 @@ static struct wlr_vk_render_buffer *create_render_buffer( goto error; } } else { - // Set up the plain framebuffer & blending image - if (!vulkan_setup_plain_framebuffer(buffer, &dmabuf)) { + // Set up the two-pass framebuffer & blending image + if (!vulkan_setup_two_pass_framebuffer(buffer, &dmabuf)) { goto error; } } From 7f6d66ea62c88e788421970f6e7ff6dd252f4f46 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 21 Jun 2025 17:07:00 +0200 Subject: [PATCH 482/519] render/vulkan: use sRGB image view when color transform is set If the color transform is set to sRGB inverse EOTF, we can use the sRGB image view just like when no color transform is passed in. --- render/vulkan/pass.c | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index b35ab257d..646e3cab6 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -1177,28 +1177,41 @@ static const struct wlr_addon_interface vk_color_transform_impl = { struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *renderer, struct wlr_vk_render_buffer *buffer, const struct wlr_buffer_pass_options *options) { - bool using_srgb_pathway; + uint32_t inv_eotf; if (options != NULL && options->color_transform != NULL) { - using_srgb_pathway = false; + if (options->color_transform->type == COLOR_TRANSFORM_INVERSE_EOTF) { + struct wlr_color_transform_inverse_eotf *tr = + wlr_color_transform_inverse_eotf_from_base(options->color_transform); + inv_eotf = tr->tf; + } else { + // Color transform is not an inverse EOTF + inv_eotf = 0; + } + } else { + // This is the default when unspecified + inv_eotf = WLR_COLOR_TRANSFER_FUNCTION_SRGB; + } - if (!get_color_transform(options->color_transform, renderer)) { + bool using_srgb_pathway = inv_eotf == WLR_COLOR_TRANSFER_FUNCTION_SRGB && + buffer->srgb.framebuffer != VK_NULL_HANDLE; + + if (!using_srgb_pathway) { + if (options != NULL && options->color_transform != NULL && + !get_color_transform(options->color_transform, renderer)) { /* Try to create a new color transform */ if (!vk_color_transform_create(renderer, options->color_transform)) { wlr_log(WLR_ERROR, "Failed to create color transform"); return NULL; } } - } else { - // Use srgb pathway if it is the default/has already been set up - using_srgb_pathway = buffer->srgb.framebuffer != VK_NULL_HANDLE; - } - if (!using_srgb_pathway && !buffer->two_pass.image_view) { - struct wlr_dmabuf_attributes attribs; - wlr_buffer_get_dmabuf(buffer->wlr_buffer, &attribs); - if (!vulkan_setup_two_pass_framebuffer(buffer, &attribs)) { - wlr_log(WLR_ERROR, "Failed to set up blend image"); - return NULL; + if (!buffer->two_pass.image_view) { + struct wlr_dmabuf_attributes attribs; + wlr_buffer_get_dmabuf(buffer->wlr_buffer, &attribs); + if (!vulkan_setup_two_pass_framebuffer(buffer, &attribs)) { + wlr_log(WLR_ERROR, "Failed to set up blend image"); + return NULL; + } } } From 6fee3623e44bbfaf7588431ee87c1f85c8de9fcb Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 21 Jun 2025 17:18:26 +0200 Subject: [PATCH 483/519] render/vulkan: rename vulkan_setup_srgb_framebuffer() for linear Rename to "one-pass" (to indicate no blending buffer is involved), because this will get re-used when introducing a linear single-subpass codepath. --- render/vulkan/renderer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index 62168d979..4f69c0c94 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -813,7 +813,7 @@ error: return false; } -static bool vulkan_setup_srgb_framebuffer(struct wlr_vk_render_buffer *buffer, +static bool vulkan_setup_one_pass_framebuffer(struct wlr_vk_render_buffer *buffer, const struct wlr_dmabuf_attributes *dmabuf) { struct wlr_vk_renderer *renderer = buffer->renderer; VkResult res; @@ -916,7 +916,7 @@ static struct wlr_vk_render_buffer *create_render_buffer( } if (using_mutable_srgb) { - if (!vulkan_setup_srgb_framebuffer(buffer, &dmabuf)) { + if (!vulkan_setup_one_pass_framebuffer(buffer, &dmabuf)) { goto error; } } else { From a91f96b391c9ea32ba0ba89875a360130eb830c9 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 21 Jun 2025 17:39:42 +0200 Subject: [PATCH 484/519] render/vulkan: introduce wlr_vk_render_buffer_out Holds common state for final output buffer targets. --- include/render/vulkan.h | 16 +++++++++------- render/vulkan/pass.c | 16 ++++++++-------- render/vulkan/renderer.c | 24 ++++++++++++++---------- 3 files changed, 31 insertions(+), 25 deletions(-) diff --git a/include/render/vulkan.h b/include/render/vulkan.h index 52be0ba3d..ef5daceef 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -204,6 +204,13 @@ struct wlr_vk_render_format_setup { struct wl_list pipelines; // struct wlr_vk_pipeline.link }; +// Final output framebuffer and image view +struct wlr_vk_render_buffer_out { + VkImageView image_view; + VkFramebuffer framebuffer; + bool transitioned; +}; + // Renderer-internal represenation of an wlr_buffer imported for rendering. struct wlr_vk_render_buffer { struct wlr_buffer *wlr_buffer; @@ -219,22 +226,17 @@ struct wlr_vk_render_buffer { // This requires that the image support an _SRGB VkFormat, and does // not work with color transforms. struct { + struct wlr_vk_render_buffer_out out; struct wlr_vk_render_format_setup *render_setup; - VkImageView image_view; - VkFramebuffer framebuffer; - bool transitioned; } srgb; // Framebuffer, image view, and blending image to render indirectly // onto the buffer image. This works for general image types and permits // color transforms. struct { + struct wlr_vk_render_buffer_out out; struct wlr_vk_render_format_setup *render_setup; - VkImageView image_view; - VkFramebuffer framebuffer; - bool transitioned; - VkImage blend_image; VkImageView blend_image_view; VkDeviceMemory blend_memory; diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 646e3cab6..c18761b9f 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -399,14 +399,14 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { // also add acquire/release barriers for the current render buffer VkImageLayout src_layout = VK_IMAGE_LAYOUT_GENERAL; if (pass->srgb_pathway) { - if (!render_buffer->srgb.transitioned) { + if (!render_buffer->srgb.out.transitioned) { src_layout = VK_IMAGE_LAYOUT_PREINITIALIZED; - render_buffer->srgb.transitioned = true; + render_buffer->srgb.out.transitioned = true; } } else { - if (!render_buffer->two_pass.transitioned) { + if (!render_buffer->two_pass.out.transitioned) { src_layout = VK_IMAGE_LAYOUT_PREINITIALIZED; - render_buffer->two_pass.transitioned = true; + render_buffer->two_pass.out.transitioned = true; } // The render pass changes the blend image layout from // color attachment to read only, so on each frame, before @@ -1193,7 +1193,7 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend } bool using_srgb_pathway = inv_eotf == WLR_COLOR_TRANSFER_FUNCTION_SRGB && - buffer->srgb.framebuffer != VK_NULL_HANDLE; + buffer->srgb.out.framebuffer != VK_NULL_HANDLE; if (!using_srgb_pathway) { if (options != NULL && options->color_transform != NULL && @@ -1205,7 +1205,7 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend } } - if (!buffer->two_pass.image_view) { + if (!buffer->two_pass.out.image_view) { struct wlr_dmabuf_attributes attribs; wlr_buffer_get_dmabuf(buffer->wlr_buffer, &attribs); if (!vulkan_setup_two_pass_framebuffer(buffer, &attribs)) { @@ -1273,10 +1273,10 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend }; if (pass->srgb_pathway) { rp_info.renderPass = buffer->srgb.render_setup->render_pass; - rp_info.framebuffer = buffer->srgb.framebuffer; + rp_info.framebuffer = buffer->srgb.out.framebuffer; } else { rp_info.renderPass = buffer->two_pass.render_setup->render_pass; - rp_info.framebuffer = buffer->two_pass.framebuffer; + rp_info.framebuffer = buffer->two_pass.out.framebuffer; } vkCmdBeginRenderPass(cb->vk, &rp_info, VK_SUBPASS_CONTENTS_INLINE); diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index 4f69c0c94..23f75d93e 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -604,6 +604,12 @@ void vulkan_reset_command_buffer(struct wlr_vk_command_buffer *cb) { } } +static void finish_render_buffer_out(struct wlr_vk_render_buffer_out *out, + VkDevice dev) { + vkDestroyFramebuffer(dev, out->framebuffer, NULL); + vkDestroyImageView(dev, out->image_view, NULL); +} + static void destroy_render_buffer(struct wlr_vk_render_buffer *buffer) { wl_list_remove(&buffer->link); wlr_addon_finish(&buffer->addon); @@ -617,11 +623,9 @@ static void destroy_render_buffer(struct wlr_vk_render_buffer *buffer) { wlr_vk_error("vkQueueWaitIdle", res); } - vkDestroyFramebuffer(dev, buffer->srgb.framebuffer, NULL); - vkDestroyImageView(dev, buffer->srgb.image_view, NULL); + finish_render_buffer_out(&buffer->srgb.out, dev); - vkDestroyFramebuffer(dev, buffer->two_pass.framebuffer, NULL); - vkDestroyImageView(dev, buffer->two_pass.image_view, NULL); + finish_render_buffer_out(&buffer->two_pass.out, dev); vkDestroyImage(dev, buffer->two_pass.blend_image, NULL); vkFreeMemory(dev, buffer->two_pass.blend_memory, NULL); vkDestroyImageView(dev, buffer->two_pass.blend_image_view, NULL); @@ -676,7 +680,7 @@ bool vulkan_setup_two_pass_framebuffer(struct wlr_vk_render_buffer *buffer, }, }; - res = vkCreateImageView(dev, &view_info, NULL, &buffer->two_pass.image_view); + res = vkCreateImageView(dev, &view_info, NULL, &buffer->two_pass.out.image_view); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateImageView failed", res); goto error; @@ -786,7 +790,7 @@ bool vulkan_setup_two_pass_framebuffer(struct wlr_vk_render_buffer *buffer, VkImageView attachments[] = { buffer->two_pass.blend_image_view, - buffer->two_pass.image_view, + buffer->two_pass.out.image_view, }; VkFramebufferCreateInfo fb_info = { .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, @@ -799,7 +803,7 @@ bool vulkan_setup_two_pass_framebuffer(struct wlr_vk_render_buffer *buffer, .renderPass = buffer->two_pass.render_setup->render_pass, }; - res = vkCreateFramebuffer(dev, &fb_info, NULL, &buffer->two_pass.framebuffer); + res = vkCreateFramebuffer(dev, &fb_info, NULL, &buffer->two_pass.out.framebuffer); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateFramebuffer", res); goto error; @@ -844,7 +848,7 @@ static bool vulkan_setup_one_pass_framebuffer(struct wlr_vk_render_buffer *buffe }, }; - res = vkCreateImageView(dev, &view_info, NULL, &buffer->srgb.image_view); + res = vkCreateImageView(dev, &view_info, NULL, &buffer->srgb.out.image_view); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateImageView failed", res); goto error; @@ -859,7 +863,7 @@ static bool vulkan_setup_one_pass_framebuffer(struct wlr_vk_render_buffer *buffe VkFramebufferCreateInfo fb_info = { .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, .attachmentCount = 1, - .pAttachments = &buffer->srgb.image_view, + .pAttachments = &buffer->srgb.out.image_view, .flags = 0u, .width = dmabuf->width, .height = dmabuf->height, @@ -867,7 +871,7 @@ static bool vulkan_setup_one_pass_framebuffer(struct wlr_vk_render_buffer *buffe .renderPass = buffer->srgb.render_setup->render_pass, }; - res = vkCreateFramebuffer(dev, &fb_info, NULL, &buffer->srgb.framebuffer); + res = vkCreateFramebuffer(dev, &fb_info, NULL, &buffer->srgb.out.framebuffer); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateFramebuffer", res); goto error; From 35eba5f2fe7580031d386a0857414f47f10fa7a0 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 21 Jun 2025 17:59:29 +0200 Subject: [PATCH 485/519] render/vulkan: add wlr_vk_render_pass.render_setup Simplifies the logic and prepares for a new render setup. --- include/render/vulkan.h | 1 + render/vulkan/pass.c | 17 +++++++---------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/include/render/vulkan.h b/include/render/vulkan.h index ef5daceef..20272882d 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -398,6 +398,7 @@ struct wlr_vk_render_pass { struct wlr_render_pass base; struct wlr_vk_renderer *renderer; struct wlr_vk_render_buffer *render_buffer; + struct wlr_vk_render_format_setup *render_setup; struct wlr_vk_command_buffer *command_buffer; struct rect_union updated_region; VkPipeline bound_pipeline; diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index c18761b9f..00f840175 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -678,11 +678,8 @@ static void render_pass_add_rect(struct wlr_render_pass *wlr_pass, wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, proj); wlr_matrix_multiply(matrix, pass->projection, matrix); - struct wlr_vk_render_format_setup *setup = pass->srgb_pathway ? - pass->render_buffer->srgb.render_setup : - pass->render_buffer->two_pass.render_setup; struct wlr_vk_pipeline *pipe = setup_get_or_create_pipeline( - setup, + pass->render_setup, &(struct wlr_vk_pipeline_key) { .source = WLR_VK_SHADER_SOURCE_SINGLE_COLOR, .layout = { .ycbcr_format = NULL }, @@ -805,11 +802,8 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, break; } - struct wlr_vk_render_format_setup *setup = pass->srgb_pathway ? - pass->render_buffer->srgb.render_setup : - pass->render_buffer->two_pass.render_setup; struct wlr_vk_pipeline *pipe = setup_get_or_create_pipeline( - setup, + pass->render_setup, &(struct wlr_vk_pipeline_key) { .source = WLR_VK_SHADER_SOURCE_TEXTURE, .layout = { @@ -1215,6 +1209,9 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend } } + struct wlr_vk_render_format_setup *render_setup = + using_srgb_pathway ? buffer->srgb.render_setup : buffer->two_pass.render_setup; + struct wlr_vk_render_pass *pass = calloc(1, sizeof(*pass)); if (pass == NULL) { return NULL; @@ -1270,12 +1267,11 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, .renderArea = rect, .clearValueCount = 0, + .renderPass = render_setup->render_pass, }; if (pass->srgb_pathway) { - rp_info.renderPass = buffer->srgb.render_setup->render_pass; rp_info.framebuffer = buffer->srgb.out.framebuffer; } else { - rp_info.renderPass = buffer->two_pass.render_setup->render_pass; rp_info.framebuffer = buffer->two_pass.out.framebuffer; } vkCmdBeginRenderPass(cb->vk, &rp_info, VK_SUBPASS_CONTENTS_INLINE); @@ -1292,6 +1288,7 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend wlr_buffer_lock(buffer->wlr_buffer); pass->render_buffer = buffer; + pass->render_setup = render_setup; pass->command_buffer = cb; return pass; } From b2d09cdee9bcc512bda047c27942976561e44b5f Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 21 Jun 2025 18:06:28 +0200 Subject: [PATCH 486/519] render/vulkan: add wlr_vk_render_pass.render_buffer_out Simplifies the logic and prepares for a new render setup. --- include/render/vulkan.h | 1 + render/vulkan/pass.c | 25 ++++++++++--------------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/include/render/vulkan.h b/include/render/vulkan.h index 20272882d..5f84ca57d 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -398,6 +398,7 @@ struct wlr_vk_render_pass { struct wlr_render_pass base; struct wlr_vk_renderer *renderer; struct wlr_vk_render_buffer *render_buffer; + struct wlr_vk_render_buffer_out *render_buffer_out; struct wlr_vk_render_format_setup *render_setup; struct wlr_vk_command_buffer *command_buffer; struct rect_union updated_region; diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 00f840175..c843b9b8f 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -398,16 +398,12 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { // also add acquire/release barriers for the current render buffer VkImageLayout src_layout = VK_IMAGE_LAYOUT_GENERAL; - if (pass->srgb_pathway) { - if (!render_buffer->srgb.out.transitioned) { - src_layout = VK_IMAGE_LAYOUT_PREINITIALIZED; - render_buffer->srgb.out.transitioned = true; - } - } else { - if (!render_buffer->two_pass.out.transitioned) { - src_layout = VK_IMAGE_LAYOUT_PREINITIALIZED; - render_buffer->two_pass.out.transitioned = true; - } + if (!pass->render_buffer_out->transitioned) { + src_layout = VK_IMAGE_LAYOUT_PREINITIALIZED; + pass->render_buffer_out->transitioned = true; + } + + if (!pass->srgb_pathway) { // The render pass changes the blend image layout from // color attachment to read only, so on each frame, before // the render pass starts, we change it back @@ -1211,6 +1207,8 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend struct wlr_vk_render_format_setup *render_setup = using_srgb_pathway ? buffer->srgb.render_setup : buffer->two_pass.render_setup; + struct wlr_vk_render_buffer_out *buffer_out = + using_srgb_pathway ? &buffer->srgb.out : &buffer->two_pass.out; struct wlr_vk_render_pass *pass = calloc(1, sizeof(*pass)); if (pass == NULL) { @@ -1268,12 +1266,8 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend .renderArea = rect, .clearValueCount = 0, .renderPass = render_setup->render_pass, + .framebuffer = buffer_out->framebuffer, }; - if (pass->srgb_pathway) { - rp_info.framebuffer = buffer->srgb.out.framebuffer; - } else { - rp_info.framebuffer = buffer->two_pass.out.framebuffer; - } vkCmdBeginRenderPass(cb->vk, &rp_info, VK_SUBPASS_CONTENTS_INLINE); vkCmdSetViewport(cb->vk, 0, 1, &(VkViewport){ @@ -1288,6 +1282,7 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend wlr_buffer_lock(buffer->wlr_buffer); pass->render_buffer = buffer; + pass->render_buffer_out = buffer_out; pass->render_setup = render_setup; pass->command_buffer = cb; return pass; From 3e88a79e6f43dea779996673939561ad54cab4be Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 21 Jun 2025 18:14:45 +0200 Subject: [PATCH 487/519] render/vulkan: replace wlr_vk_render_pass.srgb_pathway with two_pass The important bit here is whether this is using a single or two sub-passes. The flag isn't used for anything else. Preparation for an upcoming one-subpass codepath. --- include/render/vulkan.h | 2 +- render/vulkan/pass.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/render/vulkan.h b/include/render/vulkan.h index 5f84ca57d..1d6e9949e 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -405,7 +405,7 @@ struct wlr_vk_render_pass { VkPipeline bound_pipeline; float projection[9]; bool failed; - bool srgb_pathway; // if false, rendering via intermediate blending buffer + bool two_pass; // rendering via intermediate blending buffer struct wlr_color_transform *color_transform; bool has_primaries; diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index c843b9b8f..a950e2732 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -175,7 +175,7 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { assert(stage_cb != NULL); renderer->stage.cb = NULL; - if (!pass->srgb_pathway) { + if (pass->two_pass) { // Apply output shader to map blend image to actual output image vkCmdNextSubpass(render_cb->vk, VK_SUBPASS_CONTENTS_INLINE); @@ -403,7 +403,7 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { pass->render_buffer_out->transitioned = true; } - if (!pass->srgb_pathway) { + if (pass->two_pass) { // The render pass changes the blend image layout from // color attachment to read only, so on each frame, before // the render pass starts, we change it back @@ -614,7 +614,7 @@ error: static void render_pass_mark_box_updated(struct wlr_vk_render_pass *pass, const struct wlr_box *box) { - if (pass->srgb_pathway) { + if (!pass->two_pass) { return; } @@ -1217,7 +1217,7 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend wlr_render_pass_init(&pass->base, &render_pass_impl); pass->renderer = renderer; - pass->srgb_pathway = using_srgb_pathway; + pass->two_pass = !using_srgb_pathway; if (options != NULL && options->color_transform != NULL) { pass->color_transform = wlr_color_transform_ref(options->color_transform); } From d1c88e94970eea1df013d78e47c0d9d904082795 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 21 Jun 2025 18:22:43 +0200 Subject: [PATCH 488/519] render/vulkan: add linear single-subpass When the TF is set to EXT_LINEAR, we can write out color values straight up to a non-SRGB image view. --- include/render/vulkan.h | 9 +++++++++ render/vulkan/pass.c | 33 +++++++++++++++++++++++++++------ render/vulkan/renderer.c | 37 +++++++++++++++++++++++++------------ 3 files changed, 61 insertions(+), 18 deletions(-) diff --git a/include/render/vulkan.h b/include/render/vulkan.h index 1d6e9949e..33d158dee 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -222,6 +222,13 @@ struct wlr_vk_render_buffer { uint32_t mem_count; VkImage image; + // Framebuffer and image view for rendering directly onto the buffer image, + // without any color transform. + struct { + struct wlr_vk_render_buffer_out out; + struct wlr_vk_render_format_setup *render_setup; + } linear; + // Framebuffer and image view for rendering directly onto the buffer image. // This requires that the image support an _SRGB VkFormat, and does // not work with color transforms. @@ -246,6 +253,8 @@ struct wlr_vk_render_buffer { } two_pass; }; +bool vulkan_setup_one_pass_framebuffer(struct wlr_vk_render_buffer *buffer, + const struct wlr_dmabuf_attributes *dmabuf, bool srgb); bool vulkan_setup_two_pass_framebuffer(struct wlr_vk_render_buffer *buffer, const struct wlr_dmabuf_attributes *dmabuf); diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index a950e2732..4cbb3c26c 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -1182,10 +1182,21 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend inv_eotf = WLR_COLOR_TRANSFER_FUNCTION_SRGB; } + bool using_linear_pathway = inv_eotf == WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR; bool using_srgb_pathway = inv_eotf == WLR_COLOR_TRANSFER_FUNCTION_SRGB && buffer->srgb.out.framebuffer != VK_NULL_HANDLE; + bool using_two_pass_pathway = !using_linear_pathway && !using_srgb_pathway; - if (!using_srgb_pathway) { + if (using_linear_pathway && !buffer->linear.out.image_view) { + struct wlr_dmabuf_attributes attribs; + wlr_buffer_get_dmabuf(buffer->wlr_buffer, &attribs); + if (!vulkan_setup_one_pass_framebuffer(buffer, &attribs, false)) { + wlr_log(WLR_ERROR, "Failed to set up blend image"); + return NULL; + } + } + + if (using_two_pass_pathway) { if (options != NULL && options->color_transform != NULL && !get_color_transform(options->color_transform, renderer)) { /* Try to create a new color transform */ @@ -1205,10 +1216,20 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend } } - struct wlr_vk_render_format_setup *render_setup = - using_srgb_pathway ? buffer->srgb.render_setup : buffer->two_pass.render_setup; - struct wlr_vk_render_buffer_out *buffer_out = - using_srgb_pathway ? &buffer->srgb.out : &buffer->two_pass.out; + struct wlr_vk_render_format_setup *render_setup; + struct wlr_vk_render_buffer_out *buffer_out; + if (using_two_pass_pathway) { + render_setup = buffer->two_pass.render_setup; + buffer_out = &buffer->two_pass.out; + } else if (using_srgb_pathway) { + render_setup = buffer->srgb.render_setup; + buffer_out = &buffer->srgb.out; + } else if (using_linear_pathway) { + render_setup = buffer->linear.render_setup; + buffer_out = &buffer->linear.out; + } else { + abort(); // unreachable + } struct wlr_vk_render_pass *pass = calloc(1, sizeof(*pass)); if (pass == NULL) { @@ -1217,7 +1238,7 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend wlr_render_pass_init(&pass->base, &render_pass_impl); pass->renderer = renderer; - pass->two_pass = !using_srgb_pathway; + pass->two_pass = using_two_pass_pathway; if (options != NULL && options->color_transform != NULL) { pass->color_transform = wlr_color_transform_ref(options->color_transform); } diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index 23f75d93e..a1a1bf6da 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -623,6 +623,7 @@ static void destroy_render_buffer(struct wlr_vk_render_buffer *buffer) { wlr_vk_error("vkQueueWaitIdle", res); } + finish_render_buffer_out(&buffer->linear.out, dev); finish_render_buffer_out(&buffer->srgb.out, dev); finish_render_buffer_out(&buffer->two_pass.out, dev); @@ -817,8 +818,8 @@ error: return false; } -static bool vulkan_setup_one_pass_framebuffer(struct wlr_vk_render_buffer *buffer, - const struct wlr_dmabuf_attributes *dmabuf) { +bool vulkan_setup_one_pass_framebuffer(struct wlr_vk_render_buffer *buffer, + const struct wlr_dmabuf_attributes *dmabuf, bool srgb) { struct wlr_vk_renderer *renderer = buffer->renderer; VkResult res; VkDevice dev = renderer->dev->dev; @@ -827,14 +828,18 @@ static bool vulkan_setup_one_pass_framebuffer(struct wlr_vk_render_buffer *buffe renderer->dev, dmabuf->format); assert(fmt); - assert(fmt->format.vk_srgb); + VkFormat vk_fmt = srgb ? fmt->format.vk_srgb : fmt->format.vk; + assert(vk_fmt != VK_FORMAT_UNDEFINED); + + struct wlr_vk_render_buffer_out *out = srgb ? &buffer->srgb.out : &buffer->linear.out; + // Set up the srgb framebuffer by default; two-pass framebuffer and // blending image will be set up later if necessary VkImageViewCreateInfo view_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .image = buffer->image, .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = fmt->format.vk_srgb, + .format = vk_fmt, .components.r = VK_COMPONENT_SWIZZLE_IDENTITY, .components.g = VK_COMPONENT_SWIZZLE_IDENTITY, .components.b = VK_COMPONENT_SWIZZLE_IDENTITY, @@ -848,35 +853,43 @@ static bool vulkan_setup_one_pass_framebuffer(struct wlr_vk_render_buffer *buffe }, }; - res = vkCreateImageView(dev, &view_info, NULL, &buffer->srgb.out.image_view); + res = vkCreateImageView(dev, &view_info, NULL, &out->image_view); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateImageView failed", res); goto error; } - buffer->srgb.render_setup = find_or_create_render_setup( - renderer, &fmt->format, false); - if (!buffer->srgb.render_setup) { + struct wlr_vk_render_format_setup *render_setup = + find_or_create_render_setup(renderer, &fmt->format, false); + if (!render_setup) { goto error; } VkFramebufferCreateInfo fb_info = { .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, .attachmentCount = 1, - .pAttachments = &buffer->srgb.out.image_view, + .pAttachments = &out->image_view, .flags = 0u, .width = dmabuf->width, .height = dmabuf->height, .layers = 1u, - .renderPass = buffer->srgb.render_setup->render_pass, + .renderPass = render_setup->render_pass, }; - res = vkCreateFramebuffer(dev, &fb_info, NULL, &buffer->srgb.out.framebuffer); + res = vkCreateFramebuffer(dev, &fb_info, NULL, &out->framebuffer); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateFramebuffer", res); goto error; } + + if (srgb) { + buffer->srgb.render_setup = render_setup; + } else { + buffer->linear.render_setup = render_setup; + } + return true; + error: // cleaning up everything is the caller's responsibility, // since it will need to do this anyway if framebuffer setup fails @@ -920,7 +933,7 @@ static struct wlr_vk_render_buffer *create_render_buffer( } if (using_mutable_srgb) { - if (!vulkan_setup_one_pass_framebuffer(buffer, &dmabuf)) { + if (!vulkan_setup_one_pass_framebuffer(buffer, &dmabuf, true)) { goto error; } } else { From aaf82ee332cf92c615a98ccd79fe3fe666a52b8f Mon Sep 17 00:00:00 2001 From: xurui Date: Thu, 7 Aug 2025 11:06:46 +0800 Subject: [PATCH 489/519] wlr_drag: drag motion signal also needs to be sent Signed-off-by: xurui --- types/data_device/wlr_drag.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/types/data_device/wlr_drag.c b/types/data_device/wlr_drag.c index b780eedac..8c17eeb68 100644 --- a/types/data_device/wlr_drag.c +++ b/types/data_device/wlr_drag.c @@ -308,6 +308,14 @@ static void drag_handle_touch_motion(struct wlr_seat_touch_grab *grab, wl_fixed_from_double(point->sx), wl_fixed_from_double(point->sy)); } + + struct wlr_drag_motion_event event = { + .drag = drag, + .time = time, + .sx = point->sx, + .sy = point->sy, + }; + wl_signal_emit_mutable(&drag->events.motion, &event); } } From 108d94f7980b0fa0d72aa1332b5169995e27f9d8 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 21 Sep 2025 23:11:12 +0200 Subject: [PATCH 490/519] Add release script This is useful to speed up the release process and avoid making mistakes. --- release.sh | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100755 release.sh diff --git a/release.sh b/release.sh new file mode 100755 index 000000000..8531a23b4 --- /dev/null +++ b/release.sh @@ -0,0 +1,32 @@ +#!/bin/sh -eu + +prev=$(git describe --tags --abbrev=0) +next=$(meson rewrite kwargs info project / | jq -r '.kwargs["project#/"].version') + +case "$next" in +*-dev) + echo "This is a development version" + exit 1 + ;; +esac + +if [ "$prev" = "$next" ]; then + echo "Version not bumped in meson.build" + exit 1 +fi + +if ! git diff-index --quiet HEAD -- meson.build; then + echo "meson.build not committed" + exit 1 +fi + +shortlog="$(git shortlog --no-merges "$prev..")" +(echo "wlroots $next"; echo ""; echo "$shortlog") | git tag "$next" -ase -F - + +prefix=wlroots-$next +archive=$prefix.tar.gz +git archive --prefix="$prefix/" -o "$archive" "$next" +gpg --output "$archive".sig --detach-sig "$archive" + +git push --follow-tags +glab release create "$next" "$archive" "$archive.sig" --notes "" From 845a7a581d2ab3df42dbd67c63af02932f9da12c Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 29 Jul 2025 19:15:12 +0200 Subject: [PATCH 491/519] color_management_v1: drop duplicated enum converters --- types/wlr_color_management_v1.c | 32 ++++---------------------------- 1 file changed, 4 insertions(+), 28 deletions(-) diff --git a/types/wlr_color_management_v1.c b/types/wlr_color_management_v1.c index 55faaacaa..ea920a122 100644 --- a/types/wlr_color_management_v1.c +++ b/types/wlr_color_management_v1.c @@ -65,18 +65,6 @@ static void resource_handle_destroy(struct wl_client *client, struct wl_resource wl_resource_destroy(resource); } -static enum wlr_color_named_primaries named_primaries_to_wlr( - enum wp_color_manager_v1_primaries primaries) { - switch (primaries) { - case WP_COLOR_MANAGER_V1_PRIMARIES_SRGB: - return WLR_COLOR_NAMED_PRIMARIES_SRGB; - case WP_COLOR_MANAGER_V1_PRIMARIES_BT2020: - return WLR_COLOR_NAMED_PRIMARIES_BT2020; - default: - abort(); - } -} - static enum wp_color_manager_v1_primaries named_primaries_from_wlr( enum wlr_color_named_primaries primaries) { switch (primaries) { @@ -88,20 +76,6 @@ static enum wp_color_manager_v1_primaries named_primaries_from_wlr( abort(); } -static enum wlr_color_transfer_function transfer_function_to_wlr( - enum wp_color_manager_v1_transfer_function tf) { - switch (tf) { - case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB: - return WLR_COLOR_TRANSFER_FUNCTION_SRGB; - case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ: - return WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ; - case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR: - return WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR; - default: - abort(); - } -} - static enum wp_color_manager_v1_transfer_function transfer_function_from_wlr( enum wlr_color_transfer_function tf) { switch (tf) { @@ -156,10 +130,12 @@ static void image_desc_handle_get_information(struct wl_client *client, } struct wlr_color_primaries primaries; - wlr_color_primaries_from_named(&primaries, named_primaries_to_wlr(image_desc->data.primaries_named)); + wlr_color_primaries_from_named(&primaries, + wlr_color_manager_v1_primaries_to_wlr(image_desc->data.primaries_named)); struct wlr_color_luminances luminances; - wlr_color_transfer_function_get_default_luminance(transfer_function_to_wlr(image_desc->data.tf_named), &luminances); + wlr_color_transfer_function_get_default_luminance( + wlr_color_manager_v1_transfer_function_to_wlr(image_desc->data.tf_named), &luminances); wp_image_description_info_v1_send_primaries_named(resource, image_desc->data.primaries_named); wp_image_description_info_v1_send_primaries(resource, From 138210f01c4f3a7de04a6a119389e34728364231 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 29 Jul 2025 19:18:10 +0200 Subject: [PATCH 492/519] color_management_v1: make from_wlr enum converters public This can be useful for compositors to set surface feedback. --- include/wlr/types/wlr_color_management_v1.h | 12 +++++ types/wlr_color_management_v1.c | 52 ++++++++++----------- 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/include/wlr/types/wlr_color_management_v1.h b/include/wlr/types/wlr_color_management_v1.h index 4a50e94e3..b24b22ef9 100644 --- a/include/wlr/types/wlr_color_management_v1.h +++ b/include/wlr/types/wlr_color_management_v1.h @@ -96,6 +96,12 @@ void wlr_color_manager_v1_set_surface_preferred_image_description( enum wlr_color_transfer_function wlr_color_manager_v1_transfer_function_to_wlr(enum wp_color_manager_v1_transfer_function tf); +/** + * Convert an enum wlr_color_transfer_function value into a protocol transfer function. + */ +enum wp_color_manager_v1_transfer_function +wlr_color_manager_v1_transfer_function_from_wlr(enum wlr_color_transfer_function tf); + /** * Convert a protocol named primaries to enum wlr_color_named_primaries. * Aborts if there is no matching wlroots entry. @@ -103,4 +109,10 @@ wlr_color_manager_v1_transfer_function_to_wlr(enum wp_color_manager_v1_transfer_ enum wlr_color_named_primaries wlr_color_manager_v1_primaries_to_wlr(enum wp_color_manager_v1_primaries primaries); +/** + * Convert an enum wlr_color_named_primaries value into protocol primaries. + */ +enum wp_color_manager_v1_primaries +wlr_color_manager_v1_primaries_from_wlr(enum wlr_color_named_primaries primaries); + #endif diff --git a/types/wlr_color_management_v1.c b/types/wlr_color_management_v1.c index ea920a122..dacbeed0c 100644 --- a/types/wlr_color_management_v1.c +++ b/types/wlr_color_management_v1.c @@ -65,30 +65,6 @@ static void resource_handle_destroy(struct wl_client *client, struct wl_resource wl_resource_destroy(resource); } -static enum wp_color_manager_v1_primaries named_primaries_from_wlr( - enum wlr_color_named_primaries primaries) { - switch (primaries) { - case WLR_COLOR_NAMED_PRIMARIES_SRGB: - return WP_COLOR_MANAGER_V1_PRIMARIES_SRGB; - case WLR_COLOR_NAMED_PRIMARIES_BT2020: - return WP_COLOR_MANAGER_V1_PRIMARIES_BT2020; - } - abort(); -} - -static enum wp_color_manager_v1_transfer_function transfer_function_from_wlr( - enum wlr_color_transfer_function tf) { - switch (tf) { - case WLR_COLOR_TRANSFER_FUNCTION_SRGB: - return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB; - case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ: - return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ; - case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR: - return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR; - } - abort(); -} - static int32_t encode_cie1931_coord(float value) { return round(value * 1000 * 1000); } @@ -241,8 +217,8 @@ static void cm_output_handle_get_image_description(struct wl_client *client, }; const struct wlr_output_image_description *image_desc = cm_output->output->image_description; if (image_desc != NULL) { - data.tf_named = transfer_function_from_wlr(image_desc->transfer_function); - data.primaries_named = named_primaries_from_wlr(image_desc->primaries); + data.tf_named = wlr_color_manager_v1_transfer_function_from_wlr(image_desc->transfer_function); + data.primaries_named = wlr_color_manager_v1_primaries_from_wlr(image_desc->primaries); } image_desc_create_ready(cm_output->manager, cm_output_resource, id, &data, true); } @@ -1019,6 +995,19 @@ wlr_color_manager_v1_transfer_function_to_wlr(enum wp_color_manager_v1_transfer_ } } +enum wp_color_manager_v1_transfer_function +wlr_color_manager_v1_transfer_function_from_wlr(enum wlr_color_transfer_function tf) { + switch (tf) { + case WLR_COLOR_TRANSFER_FUNCTION_SRGB: + return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB; + case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ: + return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ; + case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR: + return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR; + } + abort(); +} + enum wlr_color_named_primaries wlr_color_manager_v1_primaries_to_wlr(enum wp_color_manager_v1_primaries primaries) { switch (primaries) { @@ -1030,3 +1019,14 @@ wlr_color_manager_v1_primaries_to_wlr(enum wp_color_manager_v1_primaries primari abort(); } } + +enum wp_color_manager_v1_primaries +wlr_color_manager_v1_primaries_from_wlr(enum wlr_color_named_primaries primaries) { + switch (primaries) { + case WLR_COLOR_NAMED_PRIMARIES_SRGB: + return WP_COLOR_MANAGER_V1_PRIMARIES_SRGB; + case WLR_COLOR_NAMED_PRIMARIES_BT2020: + return WP_COLOR_MANAGER_V1_PRIMARIES_BT2020; + } + abort(); +} From 26c1476827dfd175fcf0e2b6e027292c78ce3f55 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 29 Jul 2025 19:29:24 +0200 Subject: [PATCH 493/519] color_management_v1: add destroy event to manager --- include/wlr/types/wlr_color_management_v1.h | 4 ++++ types/wlr_color_management_v1.c | 3 +++ 2 files changed, 7 insertions(+) diff --git a/include/wlr/types/wlr_color_management_v1.h b/include/wlr/types/wlr_color_management_v1.h index b24b22ef9..e6cb7dfbb 100644 --- a/include/wlr/types/wlr_color_management_v1.h +++ b/include/wlr/types/wlr_color_management_v1.h @@ -58,6 +58,10 @@ struct wlr_color_manager_v1_options { struct wlr_color_manager_v1 { struct wl_global *global; + struct { + struct wl_signal destroy; + } events; + struct { struct wlr_color_manager_v1_features features; diff --git a/types/wlr_color_management_v1.c b/types/wlr_color_management_v1.c index dacbeed0c..4524015fd 100644 --- a/types/wlr_color_management_v1.c +++ b/types/wlr_color_management_v1.c @@ -887,6 +887,8 @@ static void manager_bind(struct wl_client *client, void *data, static void manager_handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_color_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); + wl_signal_emit_mutable(&manager->events.destroy, NULL); + assert(wl_list_empty(&manager->events.destroy.listener_list)); wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager->render_intents); @@ -934,6 +936,7 @@ struct wlr_color_manager_v1 *wlr_color_manager_v1_create(struct wl_display *disp manager->transfer_functions_len = options->transfer_functions_len; manager->primaries_len = options->primaries_len; + wl_signal_init(&manager->events.destroy); wl_list_init(&manager->outputs); wl_list_init(&manager->surface_feedbacks); From 7cb3393e75667ca349287ad70aef3f7e2d008ec6 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 29 Jul 2025 19:29:45 +0200 Subject: [PATCH 494/519] scene: send color_management_v1 surface feedback Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3999 --- include/wlr/types/wlr_scene.h | 10 ++++++ types/scene/surface.c | 58 +++++++++++++++++++++++++++++++++++ types/scene/wlr_scene.c | 16 ++++++++++ 3 files changed, 84 insertions(+) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 0f4aa4c27..58794e8db 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -43,6 +43,7 @@ struct wlr_scene_output_layout; struct wlr_presentation; struct wlr_linux_dmabuf_v1; struct wlr_gamma_control_manager_v1; +struct wlr_color_manager_v1; struct wlr_output_state; typedef bool (*wlr_scene_buffer_point_accepts_input_func_t)( @@ -102,11 +103,13 @@ struct wlr_scene { // May be NULL struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1; struct wlr_gamma_control_manager_v1 *gamma_control_manager_v1; + struct wlr_color_manager_v1 *color_manager_v1; struct { struct wl_listener linux_dmabuf_v1_destroy; struct wl_listener gamma_control_manager_v1_destroy; struct wl_listener gamma_control_manager_v1_set_gamma; + struct wl_listener color_manager_v1_destroy; enum wlr_scene_debug_damage_option debug_damage_option; bool direct_scanout; @@ -366,6 +369,13 @@ void wlr_scene_set_linux_dmabuf_v1(struct wlr_scene *scene, void wlr_scene_set_gamma_control_manager_v1(struct wlr_scene *scene, struct wlr_gamma_control_manager_v1 *gamma_control); +/** + * Handles color_management_v1 feedback for all surfaces in the scene. + * + * Asserts that a struct wlr_color_manager_v1 hasn't already been set for the scene. + */ +void wlr_scene_set_color_manager_v1(struct wlr_scene *scene, struct wlr_color_manager_v1 *manager); + /** * Add a node displaying nothing but its children. */ diff --git a/types/scene/surface.c b/types/scene/surface.c index 135ded6fd..c798abfb3 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -35,16 +35,74 @@ static struct wlr_output *get_surface_frame_pacing_output(struct wlr_surface *su return frame_pacing_output; } +static bool get_tf_preference(enum wlr_color_transfer_function tf) { + switch (tf) { + case WLR_COLOR_TRANSFER_FUNCTION_SRGB: + return 0; + case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ: + return 1; + case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR: + return -1; + } + abort(); // unreachable +} + +static bool get_primaries_preference(enum wlr_color_named_primaries primaries) { + switch (primaries) { + case WLR_COLOR_NAMED_PRIMARIES_SRGB: + return 0; + case WLR_COLOR_NAMED_PRIMARIES_BT2020: + return 1; + } + abort(); // unreachable +} + +static void get_surface_preferred_image_description(struct wlr_surface *surface, + struct wlr_image_description_v1_data *out) { + struct wlr_output_image_description preferred = { + .transfer_function = WLR_COLOR_TRANSFER_FUNCTION_SRGB, + .primaries = WLR_COLOR_NAMED_PRIMARIES_SRGB, + }; + + struct wlr_surface_output *surface_output; + wl_list_for_each(surface_output, &surface->current_outputs, link) { + const struct wlr_output_image_description *img_desc = + surface_output->output->image_description; + if (img_desc == NULL) { + continue; + } + if (get_tf_preference(preferred.transfer_function) < get_tf_preference(img_desc->transfer_function)) { + preferred.transfer_function = img_desc->transfer_function; + } + if (get_primaries_preference(preferred.primaries) < get_primaries_preference(img_desc->primaries)) { + preferred.primaries = img_desc->primaries; + } + } + + *out = (struct wlr_image_description_v1_data){ + .tf_named = wlr_color_manager_v1_transfer_function_from_wlr(preferred.transfer_function), + .primaries_named = wlr_color_manager_v1_primaries_from_wlr(preferred.primaries), + }; +} + static void handle_scene_buffer_outputs_update( struct wl_listener *listener, void *data) { struct wlr_scene_surface *surface = wl_container_of(listener, surface, outputs_update); + struct wlr_scene *scene = scene_node_get_root(&surface->buffer->node); surface->frame_pacing_output = get_surface_frame_pacing_output(surface->surface); double scale = get_surface_preferred_buffer_scale(surface->surface); wlr_fractional_scale_v1_notify_scale(surface->surface, scale); wlr_surface_set_preferred_buffer_scale(surface->surface, ceil(scale)); + + if (scene->color_manager_v1 != NULL) { + struct wlr_image_description_v1_data img_desc = {0}; + get_surface_preferred_image_description(surface->surface, &img_desc); + wlr_color_manager_v1_set_surface_preferred_image_description(scene->color_manager_v1, + surface->surface, &img_desc); + } } static void handle_scene_buffer_output_enter( diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index d2327eda2..ac3ea3ecf 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -1562,6 +1563,21 @@ void wlr_scene_set_gamma_control_manager_v1(struct wlr_scene *scene, wl_signal_add(&gamma_control->events.set_gamma, &scene->gamma_control_manager_v1_set_gamma); } +static void scene_handle_color_manager_v1_destroy(struct wl_listener *listener, void *data) { + struct wlr_scene *scene = wl_container_of(listener, scene, color_manager_v1_destroy); + wl_list_remove(&scene->color_manager_v1_destroy.link); + wl_list_init(&scene->color_manager_v1_destroy.link); + scene->color_manager_v1 = NULL; +} + +void wlr_scene_set_color_manager_v1(struct wlr_scene *scene, struct wlr_color_manager_v1 *manager) { + assert(scene->color_manager_v1 == NULL); + scene->color_manager_v1 = manager; + + scene->color_manager_v1_destroy.notify = scene_handle_color_manager_v1_destroy; + wl_signal_add(&manager->events.destroy, &scene->color_manager_v1_destroy); +} + static void scene_output_handle_destroy(struct wlr_addon *addon) { struct wlr_scene_output *scene_output = wl_container_of(addon, scene_output, addon); From 60d72724cd740a2f9bebf56deeb645d1d4a23a30 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 30 Sep 2025 09:23:24 +0200 Subject: [PATCH 495/519] render/color: fix bounds check in lut_1d_get() i == len is out-of-bounds. Fixes: 74217a4d9341 ("render/color: introduce COLOR_TRANSFORM_LUT_3X1D") --- render/color.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/render/color.c b/render/color.c index ca8236515..ae9309dd1 100644 --- a/render/color.c +++ b/render/color.c @@ -109,7 +109,7 @@ struct wlr_color_transform_lut_3x1d *color_transform_lut_3x1d_from_base( } static float lut_1d_get(const uint16_t *lut, size_t len, size_t i) { - if (i > len) { + if (i >= len) { i = len - 1; } return (float) lut[i] / UINT16_MAX; From 3f0d338643e97de500298bbf378e340705690cce Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 26 Jun 2023 19:40:52 +0200 Subject: [PATCH 496/519] backend/wayland: log when getting disconnected from remote display It can be a bit confusing to understand why a compositor is shutting down on its own. Log a message when we get disconnected from the parent compositor to explain the cause. --- backend/wayland/backend.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/wayland/backend.c b/backend/wayland/backend.c index ff95a7b4b..fbae3a3c2 100644 --- a/backend/wayland/backend.c +++ b/backend/wayland/backend.c @@ -58,6 +58,8 @@ static int dispatch_events(int fd, uint32_t mask, void *data) { if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) { if (mask & WL_EVENT_ERROR) { wlr_log(WLR_ERROR, "Failed to read from remote Wayland display"); + } else { + wlr_log(WLR_DEBUG, "Disconnected from remote Wayland display"); } wlr_backend_destroy(&wl->backend); return 0; From d039ad8da3a92f41bbed409e685535bcceb39b33 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 26 Jun 2023 19:54:08 +0200 Subject: [PATCH 497/519] backend/wayland: continue reading on hangup If we stop immediately, we won't see any wl_display.error events. Make sure we've read everything before handling hangup. --- backend/wayland/backend.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/backend/wayland/backend.c b/backend/wayland/backend.c index fbae3a3c2..14a783b67 100644 --- a/backend/wayland/backend.c +++ b/backend/wayland/backend.c @@ -55,16 +55,6 @@ struct wlr_wl_backend *get_wl_backend_from_backend(struct wlr_backend *wlr_backe static int dispatch_events(int fd, uint32_t mask, void *data) { struct wlr_wl_backend *wl = data; - if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) { - if (mask & WL_EVENT_ERROR) { - wlr_log(WLR_ERROR, "Failed to read from remote Wayland display"); - } else { - wlr_log(WLR_DEBUG, "Disconnected from remote Wayland display"); - } - wlr_backend_destroy(&wl->backend); - return 0; - } - int count = 0; if (mask & WL_EVENT_READABLE) { count = wl_display_dispatch(wl->remote_display); @@ -77,6 +67,18 @@ static int dispatch_events(int fd, uint32_t mask, void *data) { wl_display_flush(wl->remote_display); } + // Make sure we've consumed all data before disconnecting due to hangup, + // so that we process any wl_display.error events + if (!(mask & WL_EVENT_READABLE) && (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR))) { + if (mask & WL_EVENT_ERROR) { + wlr_log(WLR_ERROR, "Failed to read from remote Wayland display"); + } else { + wlr_log(WLR_DEBUG, "Disconnected from remote Wayland display"); + } + wlr_backend_destroy(&wl->backend); + return 0; + } + if (count < 0) { wlr_log(WLR_ERROR, "Failed to dispatch remote Wayland display"); wlr_backend_destroy(&wl->backend); From 2ec4012559d69c6acff0b557e1b533ddf5ced918 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 1 Oct 2025 15:14:46 +0200 Subject: [PATCH 498/519] backend/drm: avoid error message when EDID is missing We'd attempt to parse an EDID even when the connector has no EDID, printing "Failed to parse EDID" in logs. Instead, don't attempt to parse the EDID and print a more appropriate log message. --- backend/drm/drm.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 15cb181cf..86b52c684 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -1717,7 +1717,11 @@ static bool connect_drm_connector(struct wlr_drm_connector *wlr_conn, size_t edid_len = 0; uint8_t *edid = get_drm_prop_blob(drm->fd, wlr_conn->id, wlr_conn->props.edid, &edid_len); - parse_edid(wlr_conn, edid_len, edid); + if (edid_len > 0) { + parse_edid(wlr_conn, edid_len, edid); + } else { + wlr_log(WLR_DEBUG, "Connector has no EDID"); + } free(edid); char *subconnector = NULL; From 406aa5f7f5649a9774fd89880037be3a731e96fa Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 1 Oct 2025 16:58:49 +0200 Subject: [PATCH 499/519] backend/session: fix crash on udev device remove event libwayland adds phantom listeners here: https://gitlab.freedesktop.org/wayland/wayland/-/blob/d81525a235e48cc5de3e4005a16ddb1fbdfd9d7c/src/wayland-server.c#L2378 Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3982 --- backend/session/session.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/backend/session/session.c b/backend/session/session.c index dcf07c708..48f4ab187 100644 --- a/backend/session/session.c +++ b/backend/session/session.c @@ -367,7 +367,10 @@ void wlr_session_close_file(struct wlr_session *session, } assert(wl_list_empty(&dev->events.change.listener_list)); - assert(wl_list_empty(&dev->events.remove.listener_list)); + // TODO: assert that the "remove" listener list is empty as well. Listeners + // will typically call wlr_session_close_file() in response, and + // wl_signal_emit_mutable() installs two phantom listeners, so we'd count + // these two. close(dev->fd); wl_list_remove(&dev->link); From dde07b68404a54ba0134baae1c1b429fdce6a707 Mon Sep 17 00:00:00 2001 From: llyyr Date: Thu, 2 Oct 2025 11:29:53 +0530 Subject: [PATCH 500/519] wlr_scene: fix tf/prim comparison for scanout attempt We were incorrectly doing comparison with `!= 0` to detect non-sRGB tf/primaries. Since these enums are bit flags, the default sRGB values are 1, not 0, so sRGB buffers were incorrectly rejected. Fixes: bf40f396bfd0 ("scene: grab image description from output state") --- types/scene/wlr_scene.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index ac3ea3ecf..c9454abcb 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1982,7 +1982,8 @@ static enum scene_direct_scanout_result scene_entry_try_direct_scanout( } const struct wlr_output_image_description *img_desc = output_pending_image_description(scene_output->output, state); - if (buffer->transfer_function != 0 || buffer->primaries != 0) { + if (buffer->transfer_function != WLR_COLOR_TRANSFER_FUNCTION_SRGB || + buffer->primaries != WLR_COLOR_NAMED_PRIMARIES_SRGB) { if (img_desc == NULL || img_desc->transfer_function != buffer->transfer_function || img_desc->primaries != buffer->primaries) { return false; From 22528542970687720556035790212df8d9bb30bb Mon Sep 17 00:00:00 2001 From: llyyr Date: Thu, 2 Oct 2025 13:51:41 +0530 Subject: [PATCH 501/519] wlr_scene: return scene_direct_scanout_result instead of bool --- types/scene/wlr_scene.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index c9454abcb..537cfe910 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1986,17 +1986,17 @@ static enum scene_direct_scanout_result scene_entry_try_direct_scanout( buffer->primaries != WLR_COLOR_NAMED_PRIMARIES_SRGB) { if (img_desc == NULL || img_desc->transfer_function != buffer->transfer_function || img_desc->primaries != buffer->primaries) { - return false; + return SCANOUT_INELIGIBLE; } } else if (img_desc != NULL) { - return false; + return SCANOUT_INELIGIBLE; } if (buffer->transfer_function != 0 && buffer->transfer_function != WLR_COLOR_TRANSFER_FUNCTION_SRGB) { - return false; + return SCANOUT_INELIGIBLE; } if (buffer->primaries != 0 && buffer->primaries != WLR_COLOR_NAMED_PRIMARIES_SRGB) { - return false; + return SCANOUT_INELIGIBLE; } // We want to ensure optimal buffer selection, but as direct-scanout can be enabled and disabled From 6978509f64a729984998c92301fdf83d6da7e4e1 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 3 Oct 2025 20:42:21 +0200 Subject: [PATCH 502/519] Revert "wlr_scene: fix tf/prim comparison for scanout attempt" This reverts commit dde07b68404a54ba0134baae1c1b429fdce6a707. This is incorrect as discussed here: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5163#note_3118744 --- types/scene/wlr_scene.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 537cfe910..9efbe58ad 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1982,8 +1982,7 @@ static enum scene_direct_scanout_result scene_entry_try_direct_scanout( } const struct wlr_output_image_description *img_desc = output_pending_image_description(scene_output->output, state); - if (buffer->transfer_function != WLR_COLOR_TRANSFER_FUNCTION_SRGB || - buffer->primaries != WLR_COLOR_NAMED_PRIMARIES_SRGB) { + if (buffer->transfer_function != 0 || buffer->primaries != 0) { if (img_desc == NULL || img_desc->transfer_function != buffer->transfer_function || img_desc->primaries != buffer->primaries) { return SCANOUT_INELIGIBLE; From c2d9ae21425ae968f4491a73ea024df1338c52cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Poisot?= Date: Sun, 17 Aug 2025 15:51:26 +0000 Subject: [PATCH 503/519] render: introduce Gamma 2.2 color transform --- backend/drm/atomic.c | 2 ++ include/render/vulkan.h | 3 +++ include/wlr/render/color.h | 1 + render/vulkan/pass.c | 9 ++++++++- render/vulkan/renderer.c | 6 ++++++ render/vulkan/shaders/output.frag | 3 +++ render/vulkan/shaders/texture.frag | 3 +++ types/scene/surface.c | 1 + types/wlr_color_management_v1.c | 4 ++++ 9 files changed, 31 insertions(+), 1 deletion(-) diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index 33ce6f42d..7b4b4636a 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -186,6 +186,8 @@ static uint8_t convert_cta861_eotf(enum wlr_color_transfer_function tf) { return 2; case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR: abort(); // unsupported + case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22: + abort(); // unsupported } abort(); // unreachable } diff --git a/include/render/vulkan.h b/include/render/vulkan.h index 33d158dee..498661070 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -153,6 +153,7 @@ enum wlr_vk_texture_transform { WLR_VK_TEXTURE_TRANSFORM_IDENTITY = 0, WLR_VK_TEXTURE_TRANSFORM_SRGB = 1, WLR_VK_TEXTURE_TRANSFORM_ST2084_PQ = 2, + WLR_VK_TEXTURE_TRANSFORM_GAMMA22 = 3, }; enum wlr_vk_shader_source { @@ -167,6 +168,7 @@ enum wlr_vk_output_transform { WLR_VK_OUTPUT_TRANSFORM_INVERSE_SRGB = 1, WLR_VK_OUTPUT_TRANSFORM_INVERSE_ST2084_PQ = 2, WLR_VK_OUTPUT_TRANSFORM_LUT3D = 3, + WLR_VK_OUTPUT_TRANSFORM_INVERSE_GAMMA22 = 4, }; struct wlr_vk_pipeline_key { @@ -199,6 +201,7 @@ struct wlr_vk_render_format_setup { VkPipeline output_pipe_srgb; VkPipeline output_pipe_pq; VkPipeline output_pipe_lut3d; + VkPipeline output_pipe_gamma22; struct wlr_vk_renderer *renderer; struct wl_list pipelines; // struct wlr_vk_pipeline.link diff --git a/include/wlr/render/color.h b/include/wlr/render/color.h index e2397b75a..7d5b26857 100644 --- a/include/wlr/render/color.h +++ b/include/wlr/render/color.h @@ -28,6 +28,7 @@ enum wlr_color_transfer_function { WLR_COLOR_TRANSFER_FUNCTION_SRGB = 1 << 0, WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ = 1 << 1, WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR = 1 << 2, + WLR_COLOR_TRANSFER_FUNCTION_GAMMA22 = 1 << 3, }; /** diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 4cbb3c26c..5555f1197 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -246,6 +246,9 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ: pipeline = render_buffer->two_pass.render_setup->output_pipe_pq; break; + case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22: + pipeline = render_buffer->two_pass.render_setup->output_pipe_gamma22; + break; } struct wlr_color_luminances srgb_lum, dst_lum; @@ -796,6 +799,9 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ: tex_transform = WLR_VK_TEXTURE_TRANSFORM_ST2084_PQ; break; + case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22: + tex_transform = WLR_VK_TEXTURE_TRANSFORM_GAMMA22; + break; } struct wlr_vk_pipeline *pipe = setup_get_or_create_pipeline( @@ -840,7 +846,8 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, } float luminance_multiplier = 1; - if (tf != WLR_COLOR_TRANSFER_FUNCTION_SRGB) { + if (tf != WLR_COLOR_TRANSFER_FUNCTION_SRGB + && tf != WLR_COLOR_TRANSFER_FUNCTION_GAMMA22) { struct wlr_color_luminances src_lum, srgb_lum; wlr_color_transfer_function_get_default_luminance(tf, &src_lum); wlr_color_transfer_function_get_default_luminance( diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index a1a1bf6da..ba14bc2ba 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -175,6 +175,7 @@ static void destroy_render_format_setup(struct wlr_vk_renderer *renderer, vkDestroyPipeline(dev, setup->output_pipe_srgb, NULL); vkDestroyPipeline(dev, setup->output_pipe_pq, NULL); vkDestroyPipeline(dev, setup->output_pipe_lut3d, NULL); + vkDestroyPipeline(dev, setup->output_pipe_gamma22, NULL); struct wlr_vk_pipeline *pipeline, *tmp_pipeline; wl_list_for_each_safe(pipeline, tmp_pipeline, &setup->pipelines, link) { @@ -2345,6 +2346,11 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup( &setup->output_pipe_pq, WLR_VK_OUTPUT_TRANSFORM_INVERSE_ST2084_PQ)) { goto error; } + if (!init_blend_to_output_pipeline( + renderer, setup->render_pass, renderer->output_pipe_layout, + &setup->output_pipe_gamma22, WLR_VK_OUTPUT_TRANSFORM_INVERSE_GAMMA22)) { + goto error; + } } else { assert(format->vk_srgb); VkAttachmentDescription attachment = { diff --git a/render/vulkan/shaders/output.frag b/render/vulkan/shaders/output.frag index 3d5ac4089..9785fd225 100644 --- a/render/vulkan/shaders/output.frag +++ b/render/vulkan/shaders/output.frag @@ -22,6 +22,7 @@ layout (constant_id = 0) const int OUTPUT_TRANSFORM = 0; #define OUTPUT_TRANSFORM_INVERSE_SRGB 1 #define OUTPUT_TRANSFORM_INVERSE_ST2084_PQ 2 #define OUTPUT_TRANSFORM_LUT_3D 3 +#define OUTPUT_TRANSFORM_INVERSE_GAMMA22 4 float linear_channel_to_srgb(float x) { return max(min(x * 12.92, 0.04045), 1.055 * pow(x, 1. / 2.4) - 0.055); @@ -71,6 +72,8 @@ void main() { } else if (OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_INVERSE_SRGB) { // Produce sRGB encoded values rgb = linear_color_to_srgb(rgb); + } else if (OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_INVERSE_GAMMA22) { + rgb = pow(rgb, vec3(1. / 2.2)); } // Back to pre-multiplied alpha diff --git a/render/vulkan/shaders/texture.frag b/render/vulkan/shaders/texture.frag index 2a7e2c517..bb73681f5 100644 --- a/render/vulkan/shaders/texture.frag +++ b/render/vulkan/shaders/texture.frag @@ -18,6 +18,7 @@ layout (constant_id = 0) const int TEXTURE_TRANSFORM = 0; #define TEXTURE_TRANSFORM_IDENTITY 0 #define TEXTURE_TRANSFORM_SRGB 1 #define TEXTURE_TRANSFORM_ST2084_PQ 2 +#define TEXTURE_TRANSFORM_GAMMA22 3 float srgb_channel_to_linear(float x) { return mix(x / 12.92, @@ -60,6 +61,8 @@ void main() { rgb = srgb_color_to_linear(rgb); } else if (TEXTURE_TRANSFORM == TEXTURE_TRANSFORM_ST2084_PQ) { rgb = pq_color_to_linear(rgb); + } else if (TEXTURE_TRANSFORM == TEXTURE_TRANSFORM_GAMMA22) { + rgb = pow(rgb, vec3(2.2)); } rgb *= data.luminance_multiplier; diff --git a/types/scene/surface.c b/types/scene/surface.c index c798abfb3..aebc00ea2 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -42,6 +42,7 @@ static bool get_tf_preference(enum wlr_color_transfer_function tf) { case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ: return 1; case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR: + case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22: return -1; } abort(); // unreachable diff --git a/types/wlr_color_management_v1.c b/types/wlr_color_management_v1.c index 4524015fd..54c0a44af 100644 --- a/types/wlr_color_management_v1.c +++ b/types/wlr_color_management_v1.c @@ -993,6 +993,8 @@ wlr_color_manager_v1_transfer_function_to_wlr(enum wp_color_manager_v1_transfer_ return WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ; case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR: return WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR; + case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22: + return WLR_COLOR_TRANSFER_FUNCTION_GAMMA22; default: abort(); } @@ -1007,6 +1009,8 @@ wlr_color_manager_v1_transfer_function_from_wlr(enum wlr_color_transfer_function return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ; case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR; + case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22: + return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22; } abort(); } From d8fb7adcf041af7e958804b77c9a6669fbff4efd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Poisot?= Date: Sun, 17 Aug 2025 15:57:16 +0000 Subject: [PATCH 504/519] scene, render: use Gamma 2.2 TF as default --- include/wlr/render/pass.h | 5 +++-- render/vulkan/pass.c | 11 ++++------- types/scene/surface.c | 8 ++++---- types/scene/wlr_scene.c | 2 +- types/wlr_color_management_v1.c | 4 ++-- 5 files changed, 14 insertions(+), 16 deletions(-) diff --git a/include/wlr/render/pass.h b/include/wlr/render/pass.h index 8e22bdf8f..1785ee562 100644 --- a/include/wlr/render/pass.h +++ b/include/wlr/render/pass.h @@ -31,8 +31,9 @@ struct wlr_render_timer; struct wlr_buffer_pass_options { /* Timer to measure the duration of the render pass */ struct wlr_render_timer *timer; - /* Color transform to apply to the output of the render pass, - * leave NULL to indicate sRGB/no custom transform */ + /* Color transform to apply to the output of the render pass. + * Leave NULL to indicate the default transform (Gamma 2.2 encoding for + * sRGB monitors) */ struct wlr_color_transform *color_transform; /** Primaries describing the color volume of the destination buffer */ const struct wlr_color_primaries *primaries; diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 5555f1197..4cce62eb5 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -57,10 +57,7 @@ static void convert_pixman_box_to_vk_rect(const pixman_box32_t *box, VkRect2D *r } static float color_to_linear(float non_linear) { - // See https://www.w3.org/Graphics/Color/srgb - return (non_linear > 0.04045) ? - pow((non_linear + 0.055) / 1.055, 2.4) : - non_linear / 12.92; + return pow(non_linear, 2.2); } static float color_to_linear_premult(float non_linear, float alpha) { @@ -229,7 +226,7 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { if (pass->color_transform && pass->color_transform->type != COLOR_TRANSFORM_INVERSE_EOTF) { pipeline = render_buffer->two_pass.render_setup->output_pipe_lut3d; } else { - enum wlr_color_transfer_function tf = WLR_COLOR_TRANSFER_FUNCTION_SRGB; + enum wlr_color_transfer_function tf = WLR_COLOR_TRANSFER_FUNCTION_GAMMA22; if (pass->color_transform && pass->color_transform->type == COLOR_TRANSFORM_INVERSE_EOTF) { struct wlr_color_transform_inverse_eotf *inverse_eotf = wlr_color_transform_inverse_eotf_from_base(pass->color_transform); @@ -779,7 +776,7 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, enum wlr_color_transfer_function tf = options->transfer_function; if (tf == 0) { - tf = WLR_COLOR_TRANSFER_FUNCTION_SRGB; + tf = WLR_COLOR_TRANSFER_FUNCTION_GAMMA22; } bool srgb_image_view = false; @@ -1186,7 +1183,7 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend } } else { // This is the default when unspecified - inv_eotf = WLR_COLOR_TRANSFER_FUNCTION_SRGB; + inv_eotf = WLR_COLOR_TRANSFER_FUNCTION_GAMMA22; } bool using_linear_pathway = inv_eotf == WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR; diff --git a/types/scene/surface.c b/types/scene/surface.c index aebc00ea2..593537163 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -37,12 +37,12 @@ static struct wlr_output *get_surface_frame_pacing_output(struct wlr_surface *su static bool get_tf_preference(enum wlr_color_transfer_function tf) { switch (tf) { - case WLR_COLOR_TRANSFER_FUNCTION_SRGB: + case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22: return 0; case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ: return 1; + case WLR_COLOR_TRANSFER_FUNCTION_SRGB: case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR: - case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22: return -1; } abort(); // unreachable @@ -61,7 +61,7 @@ static bool get_primaries_preference(enum wlr_color_named_primaries primaries) { static void get_surface_preferred_image_description(struct wlr_surface *surface, struct wlr_image_description_v1_data *out) { struct wlr_output_image_description preferred = { - .transfer_function = WLR_COLOR_TRANSFER_FUNCTION_SRGB, + .transfer_function = WLR_COLOR_TRANSFER_FUNCTION_GAMMA22, .primaries = WLR_COLOR_NAMED_PRIMARIES_SRGB, }; @@ -249,7 +249,7 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { opacity = (float)alpha_modifier_state->multiplier; } - enum wlr_color_transfer_function tf = WLR_COLOR_TRANSFER_FUNCTION_SRGB; + enum wlr_color_transfer_function tf = WLR_COLOR_TRANSFER_FUNCTION_GAMMA22; enum wlr_color_named_primaries primaries = WLR_COLOR_NAMED_PRIMARIES_SRGB; const struct wlr_image_description_v1_data *img_desc = wlr_surface_get_image_description_v1_data(surface); diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 9efbe58ad..3d9f96fac 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1991,7 +1991,7 @@ static enum scene_direct_scanout_result scene_entry_try_direct_scanout( return SCANOUT_INELIGIBLE; } - if (buffer->transfer_function != 0 && buffer->transfer_function != WLR_COLOR_TRANSFER_FUNCTION_SRGB) { + if (buffer->transfer_function != 0 && buffer->transfer_function != WLR_COLOR_TRANSFER_FUNCTION_GAMMA22) { return SCANOUT_INELIGIBLE; } if (buffer->primaries != 0 && buffer->primaries != WLR_COLOR_NAMED_PRIMARIES_SRGB) { diff --git a/types/wlr_color_management_v1.c b/types/wlr_color_management_v1.c index 54c0a44af..80c357539 100644 --- a/types/wlr_color_management_v1.c +++ b/types/wlr_color_management_v1.c @@ -212,7 +212,7 @@ static void cm_output_handle_get_image_description(struct wl_client *client, } struct wlr_image_description_v1_data data = { - .tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB, + .tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22, .primaries_named = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB, }; const struct wlr_output_image_description *image_desc = cm_output->output->image_description; @@ -777,7 +777,7 @@ static void manager_handle_get_surface_feedback(struct wl_client *client, surface_feedback->surface = surface; surface_feedback->data = (struct wlr_image_description_v1_data){ - .tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB, + .tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22, .primaries_named = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB, }; From 6e1c8748ff4e0737133b11259e8fd4e47ab3c795 Mon Sep 17 00:00:00 2001 From: llyyr Date: Sat, 4 Oct 2025 14:48:42 +0530 Subject: [PATCH 505/519] render: introduce bt.1886 transfer function --- backend/drm/atomic.c | 2 ++ include/render/vulkan.h | 3 +++ include/wlr/render/color.h | 1 + render/color.c | 7 +++++++ render/vulkan/pass.c | 6 ++++++ render/vulkan/renderer.c | 6 ++++++ render/vulkan/shaders/output.frag | 11 +++++++++++ render/vulkan/shaders/texture.frag | 11 +++++++++++ types/scene/surface.c | 1 + types/wlr_color_management_v1.c | 4 ++++ 10 files changed, 52 insertions(+) diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index 7b4b4636a..41773d4f5 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -188,6 +188,8 @@ static uint8_t convert_cta861_eotf(enum wlr_color_transfer_function tf) { abort(); // unsupported case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22: abort(); // unsupported + case WLR_COLOR_TRANSFER_FUNCTION_BT1886: + abort(); // unsupported } abort(); // unreachable } diff --git a/include/render/vulkan.h b/include/render/vulkan.h index 498661070..5358b01c1 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -154,6 +154,7 @@ enum wlr_vk_texture_transform { WLR_VK_TEXTURE_TRANSFORM_SRGB = 1, WLR_VK_TEXTURE_TRANSFORM_ST2084_PQ = 2, WLR_VK_TEXTURE_TRANSFORM_GAMMA22 = 3, + WLR_VK_TEXTURE_TRANSFORM_BT1886 = 4, }; enum wlr_vk_shader_source { @@ -169,6 +170,7 @@ enum wlr_vk_output_transform { WLR_VK_OUTPUT_TRANSFORM_INVERSE_ST2084_PQ = 2, WLR_VK_OUTPUT_TRANSFORM_LUT3D = 3, WLR_VK_OUTPUT_TRANSFORM_INVERSE_GAMMA22 = 4, + WLR_VK_OUTPUT_TRANSFORM_INVERSE_BT1886 = 5, }; struct wlr_vk_pipeline_key { @@ -202,6 +204,7 @@ struct wlr_vk_render_format_setup { VkPipeline output_pipe_pq; VkPipeline output_pipe_lut3d; VkPipeline output_pipe_gamma22; + VkPipeline output_pipe_bt1886; struct wlr_vk_renderer *renderer; struct wl_list pipelines; // struct wlr_vk_pipeline.link diff --git a/include/wlr/render/color.h b/include/wlr/render/color.h index 7d5b26857..d906e3425 100644 --- a/include/wlr/render/color.h +++ b/include/wlr/render/color.h @@ -29,6 +29,7 @@ enum wlr_color_transfer_function { WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ = 1 << 1, WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR = 1 << 2, WLR_COLOR_TRANSFER_FUNCTION_GAMMA22 = 1 << 3, + WLR_COLOR_TRANSFER_FUNCTION_BT1886 = 1 << 4, }; /** diff --git a/render/color.c b/render/color.c index ae9309dd1..da2f938f8 100644 --- a/render/color.c +++ b/render/color.c @@ -202,6 +202,13 @@ void wlr_color_transfer_function_get_default_luminance(enum wlr_color_transfer_f .reference = 203, }; break; + case WLR_COLOR_TRANSFER_FUNCTION_BT1886: + *lum = (struct wlr_color_luminances){ + .min = 0.01, + .max = 100, + .reference = 100, + }; + break; default: *lum = (struct wlr_color_luminances){ .min = 0.2, diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 4cce62eb5..9e212aacc 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -246,6 +246,9 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22: pipeline = render_buffer->two_pass.render_setup->output_pipe_gamma22; break; + case WLR_COLOR_TRANSFER_FUNCTION_BT1886: + pipeline = render_buffer->two_pass.render_setup->output_pipe_bt1886; + break; } struct wlr_color_luminances srgb_lum, dst_lum; @@ -799,6 +802,9 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22: tex_transform = WLR_VK_TEXTURE_TRANSFORM_GAMMA22; break; + case WLR_COLOR_TRANSFER_FUNCTION_BT1886: + tex_transform = WLR_VK_TEXTURE_TRANSFORM_BT1886; + break; } struct wlr_vk_pipeline *pipe = setup_get_or_create_pipeline( diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index ba14bc2ba..3f880ca6c 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -176,6 +176,7 @@ static void destroy_render_format_setup(struct wlr_vk_renderer *renderer, vkDestroyPipeline(dev, setup->output_pipe_pq, NULL); vkDestroyPipeline(dev, setup->output_pipe_lut3d, NULL); vkDestroyPipeline(dev, setup->output_pipe_gamma22, NULL); + vkDestroyPipeline(dev, setup->output_pipe_bt1886, NULL); struct wlr_vk_pipeline *pipeline, *tmp_pipeline; wl_list_for_each_safe(pipeline, tmp_pipeline, &setup->pipelines, link) { @@ -2351,6 +2352,11 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup( &setup->output_pipe_gamma22, WLR_VK_OUTPUT_TRANSFORM_INVERSE_GAMMA22)) { goto error; } + if (!init_blend_to_output_pipeline( + renderer, setup->render_pass, renderer->output_pipe_layout, + &setup->output_pipe_bt1886, WLR_VK_OUTPUT_TRANSFORM_INVERSE_BT1886)) { + goto error; + } } else { assert(format->vk_srgb); VkAttachmentDescription attachment = { diff --git a/render/vulkan/shaders/output.frag b/render/vulkan/shaders/output.frag index 9785fd225..b9cfcaaa2 100644 --- a/render/vulkan/shaders/output.frag +++ b/render/vulkan/shaders/output.frag @@ -23,6 +23,7 @@ layout (constant_id = 0) const int OUTPUT_TRANSFORM = 0; #define OUTPUT_TRANSFORM_INVERSE_ST2084_PQ 2 #define OUTPUT_TRANSFORM_LUT_3D 3 #define OUTPUT_TRANSFORM_INVERSE_GAMMA22 4 +#define OUTPUT_TRANSFORM_INVERSE_BT1886 5 float linear_channel_to_srgb(float x) { return max(min(x * 12.92, 0.04045), 1.055 * pow(x, 1. / 2.4) - 0.055); @@ -47,6 +48,14 @@ vec3 linear_color_to_pq(vec3 color) { return pow((vec3(c1) + c2 * pow_n) / (vec3(1) + c3 * pow_n), vec3(m)); } +vec3 linear_color_to_bt1886(vec3 color) { + float lb = pow(0.0001, 1.0 / 2.4); + float lw = pow(1.0, 1.0 / 2.4); + float a = pow(lw - lb, 2.4); + float b = lb / (lw - lb); + return pow(color / a, vec3(1.0 / 2.4)) - vec3(b); +} + void main() { vec4 in_color = subpassLoad(in_color).rgba; @@ -74,6 +83,8 @@ void main() { rgb = linear_color_to_srgb(rgb); } else if (OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_INVERSE_GAMMA22) { rgb = pow(rgb, vec3(1. / 2.2)); + } else if (OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_INVERSE_BT1886) { + rgb = linear_color_to_bt1886(rgb); } // Back to pre-multiplied alpha diff --git a/render/vulkan/shaders/texture.frag b/render/vulkan/shaders/texture.frag index bb73681f5..b7b78b19a 100644 --- a/render/vulkan/shaders/texture.frag +++ b/render/vulkan/shaders/texture.frag @@ -19,6 +19,7 @@ layout (constant_id = 0) const int TEXTURE_TRANSFORM = 0; #define TEXTURE_TRANSFORM_SRGB 1 #define TEXTURE_TRANSFORM_ST2084_PQ 2 #define TEXTURE_TRANSFORM_GAMMA22 3 +#define TEXTURE_TRANSFORM_BT1886 4 float srgb_channel_to_linear(float x) { return mix(x / 12.92, @@ -45,6 +46,14 @@ vec3 pq_color_to_linear(vec3 color) { return pow(num / denom, vec3(inv_m1)); } +vec3 bt1886_color_to_linear(vec3 color) { + float lb = pow(0.0001, 1.0 / 2.4); + float lw = pow(1.0, 1.0 / 2.4); + float a = pow(lw - lb, 2.4); + float b = lb / (lw - lb); + return a * pow(color + vec3(b), vec3(2.4)); +} + void main() { vec4 in_color = textureLod(tex, uv, 0); @@ -63,6 +72,8 @@ void main() { rgb = pq_color_to_linear(rgb); } else if (TEXTURE_TRANSFORM == TEXTURE_TRANSFORM_GAMMA22) { rgb = pow(rgb, vec3(2.2)); + } else if (TEXTURE_TRANSFORM == TEXTURE_TRANSFORM_BT1886) { + rgb = bt1886_color_to_linear(rgb); } rgb *= data.luminance_multiplier; diff --git a/types/scene/surface.c b/types/scene/surface.c index 593537163..68a445b98 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -41,6 +41,7 @@ static bool get_tf_preference(enum wlr_color_transfer_function tf) { return 0; case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ: return 1; + case WLR_COLOR_TRANSFER_FUNCTION_BT1886: case WLR_COLOR_TRANSFER_FUNCTION_SRGB: case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR: return -1; diff --git a/types/wlr_color_management_v1.c b/types/wlr_color_management_v1.c index 80c357539..c69a69c81 100644 --- a/types/wlr_color_management_v1.c +++ b/types/wlr_color_management_v1.c @@ -995,6 +995,8 @@ wlr_color_manager_v1_transfer_function_to_wlr(enum wp_color_manager_v1_transfer_ return WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR; case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22: return WLR_COLOR_TRANSFER_FUNCTION_GAMMA22; + case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_BT1886: + return WLR_COLOR_TRANSFER_FUNCTION_BT1886; default: abort(); } @@ -1011,6 +1013,8 @@ wlr_color_manager_v1_transfer_function_from_wlr(enum wlr_color_transfer_function return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR; case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22; + case WLR_COLOR_TRANSFER_FUNCTION_BT1886: + return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_BT1886; } abort(); } From 5529aae3e65aa182d88cecc1efa4b10a20b553eb Mon Sep 17 00:00:00 2001 From: llyyr Date: Sun, 5 Oct 2025 22:56:33 +0530 Subject: [PATCH 506/519] wlr_scene: fix direct scanout for gamma2.2 buffers Fixes incorrectly rejecting scanout for gamma2.2 buffers when the output has no image description set. This happens on `hdr off` mode on sway. Also refactor the scanout check into its own function while at it to make it easier to follow. --- types/scene/wlr_scene.c | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 3d9f96fac..a06b641e3 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1925,6 +1925,27 @@ static void scene_buffer_send_dmabuf_feedback(const struct wlr_scene *scene, wlr_linux_dmabuf_feedback_v1_finish(&feedback); } +static bool color_management_is_scanout_allowed(const struct wlr_output_image_description *img_desc, + const struct wlr_scene_buffer *buffer) { + // Disallow scanout if the output has colorimetry information but buffer + // doesn't; allow it only if the output also lacks it. + if (buffer->transfer_function == 0 && buffer->primaries == 0) { + return img_desc == NULL; + } + + // If the output has colorimetry information, the buffer must match it for + // direct scanout to be allowed. + if (img_desc != NULL) { + return img_desc->transfer_function == buffer->transfer_function && + img_desc->primaries == buffer->primaries; + } + // If the output doesn't have colorimetry image description set, we can only + // scan out buffers with default colorimetry (gamma2.2 transfer and sRGB + // primaries) used in wlroots. + return buffer->transfer_function == WLR_COLOR_TRANSFER_FUNCTION_GAMMA22 && + buffer->primaries == WLR_COLOR_NAMED_PRIMARIES_SRGB; +} + enum scene_direct_scanout_result { // This scene node is not a candidate for scanout SCANOUT_INELIGIBLE, @@ -1982,19 +2003,7 @@ static enum scene_direct_scanout_result scene_entry_try_direct_scanout( } const struct wlr_output_image_description *img_desc = output_pending_image_description(scene_output->output, state); - if (buffer->transfer_function != 0 || buffer->primaries != 0) { - if (img_desc == NULL || img_desc->transfer_function != buffer->transfer_function || - img_desc->primaries != buffer->primaries) { - return SCANOUT_INELIGIBLE; - } - } else if (img_desc != NULL) { - return SCANOUT_INELIGIBLE; - } - - if (buffer->transfer_function != 0 && buffer->transfer_function != WLR_COLOR_TRANSFER_FUNCTION_GAMMA22) { - return SCANOUT_INELIGIBLE; - } - if (buffer->primaries != 0 && buffer->primaries != WLR_COLOR_NAMED_PRIMARIES_SRGB) { + if (!color_management_is_scanout_allowed(img_desc, buffer)) { return SCANOUT_INELIGIBLE; } From 03e7966650c29cf8e563c8fcacc1ae1edf2f3efa Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 16 Oct 2025 11:04:25 +0200 Subject: [PATCH 507/519] ci: fix VKMS lookup after faux bus migration VKMS has been migrated to the new faux bus. This causes breakage in CI, because we used the platform bus to find the right device. udev hasn't been updated yet to support the faux bus, so just use sysfs instead. --- .builds/archlinux.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml index 8457ed585..fae04ab31 100644 --- a/.builds/archlinux.yml +++ b/.builds/archlinux.yml @@ -41,9 +41,10 @@ tasks: cd wlroots/build-gcc/tinywl sudo modprobe vkms udevadm settle + card="/dev/dri/$(ls /sys/devices/faux/vkms/drm/ | grep ^card)" export WLR_BACKENDS=drm export WLR_RENDERER=pixman - export WLR_DRM_DEVICES=/dev/dri/by-path/platform-vkms-card + export WLR_DRM_DEVICES="$card" export UBSAN_OPTIONS=halt_on_error=1 - sudo chmod ugo+rw /dev/dri/by-path/platform-vkms-card + sudo chmod ugo+rw "$card" sudo -E seatd-launch -- ./tinywl -s 'kill $PPID' || [ $? = 143 ] From 06275103f249cd2954630e59383342e102a6c1a3 Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Thu, 9 Oct 2025 20:02:32 -0400 Subject: [PATCH 508/519] input-method-v2: Destroy keyboard grab before input method Fixes race condition in where the keyboard grab tries to reference the input manager after it's been set to null. --- types/wlr_input_method_v2.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/types/wlr_input_method_v2.c b/types/wlr_input_method_v2.c index 0a521df48..a9501654c 100644 --- a/types/wlr_input_method_v2.c +++ b/types/wlr_input_method_v2.c @@ -56,6 +56,7 @@ static void input_method_destroy(struct wlr_input_method_v2 *input_method) { popup_surface, tmp, &input_method->popup_surfaces, link) { popup_surface_destroy(popup_surface); } + wlr_input_method_keyboard_grab_v2_destroy(input_method->keyboard_grab); wl_signal_emit_mutable(&input_method->events.destroy, NULL); assert(wl_list_empty(&input_method->events.commit.listener_list)); @@ -65,7 +66,6 @@ static void input_method_destroy(struct wlr_input_method_v2 *input_method) { wl_list_remove(wl_resource_get_link(input_method->resource)); wl_list_remove(&input_method->seat_client_destroy.link); - wlr_input_method_keyboard_grab_v2_destroy(input_method->keyboard_grab); input_state_reset(&input_method->pending); input_state_reset(&input_method->current); free(input_method); @@ -271,8 +271,7 @@ void wlr_input_method_keyboard_grab_v2_destroy( if (!keyboard_grab) { return; } - wl_signal_emit_mutable(&keyboard_grab->events.destroy, keyboard_grab); - + wl_signal_emit_mutable(&keyboard_grab->events.destroy, NULL); assert(wl_list_empty(&keyboard_grab->events.destroy.listener_list)); keyboard_grab->input_method->keyboard_grab = NULL; From 19c5d22beb1af30e5fcd831751f404caafdcd2f5 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Tue, 23 Sep 2025 23:44:52 +0900 Subject: [PATCH 509/519] util/box.c: use 1/256 instead of 1/65536 in wlr_box_closest_point() This fixes the issue that a scrollbar in a maximized GTK/Chromium window cannot be dragged when cursor is on the right/bottom edge of the output. The issue was caused by rounding in `wl_fixed_from_double()` ([1]); if `wlr_cursor_move()` constrains the x-position of the cursor to `(output width)-1/65536`, `wl_fixed_from_double()` converts it to just `(output width)`, which is perceived as outside of the window by GTK/Chromium. Using 1/256 (minimal unit of `wl_fixed_t`) instead of 1/65536 avoids this rounding issue. [1]: https://gitlab.freedesktop.org/wayland/wayland/-/commit/f246e619d17deb92f414315d1747a9b7aca659b9 --- util/box.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/util/box.c b/util/box.c index a9b42579a..aae09888f 100644 --- a/util/box.c +++ b/util/box.c @@ -19,16 +19,15 @@ void wlr_box_closest_point(const struct wlr_box *box, double x, double y, // // In order to be consistent with e.g. wlr_box_contains_point(), // this function returns a point inside the bottom and right edges - // of the box by at least 1/65536 of a unit (pixel). 1/65536 is + // of the box by at least 1/256 of a unit (pixel). 1/256 is // small enough to avoid a "dead zone" with high-resolution mice - // but large enough to avoid rounding to zero (due to loss of - // significant digits) in simple floating-point calculations. + // but large enough to avoid rounding to zero in wl_fixed_from_double(). // find the closest x point if (x < box->x) { *dest_x = box->x; - } else if (x > box->x + box->width - 1/65536.0) { - *dest_x = box->x + box->width - 1/65536.0; + } else if (x > box->x + box->width - 1/256.0) { + *dest_x = box->x + box->width - 1/256.0; } else { *dest_x = x; } @@ -36,8 +35,8 @@ void wlr_box_closest_point(const struct wlr_box *box, double x, double y, // find closest y point if (y < box->y) { *dest_y = box->y; - } else if (y > box->y + box->height - 1/65536.0) { - *dest_y = box->y + box->height - 1/65536.0; + } else if (y > box->y + box->height - 1/256.0) { + *dest_y = box->y + box->height - 1/256.0; } else { *dest_y = y; } From 6d63871f059192b15d2fa0dfacfb391709f4952d Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 16 Oct 2025 10:42:20 +0200 Subject: [PATCH 510/519] linux_drm_syncobj_v1: fix use-after-free in surface_commit_destroy() surface_commit_destroy() accesses a field from struct wlr_linux_drm_syncobj_surface_v1, however that struct may have been free'd earlier: ==1103==ERROR: AddressSanitizer: heap-use-after-free on address 0x7cdef7a6e288 at pc 0x7feefaac335a bp 0x7ffc4de8f570 sp 0x7ffc4de8f560 READ of size 8 at 0x7cdef7a6e288 thread T0 #0 0x7feefaac3359 in surface_commit_destroy ../subprojects/wlroots/types/wlr_linux_drm_syncobj_v1.c:195 #1 0x7feefaac34cd in surface_commit_handle_surface_destroy ../subprojects/wlroots/types/wlr_linux_drm_syncobj_v1.c:211 #2 0x7feefbd194cf in wl_signal_emit_mutable (/usr/lib/libwayland-server.so.0+0x84cf) (BuildId: b9664217748f523995e3f855fa197cf8e59942d1) #3 0x7feefaa52b22 in surface_handle_resource_destroy ../subprojects/wlroots/types/wlr_compositor.c:730 #4 0x7feefbd1bb9f (/usr/lib/libwayland-server.so.0+0xab9f) (BuildId: b9664217748f523995e3f855fa197cf8e59942d1) #5 0x7feefaa46a18 in surface_handle_destroy ../subprojects/wlroots/types/wlr_compositor.c:65 #6 0x7feef89afac5 (/usr/lib/libffi.so.8+0x7ac5) (BuildId: d5e3b0d8921923f35438adefa9f864745abc5e90) #7 0x7feef89ac76a (/usr/lib/libffi.so.8+0x476a) (BuildId: d5e3b0d8921923f35438adefa9f864745abc5e90) #8 0x7feef89af06d in ffi_call (/usr/lib/libffi.so.8+0x706d) (BuildId: d5e3b0d8921923f35438adefa9f864745abc5e90) #9 0x7feefbd17531 (/usr/lib/libwayland-server.so.0+0x6531) (BuildId: b9664217748f523995e3f855fa197cf8e59942d1) #10 0x7feefbd1cd2f (/usr/lib/libwayland-server.so.0+0xbd2f) (BuildId: b9664217748f523995e3f855fa197cf8e59942d1) #11 0x7feefbd1b181 in wl_event_loop_dispatch (/usr/lib/libwayland-server.so.0+0xa181) (BuildId: b9664217748f523995e3f855fa197cf8e59942d1) #12 0x7feefbd1d296 in wl_display_run (/usr/lib/libwayland-server.so.0+0xc296) (BuildId: b9664217748f523995e3f855fa197cf8e59942d1) #13 0x555bf0a55a40 in server_run ../sway/server.c:615 #14 0x555bf0a4a654 in main ../sway/main.c:376 #15 0x7feef9227674 (/usr/lib/libc.so.6+0x27674) (BuildId: 4fe011c94a88e8aeb6f2201b9eb369f42b4a1e9e) #16 0x7feef9227728 in __libc_start_main (/usr/lib/libc.so.6+0x27728) (BuildId: 4fe011c94a88e8aeb6f2201b9eb369f42b4a1e9e) #17 0x555bf0a03f54 in _start (/home/leo/code/stuff/sway/build/sway/sway+0x390f54) (BuildId: e3d4e653af1aa0885f0426c403e16fc87c086d33) 0x7cdef7a6e288 is located 8 bytes inside of 176-byte region [0x7cdef7a6e280,0x7cdef7a6e330) freed by thread T0 here: #0 0x7feefb71f79d in free /usr/src/debug/gcc/gcc/libsanitizer/asan/asan_malloc_linux.cpp:51 #1 0x7feefaac29f1 in surface_destroy ../subprojects/wlroots/types/wlr_linux_drm_syncobj_v1.c:84 #2 0x7feefaac2e47 in surface_handle_resource_destroy ../subprojects/wlroots/types/wlr_linux_drm_syncobj_v1.c:143 #3 0x7feefbd1bb9f (/usr/lib/libwayland-server.so.0+0xab9f) (BuildId: b9664217748f523995e3f855fa197cf8e59942d1) #4 0x7feefaac2a12 in surface_handle_destroy ../subprojects/wlroots/types/wlr_linux_drm_syncobj_v1.c:89 #5 0x7feef89afac5 (/usr/lib/libffi.so.8+0x7ac5) (BuildId: d5e3b0d8921923f35438adefa9f864745abc5e90) previously allocated by thread T0 here: #0 0x7feefb7205dd in calloc /usr/src/debug/gcc/gcc/libsanitizer/asan/asan_malloc_linux.cpp:74 #1 0x7feefaac4abd in manager_handle_get_surface ../subprojects/wlroots/types/wlr_linux_drm_syncobj_v1.c:313 #2 0x7feef89afac5 (/usr/lib/libffi.so.8+0x7ac5) (BuildId: d5e3b0d8921923f35438adefa9f864745abc5e90) Fix this by storing the struct wlr_surface in the field. Closes: https://github.com/swaywm/sway/issues/8917 --- types/wlr_linux_drm_syncobj_v1.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/types/wlr_linux_drm_syncobj_v1.c b/types/wlr_linux_drm_syncobj_v1.c index 7873a09c4..988d44e01 100644 --- a/types/wlr_linux_drm_syncobj_v1.c +++ b/types/wlr_linux_drm_syncobj_v1.c @@ -26,7 +26,7 @@ struct wlr_linux_drm_syncobj_surface_v1 { }; struct wlr_linux_drm_syncobj_surface_v1_commit { - struct wlr_linux_drm_syncobj_surface_v1 *surface; + struct wlr_surface *surface; struct wlr_drm_syncobj_timeline_waiter waiter; uint32_t cached_seq; @@ -192,7 +192,7 @@ static struct wlr_linux_drm_syncobj_surface_v1 *surface_from_wlr_surface( } static void surface_commit_destroy(struct wlr_linux_drm_syncobj_surface_v1_commit *commit) { - wlr_surface_unlock_cached(commit->surface->surface, commit->cached_seq); + wlr_surface_unlock_cached(commit->surface, commit->cached_seq); wl_list_remove(&commit->surface_destroy.link); wlr_drm_syncobj_timeline_waiter_finish(&commit->waiter); free(commit); @@ -237,7 +237,7 @@ static bool lock_surface_commit(struct wlr_linux_drm_syncobj_surface_v1 *surface return false; } - commit->surface = surface; + commit->surface = surface->surface; commit->cached_seq = wlr_surface_lock_pending(surface->surface); commit->surface_destroy.notify = surface_commit_handle_surface_destroy; From d786e07899481dd970025ffef09a18eb726cd41d Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Tue, 14 Oct 2025 17:46:50 -0400 Subject: [PATCH 511/519] backend/session: use device `boot_display` shouldn't need to check for `boot_vga` if newer, more general sysfs `boot_display` is set. closes https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/4016 --- backend/session/session.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/backend/session/session.c b/backend/session/session.c index 48f4ab187..32522fb42 100644 --- a/backend/session/session.c +++ b/backend/session/session.c @@ -519,8 +519,6 @@ ssize_t wlr_session_find_gpus(struct wlr_session *session, break; } - bool is_boot_vga = false; - const char *path = udev_list_entry_get_name(entry); struct udev_device *dev = udev_device_new_from_syspath(session->udev, path); if (!dev) { @@ -536,14 +534,20 @@ ssize_t wlr_session_find_gpus(struct wlr_session *session, continue; } - // This is owned by 'dev', so we don't need to free it - struct udev_device *pci = - udev_device_get_parent_with_subsystem_devtype(dev, "pci", NULL); + bool is_primary = false; + const char *boot_display = udev_device_get_sysattr_value(dev, "boot_display"); + if (boot_display && strcmp(boot_display, "1") == 0) { + is_primary = true; + } else { + // This is owned by 'dev', so we don't need to free it + struct udev_device *pci = + udev_device_get_parent_with_subsystem_devtype(dev, "pci", NULL); - if (pci) { - const char *id = udev_device_get_sysattr_value(pci, "boot_vga"); - if (id && strcmp(id, "1") == 0) { - is_boot_vga = true; + if (pci) { + const char *id = udev_device_get_sysattr_value(pci, "boot_vga"); + if (id && strcmp(id, "1") == 0) { + is_primary = true; + } } } @@ -557,7 +561,7 @@ ssize_t wlr_session_find_gpus(struct wlr_session *session, udev_device_unref(dev); ret[i] = wlr_dev; - if (is_boot_vga) { + if (is_primary) { struct wlr_device *tmp = ret[0]; ret[0] = ret[i]; ret[i] = tmp; From 3d36ab921114e17f2538d8260876c62786115b1a Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 15 Jun 2025 15:00:21 +0200 Subject: [PATCH 512/519] render/color: add wlr_color_transform_eval() Makes it so the Vulkan renderer can handle arbitrary color transforms, and doesn't need to be updated each time a new one is added. --- include/render/color.h | 6 --- include/wlr/render/color.h | 6 +++ render/color.c | 76 +++++++++++++++++++++++++++++++++++++- render/vulkan/pass.c | 19 +--------- 4 files changed, 82 insertions(+), 25 deletions(-) diff --git a/include/render/color.h b/include/render/color.h index 8730ac6e9..5dc6481ab 100644 --- a/include/render/color.h +++ b/include/render/color.h @@ -72,12 +72,6 @@ struct wlr_color_transform_inverse_eotf *wlr_color_transform_inverse_eotf_from_b struct wlr_color_transform_lut_3x1d *color_transform_lut_3x1d_from_base( struct wlr_color_transform *tr); -/** - * Evaluate a 3x1D LUT color transform for a given RGB triplet. - */ -void color_transform_lut_3x1d_eval(struct wlr_color_transform_lut_3x1d *tr, - float out[static 3], const float in[static 3]); - /** * Obtain primaries values from a well-known primaries name. */ diff --git a/include/wlr/render/color.h b/include/wlr/render/color.h index d906e3425..5f2ad36b5 100644 --- a/include/wlr/render/color.h +++ b/include/wlr/render/color.h @@ -152,4 +152,10 @@ struct wlr_color_transform *wlr_color_transform_ref(struct wlr_color_transform * */ void wlr_color_transform_unref(struct wlr_color_transform *tr); +/** + * Evaluate a color transform for a given RGB triplet. + */ +void wlr_color_transform_eval(struct wlr_color_transform *tr, + float out[static 3], const float in[static 3]); + #endif diff --git a/render/color.c b/render/color.c index da2f938f8..287cda76f 100644 --- a/render/color.c +++ b/render/color.c @@ -108,6 +108,65 @@ struct wlr_color_transform_lut_3x1d *color_transform_lut_3x1d_from_base( return lut_3x1d; } +static float srgb_eval_inverse_eotf(float x) { + // See https://www.w3.org/Graphics/Color/srgb + if (x <= 0.0031308) { + return 12.92 * x; + } else { + return 1.055 * powf(x, 1.0 / 2.4) - 0.055; + } +} + +static float st2084_pq_eval_inverse_eotf(float x) { + // H.273 TransferCharacteristics code point 16 + float c1 = 0.8359375; + float c2 = 18.8515625; + float c3 = 18.6875; + float m = 78.84375; + float n = 0.1593017578125; + if (x < 0) { + x = 0; + } + if (x > 1) { + x = 1; + } + float pow_n = powf(x, n); + return powf((c1 + c2 * pow_n) / (1 + c3 * pow_n), m); +} + +static float bt1886_eval_inverse_eotf(float x) { + float lb = powf(0.0001, 1.0 / 2.4); + float lw = powf(1.0, 1.0 / 2.4); + float a = powf(lw - lb, 2.4); + float b = lb / (lw - lb); + return powf(x / a, 1.0 / 2.4) - b; +} + +static float transfer_function_eval_inverse_eotf( + enum wlr_color_transfer_function tf, float x) { + switch (tf) { + case WLR_COLOR_TRANSFER_FUNCTION_SRGB: + return srgb_eval_inverse_eotf(x); + case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ: + return st2084_pq_eval_inverse_eotf(x); + case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR: + return x; + case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22: + return powf(x, 1.0 / 2.2); + case WLR_COLOR_TRANSFER_FUNCTION_BT1886: + return bt1886_eval_inverse_eotf(x); + } + abort(); // unreachable +} + +static void color_transform_inverse_eotf_eval( + struct wlr_color_transform_inverse_eotf *tr, + float out[static 3], const float in[static 3]) { + for (size_t i = 0; i < 3; i++) { + out[i] = transfer_function_eval_inverse_eotf(tr->tf, in[i]); + } +} + static float lut_1d_get(const uint16_t *lut, size_t len, size_t i) { if (i >= len) { i = len - 1; @@ -125,13 +184,28 @@ static float lut_1d_eval(const uint16_t *lut, size_t len, float x) { return a * (1 - frac_part) + b * frac_part; } -void color_transform_lut_3x1d_eval(struct wlr_color_transform_lut_3x1d *tr, +static void color_transform_lut_3x1d_eval(struct wlr_color_transform_lut_3x1d *tr, float out[static 3], const float in[static 3]) { for (size_t i = 0; i < 3; i++) { out[i] = lut_1d_eval(&tr->lut_3x1d[tr->dim * i], tr->dim, in[i]); } } +void wlr_color_transform_eval(struct wlr_color_transform *tr, + float out[static 3], const float in[static 3]) { + switch (tr->type) { + case COLOR_TRANSFORM_INVERSE_EOTF: + color_transform_inverse_eotf_eval(wlr_color_transform_inverse_eotf_from_base(tr), out, in); + break; + case COLOR_TRANSFORM_LCMS2: + color_transform_lcms2_eval(color_transform_lcms2_from_base(tr), out, in); + break; + case COLOR_TRANSFORM_LUT_3X1D: + color_transform_lut_3x1d_eval(color_transform_lut_3x1d_from_base(tr), out, in); + break; + } +} + void wlr_color_primaries_from_named(struct wlr_color_primaries *out, enum wlr_color_named_primaries named) { switch (named) { diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 9e212aacc..398ee2104 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -964,19 +964,6 @@ static bool create_3d_lut_image(struct wlr_vk_renderer *renderer, *ds = VK_NULL_HANDLE; *ds_pool = NULL; - struct wlr_color_transform_lcms2 *tr_lcms2 = NULL; - struct wlr_color_transform_lut_3x1d *tr_lut_3x1d = NULL; - switch (tr->type) { - case COLOR_TRANSFORM_INVERSE_EOTF: - abort(); // unreachable - case COLOR_TRANSFORM_LCMS2: - tr_lcms2 = color_transform_lcms2_from_base(tr); - break; - case COLOR_TRANSFORM_LUT_3X1D: - tr_lut_3x1d = color_transform_lut_3x1d_from_base(tr); - break; - } - // R32G32B32 is not a required Vulkan format // TODO: use it when available VkFormat format = VK_FORMAT_R32G32B32A32_SFLOAT; @@ -1074,11 +1061,7 @@ static bool create_3d_lut_image(struct wlr_vk_renderer *renderer, b_index * sample_range, }; float rgb_out[3]; - if (tr_lcms2 != NULL) { - color_transform_lcms2_eval(tr_lcms2, rgb_out, rgb_in); - } else { - color_transform_lut_3x1d_eval(tr_lut_3x1d, rgb_out, rgb_in); - } + wlr_color_transform_eval(tr, rgb_out, rgb_in); dst[dst_offset] = rgb_out[0]; dst[dst_offset + 1] = rgb_out[1]; From 0b58bddf1370a173d53bc2ed51c7b46294252c5c Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 15 Jun 2025 15:03:18 +0200 Subject: [PATCH 513/519] render/color: add wlr_color_transform_pipeline Useful to apply multiple transforms in sequence, e.g. sRGB inverse EOTF followed by gamma LUTs. --- include/render/color.h | 8 +++++++ include/wlr/render/color.h | 7 ++++++ render/color.c | 45 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/include/render/color.h b/include/render/color.h index 5dc6481ab..128b345e1 100644 --- a/include/render/color.h +++ b/include/render/color.h @@ -9,6 +9,7 @@ enum wlr_color_transform_type { COLOR_TRANSFORM_INVERSE_EOTF, COLOR_TRANSFORM_LCMS2, COLOR_TRANSFORM_LUT_3X1D, + COLOR_TRANSFORM_PIPELINE, }; struct wlr_color_transform { @@ -39,6 +40,13 @@ struct wlr_color_transform_lut_3x1d { size_t dim; }; +struct wlr_color_transform_pipeline { + struct wlr_color_transform base; + + struct wlr_color_transform **transforms; + size_t len; +}; + void wlr_color_transform_init(struct wlr_color_transform *tr, enum wlr_color_transform_type type); diff --git a/include/wlr/render/color.h b/include/wlr/render/color.h index 5f2ad36b5..bc3baf181 100644 --- a/include/wlr/render/color.h +++ b/include/wlr/render/color.h @@ -141,6 +141,13 @@ struct wlr_color_transform *wlr_color_transform_init_linear_to_inverse_eotf( struct wlr_color_transform *wlr_color_transform_init_lut_3x1d(size_t dim, const uint16_t *r, const uint16_t *g, const uint16_t *b); +/** + * Initialize a color transformation to apply a sequence of color transforms + * one after another. + */ +struct wlr_color_transform *wlr_color_transform_init_pipeline( + struct wlr_color_transform **transforms, size_t len); + /** * Increase the reference count of the color transform by 1. */ diff --git a/render/color.c b/render/color.c index 287cda76f..0a1a67be3 100644 --- a/render/color.c +++ b/render/color.c @@ -62,6 +62,33 @@ struct wlr_color_transform *wlr_color_transform_init_lut_3x1d(size_t dim, return &tx->base; } +struct wlr_color_transform *wlr_color_transform_init_pipeline( + struct wlr_color_transform **transforms, size_t len) { + assert(len > 0); + + struct wlr_color_transform **copy = calloc(len, sizeof(copy[0])); + if (copy == NULL) { + return NULL; + } + + struct wlr_color_transform_pipeline *tx = calloc(1, sizeof(*tx)); + if (!tx) { + free(copy); + return NULL; + } + wlr_color_transform_init(&tx->base, COLOR_TRANSFORM_PIPELINE); + + // TODO: flatten nested pipeline transforms + for (size_t i = 0; i < len; i++) { + copy[i] = wlr_color_transform_ref(transforms[i]); + } + + tx->transforms = copy; + tx->len = len; + + return &tx->base; +} + static void color_transform_destroy(struct wlr_color_transform *tr) { switch (tr->type) { case COLOR_TRANSFORM_INVERSE_EOTF: @@ -73,6 +100,14 @@ static void color_transform_destroy(struct wlr_color_transform *tr) { struct wlr_color_transform_lut_3x1d *lut_3x1d = color_transform_lut_3x1d_from_base(tr); free(lut_3x1d->lut_3x1d); break; + case COLOR_TRANSFORM_PIPELINE:; + struct wlr_color_transform_pipeline *pipeline = + wl_container_of(tr, pipeline, base); + for (size_t i = 0; i < pipeline->len; i++) { + wlr_color_transform_unref(pipeline->transforms[i]); + } + free(pipeline->transforms); + break; } wlr_addon_set_finish(&tr->addons); free(tr); @@ -203,6 +238,16 @@ void wlr_color_transform_eval(struct wlr_color_transform *tr, case COLOR_TRANSFORM_LUT_3X1D: color_transform_lut_3x1d_eval(color_transform_lut_3x1d_from_base(tr), out, in); break; + case COLOR_TRANSFORM_PIPELINE:; + struct wlr_color_transform_pipeline *pipeline = + wl_container_of(tr, pipeline, base); + float color[3]; + memcpy(color, in, sizeof(color)); + for (size_t i = 0; i < pipeline->len; i++) { + wlr_color_transform_eval(pipeline->transforms[i], color, color); + } + memcpy(out, color, sizeof(color)); + break; } } From 74ce6c22a54f28abcaaef743d739da78fb853e85 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 23 Jun 2025 09:15:10 +0200 Subject: [PATCH 514/519] output: check for color transform no-op changes This allows callers to always set this state and not care whether the output currently has the same color transform applied. --- include/wlr/types/wlr_output.h | 1 + types/output/output.c | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 6a0dd0455..2ae11a4d3 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -267,6 +267,7 @@ struct wlr_output { struct { struct wl_listener display_destroy; struct wlr_output_image_description image_description_value; + struct wlr_color_transform *color_transform; } WLR_PRIVATE; }; diff --git a/types/output/output.c b/types/output/output.c index 1fb0347a0..ca2e55538 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -242,6 +242,15 @@ static void output_apply_state(struct wlr_output *output, } } + if (state->committed & WLR_OUTPUT_STATE_COLOR_TRANSFORM) { + wlr_color_transform_unref(output->color_transform); + if (state->color_transform != NULL) { + output->color_transform = wlr_color_transform_ref(state->color_transform); + } else { + output->color_transform = NULL; + } + } + bool geometry_updated = state->committed & (WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_TRANSFORM | WLR_OUTPUT_STATE_SUBPIXEL); @@ -407,6 +416,7 @@ void wlr_output_finish(struct wlr_output *output) { wlr_swapchain_destroy(output->cursor_swapchain); wlr_buffer_unlock(output->cursor_front_buffer); + wlr_color_transform_unref(output->color_transform); wlr_swapchain_destroy(output->swapchain); @@ -515,8 +525,7 @@ const struct wlr_output_image_description *output_pending_image_description( * Returns a bitfield of the unchanged fields. * * Some fields are not checked: damage always changes in-between frames, the - * gamma LUT is too expensive to check, the contents of the buffer might have - * changed, etc. + * contents of the buffer might have changed, etc. */ static uint32_t output_compare_state(struct wlr_output *output, const struct wlr_output_state *state) { @@ -562,6 +571,10 @@ static uint32_t output_compare_state(struct wlr_output *output, output->subpixel == state->subpixel) { fields |= WLR_OUTPUT_STATE_SUBPIXEL; } + if ((state->committed & WLR_OUTPUT_STATE_COLOR_TRANSFORM) && + output->color_transform == state->color_transform) { + fields |= WLR_OUTPUT_STATE_COLOR_TRANSFORM; + } return fields; } From 91f4890ec27baab6f0df598e842cdf127cc6304c Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 2 Jul 2025 18:28:06 +0200 Subject: [PATCH 515/519] gamma_control_v1: add wlr_gamma_control_v1_get_color_transform() --- include/wlr/types/wlr_gamma_control_v1.h | 2 ++ types/wlr_gamma_control_v1.c | 19 ++++++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/include/wlr/types/wlr_gamma_control_v1.h b/include/wlr/types/wlr_gamma_control_v1.h index b3a70f11e..4b0948964 100644 --- a/include/wlr/types/wlr_gamma_control_v1.h +++ b/include/wlr/types/wlr_gamma_control_v1.h @@ -49,6 +49,8 @@ struct wlr_gamma_control_v1 *wlr_gamma_control_manager_v1_get_control( struct wlr_gamma_control_manager_v1 *manager, struct wlr_output *output); bool wlr_gamma_control_v1_apply(struct wlr_gamma_control_v1 *gamma_control, struct wlr_output_state *output_state); +struct wlr_color_transform *wlr_gamma_control_v1_get_color_transform( + struct wlr_gamma_control_v1 *gamma_control); void wlr_gamma_control_v1_send_failed_and_destroy(struct wlr_gamma_control_v1 *gamma_control); #endif diff --git a/types/wlr_gamma_control_v1.c b/types/wlr_gamma_control_v1.c index 207d1977f..04d73c2e5 100644 --- a/types/wlr_gamma_control_v1.c +++ b/types/wlr_gamma_control_v1.c @@ -262,15 +262,24 @@ struct wlr_gamma_control_v1 *wlr_gamma_control_manager_v1_get_control( return NULL; } +struct wlr_color_transform *wlr_gamma_control_v1_get_color_transform( + struct wlr_gamma_control_v1 *gamma_control) { + if (gamma_control == NULL || gamma_control->table == NULL) { + return NULL; + } + + const uint16_t *r = gamma_control->table; + const uint16_t *g = gamma_control->table + gamma_control->ramp_size; + const uint16_t *b = gamma_control->table + 2 * gamma_control->ramp_size; + + return wlr_color_transform_init_lut_3x1d(gamma_control->ramp_size, r, g, b); +} + bool wlr_gamma_control_v1_apply(struct wlr_gamma_control_v1 *gamma_control, struct wlr_output_state *output_state) { struct wlr_color_transform *tr = NULL; if (gamma_control != NULL && gamma_control->table != NULL) { - const uint16_t *r = gamma_control->table; - const uint16_t *g = gamma_control->table + gamma_control->ramp_size; - const uint16_t *b = gamma_control->table + 2 * gamma_control->ramp_size; - - tr = wlr_color_transform_init_lut_3x1d(gamma_control->ramp_size, r, g, b); + tr = wlr_gamma_control_v1_get_color_transform(gamma_control); if (tr == NULL) { return false; } From 3e08e3be4a02122dd3b0bebe965ab0d410f08a01 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 5 Oct 2025 21:22:07 +0200 Subject: [PATCH 516/519] gamma_control_v1: introduce fallback_gamma_size --- include/wlr/types/wlr_gamma_control_v1.h | 5 +++++ types/wlr_gamma_control_v1.c | 3 +++ 2 files changed, 8 insertions(+) diff --git a/include/wlr/types/wlr_gamma_control_v1.h b/include/wlr/types/wlr_gamma_control_v1.h index 4b0948964..7a6df98a9 100644 --- a/include/wlr/types/wlr_gamma_control_v1.h +++ b/include/wlr/types/wlr_gamma_control_v1.h @@ -10,6 +10,11 @@ struct wlr_gamma_control_manager_v1 { struct wl_global *global; struct wl_list controls; // wlr_gamma_control_v1.link + // Fallback to use when an struct wlr_output doesn't support gamma LUTs. + // Can be used to apply gamma LUTs via a struct wlr_renderer. Leave zero to + // indicate that the fallback is unsupported. + size_t fallback_gamma_size; + struct { struct wl_signal destroy; struct wl_signal set_gamma; // struct wlr_gamma_control_manager_v1_set_gamma_event diff --git a/types/wlr_gamma_control_v1.c b/types/wlr_gamma_control_v1.c index 04d73c2e5..42d14799d 100644 --- a/types/wlr_gamma_control_v1.c +++ b/types/wlr_gamma_control_v1.c @@ -157,6 +157,9 @@ static void gamma_control_manager_get_gamma_control(struct wl_client *client, } size_t gamma_size = wlr_output_get_gamma_size(output); + if (gamma_size == 0) { + gamma_size = manager->fallback_gamma_size; + } if (gamma_size == 0) { zwlr_gamma_control_v1_send_failed(resource); return; From 989cffe70d32db9fc2b5a776abd4767aabe430f6 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 5 Oct 2025 21:21:32 +0200 Subject: [PATCH 517/519] scene: add software fallback for gamma LUT --- include/wlr/types/wlr_scene.h | 5 +++ types/scene/wlr_scene.c | 79 +++++++++++++++++++++++++++++++---- 2 files changed, 77 insertions(+), 7 deletions(-) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 58794e8db..9658a02b4 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -252,6 +252,11 @@ struct wlr_scene_output { bool gamma_lut_changed; struct wlr_gamma_control_v1 *gamma_lut; + struct wlr_color_transform *gamma_lut_color_transform; + + struct wlr_color_transform *prev_gamma_lut_color_transform; + struct wlr_color_transform *prev_supplied_color_transform; + struct wlr_color_transform *prev_combined_color_transform; struct wl_listener output_commit; struct wl_listener output_damage; diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index a06b641e3..c5cbcd29f 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1530,6 +1530,8 @@ static void scene_handle_gamma_control_manager_v1_set_gamma(struct wl_listener * output->gamma_lut_changed = true; output->gamma_lut = event->control; + wlr_color_transform_unref(output->gamma_lut_color_transform); + output->gamma_lut_color_transform = wlr_gamma_control_v1_get_color_transform(event->control); wlr_output_schedule_frame(output->output); } @@ -1547,6 +1549,8 @@ static void scene_handle_gamma_control_manager_v1_destroy(struct wl_listener *li wl_list_for_each(output, &scene->outputs, link) { output->gamma_lut_changed = false; output->gamma_lut = NULL; + wlr_color_transform_unref(output->gamma_lut_color_transform); + output->gamma_lut_color_transform = NULL; } } @@ -1766,6 +1770,10 @@ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) { wl_list_remove(&scene_output->output_damage.link); wl_list_remove(&scene_output->output_needs_frame.link); wlr_drm_syncobj_timeline_unref(scene_output->in_timeline); + wlr_color_transform_unref(scene_output->gamma_lut_color_transform); + wlr_color_transform_unref(scene_output->prev_gamma_lut_color_transform); + wlr_color_transform_unref(scene_output->prev_supplied_color_transform); + wlr_color_transform_unref(scene_output->prev_combined_color_transform); wl_array_release(&scene_output->render_list); free(scene_output); } @@ -2104,16 +2112,15 @@ static void scene_output_state_attempt_gamma(struct wlr_scene_output *scene_outp return; } - if (!wlr_gamma_control_v1_apply(scene_output->gamma_lut, &gamma_pending)) { - wlr_output_state_finish(&gamma_pending); - return; - } - + wlr_output_state_set_color_transform(&gamma_pending, scene_output->gamma_lut_color_transform); scene_output->gamma_lut_changed = false; + if (!wlr_output_test_state(scene_output->output, &gamma_pending)) { wlr_gamma_control_v1_send_failed_and_destroy(scene_output->gamma_lut); scene_output->gamma_lut = NULL; + wlr_color_transform_unref(scene_output->gamma_lut_color_transform); + scene_output->gamma_lut_color_transform = NULL; wlr_output_state_finish(&gamma_pending); return; } @@ -2122,6 +2129,41 @@ static void scene_output_state_attempt_gamma(struct wlr_scene_output *scene_outp wlr_output_state_finish(&gamma_pending); } +static struct wlr_color_transform *scene_output_combine_color_transforms( + struct wlr_scene_output *scene_output, struct wlr_color_transform *supplied) { + struct wlr_color_transform *gamma_lut = scene_output->gamma_lut_color_transform; + assert(gamma_lut != NULL); + + if (gamma_lut == scene_output->prev_gamma_lut_color_transform && + supplied == scene_output->prev_supplied_color_transform) { + return wlr_color_transform_ref(scene_output->prev_combined_color_transform); + } + + struct wlr_color_transform *combined; + if (supplied == NULL) { + combined = wlr_color_transform_ref(gamma_lut); + } else { + struct wlr_color_transform *transforms[] = { + gamma_lut, + supplied, + }; + size_t transforms_len = sizeof(transforms) / sizeof(transforms[0]); + combined = wlr_color_transform_init_pipeline(transforms, transforms_len); + if (combined == NULL) { + return NULL; + } + } + + wlr_color_transform_unref(scene_output->prev_gamma_lut_color_transform); + scene_output->prev_gamma_lut_color_transform = wlr_color_transform_ref(gamma_lut); + wlr_color_transform_unref(scene_output->prev_supplied_color_transform); + scene_output->prev_supplied_color_transform = supplied ? wlr_color_transform_ref(supplied) : NULL; + wlr_color_transform_unref(scene_output->prev_combined_color_transform); + scene_output->prev_combined_color_transform = wlr_color_transform_ref(combined); + + return combined; +} + bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, struct wlr_output_state *state, const struct wlr_scene_output_state_options *options) { struct wlr_scene_output_state_options default_options = {0}; @@ -2145,6 +2187,16 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, enum wlr_scene_debug_damage_option debug_damage = scene_output->scene->debug_damage_option; + bool render_gamma_lut = false; + if (wlr_output_get_gamma_size(output) == 0 && output->renderer->features.output_color_transform) { + if (scene_output->gamma_lut_color_transform != scene_output->prev_gamma_lut_color_transform) { + scene_output_damage_whole(scene_output); + } + if (scene_output->gamma_lut_color_transform != NULL) { + render_gamma_lut = true; + } + } + struct render_data render_data = { .transform = output->transform, .scale = output->scale, @@ -2245,7 +2297,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, // - There are no color transforms that need to be applied // - Damage highlight debugging is not enabled enum scene_direct_scanout_result scanout_result = SCANOUT_INELIGIBLE; - if (options->color_transform == NULL && list_len == 1 + if (options->color_transform == NULL && !render_gamma_lut && list_len == 1 && debug_damage != WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) { scanout_result = scene_entry_try_direct_scanout(&list_data[0], state, &render_data); } @@ -2319,6 +2371,17 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, color_transform = wlr_color_transform_ref(options->color_transform); } + if (render_gamma_lut) { + struct wlr_color_transform *combined = + scene_output_combine_color_transforms(scene_output, color_transform); + wlr_color_transform_unref(color_transform); + if (combined == NULL) { + wlr_buffer_unlock(buffer); + return false; + } + color_transform = combined; + } + scene_output->in_point++; struct wlr_render_pass *render_pass = wlr_renderer_begin_buffer_pass(output->renderer, buffer, &(struct wlr_buffer_pass_options){ @@ -2441,7 +2504,9 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, scene_output->in_point); } - scene_output_state_attempt_gamma(scene_output, state); + if (!render_gamma_lut) { + scene_output_state_attempt_gamma(scene_output, state); + } return true; } From 879243e370de6167d2c49510396f937b1a93fab5 Mon Sep 17 00:00:00 2001 From: David Turner Date: Mon, 20 Oct 2025 13:55:00 +0100 Subject: [PATCH 518/519] xwm: Fix double-close When an FD is passed to xcb_connect_to_fd(), xcb takes ownership of that FD and is responsible for closing it, which it does when xcb_disconnect() is called. But the xwayland handler code also keeps a copy of the FD and closes it via safe_close() in server_finish_process(). This double-close can cause all sorts of problems if another part of wlroots allocates another FD between the two closes - the latter close will close the wrong FD and things go horribly wrong (in my case leading to use-after-free and segfaults). Fix this by setting wm_fd[0]=-1 after calling xwm_create(), and ensuring that xwm_create() closes the FD if startup errors occur. --- include/xwayland/xwm.h | 1 + xwayland/xwayland.c | 3 +++ xwayland/xwm.c | 3 +++ 3 files changed, 7 insertions(+) diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h index e460bbb63..73f440d29 100644 --- a/include/xwayland/xwm.h +++ b/include/xwayland/xwm.h @@ -164,6 +164,7 @@ struct wlr_xwm { struct wl_listener drop_focus_destroy; }; +// xwm_create takes ownership of wm_fd and will close it under all circumstances. struct wlr_xwm *xwm_create(struct wlr_xwayland *wlr_xwayland, int wm_fd); void xwm_destroy(struct wlr_xwm *xwm); diff --git a/xwayland/xwayland.c b/xwayland/xwayland.c index 5d51df074..3aa47bac2 100644 --- a/xwayland/xwayland.c +++ b/xwayland/xwayland.c @@ -42,6 +42,9 @@ static void handle_server_start(struct wl_listener *listener, void *data) { static void xwayland_mark_ready(struct wlr_xwayland *xwayland) { assert(xwayland->server->wm_fd[0] >= 0); xwayland->xwm = xwm_create(xwayland, xwayland->server->wm_fd[0]); + // xwm_create takes ownership of wm_fd[0] under all circumstances + xwayland->server->wm_fd[0] = -1; + if (!xwayland->xwm) { return; } diff --git a/xwayland/xwm.c b/xwayland/xwm.c index a82e8b145..2bb4e4c64 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -2530,6 +2530,7 @@ void xwm_set_cursor(struct wlr_xwm *xwm, const uint8_t *pixels, uint32_t stride, struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) { struct wlr_xwm *xwm = calloc(1, sizeof(*xwm)); if (xwm == NULL) { + close(wm_fd); return NULL; } @@ -2544,11 +2545,13 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) { xwm->ping_timeout = 10000; + // xcb_connect_to_fd takes ownership of the FD regardless of success/failure xwm->xcb_conn = xcb_connect_to_fd(wm_fd, NULL); int rc = xcb_connection_has_error(xwm->xcb_conn); if (rc) { wlr_log(WLR_ERROR, "xcb connect failed: %d", rc); + xcb_disconnect(xwm->xcb_conn); free(xwm); return NULL; } From 604fcdb1db36d8bad0ada101d17c2d45e7510017 Mon Sep 17 00:00:00 2001 From: llyyr Date: Mon, 6 Oct 2025 08:10:49 +0530 Subject: [PATCH 519/519] render/vulkan: clip negative values before applying transfer function Not all eotf or eotf inverse are well defined for values outside the intended domain, so just ignore it and clamp it away. An alternative solution would be to use sign preserving pow here (i.e. sign(x) * pow(abs(x), p)), however I'm not sure that makes sense or is defined anywhere. Negative values here are likely a result of colors being outside the gamut range, so clipping them to 0 is more correct than mirroring from 0. --- render/vulkan/shaders/output.frag | 3 +++ render/vulkan/shaders/texture.frag | 3 +++ 2 files changed, 6 insertions(+) diff --git a/render/vulkan/shaders/output.frag b/render/vulkan/shaders/output.frag index b9cfcaaa2..5b952fcff 100644 --- a/render/vulkan/shaders/output.frag +++ b/render/vulkan/shaders/output.frag @@ -72,6 +72,9 @@ void main() { rgb = mat3(data.matrix) * rgb; + if (OUTPUT_TRANSFORM != OUTPUT_TRANSFORM_IDENTITY) { + rgb = max(rgb, vec3(0)); + } if (OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_LUT_3D) { // Apply 3D LUT vec3 pos = data.lut_3d_offset + rgb * data.lut_3d_scale; diff --git a/render/vulkan/shaders/texture.frag b/render/vulkan/shaders/texture.frag index b7b78b19a..57d2d049c 100644 --- a/render/vulkan/shaders/texture.frag +++ b/render/vulkan/shaders/texture.frag @@ -66,6 +66,9 @@ void main() { rgb = in_color.rgb / alpha; } + if (TEXTURE_TRANSFORM != TEXTURE_TRANSFORM_IDENTITY) { + rgb = max(rgb, vec3(0)); + } if (TEXTURE_TRANSFORM == TEXTURE_TRANSFORM_SRGB) { rgb = srgb_color_to_linear(rgb); } else if (TEXTURE_TRANSFORM == TEXTURE_TRANSFORM_ST2084_PQ) {