From b33774db14ff3c66b4a5477bd633c343deec4fee Mon Sep 17 00:00:00 2001 From: lkn Date: Sat, 6 Jun 2026 18:02:18 +0200 Subject: [PATCH] =?UTF-8?q?phase=203:=20multi-output=20support=20=E2=80=94?= =?UTF-8?q?=20handle=20all=20wlr=5Foutputs=20(not=20just=20first),=20per-o?= =?UTF-8?q?utput=20scene=5Foutput/bg=5Frect/frame=5Flistener,=20refresh=20?= =?UTF-8?q?heads=20on=20hotplug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/wayland/wl_backend.c | 13 +++++++-- src/backend/wayland/wl_output.c | 46 +++++++++++++++++++++++--------- src/backend/wayland/wl_screen.c | 2 +- src/backend/wayland/wl_types.h | 16 ++++++++--- 4 files changed, 59 insertions(+), 18 deletions(-) diff --git a/src/backend/wayland/wl_backend.c b/src/backend/wayland/wl_backend.c index 938c103..7c3395f 100644 --- a/src/backend/wayland/wl_backend.c +++ b/src/backend/wayland/wl_backend.c @@ -576,6 +576,8 @@ wl_display_open(const char *name) wl_list_init(&wl_state.or_list); wl_list_init(&wl_state.frame_list); wl_list_init(&wl_state.layer_list); + wl_list_init(&wl_state.outputs); + wl_state.output_count = 0; { int ki; for (ki = 0; ki < 3; ki++) @@ -834,7 +836,6 @@ wl_display_close(void) do { if ((l).link.next) wl_list_remove(&(l).link); } while (0) /* Output frame and new-output listeners */ - WL_LISTENER_REMOVE(wl_state.output_frame_listener); WL_LISTENER_REMOVE(wl_state.backend_new_output_listener); /* XDG shell and decoration listeners */ WL_LISTENER_REMOVE(wl_state.xdg_new_surface_listener); @@ -911,10 +912,18 @@ wl_display_close(void) wl_state.headless_inner = NULL; wl_state.wl_backend_inner = NULL; wl_state.display = NULL; + /* Clean up per-output state */ + { + struct wl_wlr_output_state *ost, *tmp; + wl_list_for_each_safe(ost, tmp, &wl_state.outputs, link) { + wl_list_remove(&ost->link); + wfree(ost); + } + } + wl_state.output_count = 0; wl_state.output = NULL; wl_state.frame_timer = NULL; wl_state.output_layout = NULL; - wl_state.scene_output = NULL; wl_state.scene = NULL; wl_state.allocator = NULL; wl_state.started = 0; diff --git a/src/backend/wayland/wl_output.c b/src/backend/wayland/wl_output.c index 91ebc0b..8621448 100644 --- a/src/backend/wayland/wl_output.c +++ b/src/backend/wayland/wl_output.c @@ -1,6 +1,7 @@ #ifdef HAVE_CONFIG_H #include "config.h" #endif +#include #include #include #include @@ -13,6 +14,9 @@ #include #include "wl_types.h" +/* Forward declaration for screen head refresh */ +extern void wl_screen_refresh_heads(WScreen *scr); + void handle_output_frame(struct wl_listener *listener, void *data) @@ -22,19 +26,24 @@ handle_output_frame(struct wl_listener *listener, void *data) return; if (wl_state.session && !wl_state.session->active) return; - if (!wl_state.scene_output) + + /* Find the per-output state for this frame listener */ + struct wl_wlr_output_state *ost = + wl_container_of(listener, ost, frame_listener); + if (!ost || !ost->scene_output) return; + static int frame_count = 0; ++frame_count; if (wl_state.allocator) { - wlr_scene_output_commit(wl_state.scene_output, NULL); + wlr_scene_output_commit(ost->scene_output, NULL); struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - wlr_scene_output_send_frame_done(wl_state.scene_output, &now); + wlr_scene_output_send_frame_done(ost->scene_output, &now); } else { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - wlr_scene_output_send_frame_done(wl_state.scene_output, &now); + wlr_scene_output_send_frame_done(ost->scene_output, &now); } } @@ -48,10 +57,9 @@ handle_backend_new_output(struct wl_listener *listener, void *data) struct wlr_output *output = data; (void)listener; - if (wl_state.output) - return; - - wl_state.output = output; + struct wl_wlr_output_state *ost = wmalloc(sizeof(*ost)); + memset(ost, 0, sizeof(*ost)); + ost->output = output; if (wl_state.allocator && wl_state.renderer) wlr_output_init_render(output, wl_state.allocator, wl_state.renderer); @@ -89,15 +97,15 @@ handle_backend_new_output(struct wl_listener *listener, void *data) wlr_wl_output_set_title(output, "Window Maker"); if (wl_state.scene) { - wl_state.scene_output = wlr_scene_output_create(wl_state.scene, output); - wl_state.output_frame_listener.notify = handle_output_frame; - wl_signal_add(&output->events.frame, &wl_state.output_frame_listener); + ost->scene_output = wlr_scene_output_create(wl_state.scene, output); + ost->frame_listener.notify = handle_output_frame; + wl_signal_add(&output->events.frame, &ost->frame_listener); wwarning("wl_backend: scene output created for %s (%dx%d)", output->name, output->width, output->height); { float bg[4] = { 0x50/255.0f, 0x50/255.0f, 0x75/255.0f, 1.0f }; - wl_state.bg_rect = wlr_scene_rect_create( + ost->bg_rect = wlr_scene_rect_create( wl_state.level_trees[WL_LAYER_BACKGROUND], (int)output->width, (int)output->height, bg); wwarning("wl_backend: root background created (%dx%d)", @@ -113,6 +121,20 @@ handle_backend_new_output(struct wl_listener *listener, void *data) } } + wl_list_insert(&wl_state.outputs, &ost->link); + wl_state.output_count++; + + /* For backward compat, keep wl_state.output pointing to the first output */ + if (!wl_state.output) { + wl_state.output = output; + wl_state.scene_output = ost->scene_output; + wl_state.bg_rect = ost->bg_rect; + } + wwarning("wl_backend: new output %s (%dx%d)", output->name, output->width, output->height); + + /* Refresh screen heads to include the new output */ + if (wl_state.scr) + wl_screen_refresh_heads(wl_state.scr); } diff --git a/src/backend/wayland/wl_screen.c b/src/backend/wayland/wl_screen.c index 8e7f2c4..b049cd8 100644 --- a/src/backend/wayland/wl_screen.c +++ b/src/backend/wayland/wl_screen.c @@ -32,7 +32,7 @@ void *W_RegisterBacking(unsigned long id, void *img); /* Rebuild scr->heads from the wlr_output_layout. Called during screen * init and on output hotplug. */ -static void +void wl_screen_refresh_heads(WScreen *scr) { /* Free old head array if any */ diff --git a/src/backend/wayland/wl_types.h b/src/backend/wayland/wl_types.h index 7cf2d75..d39e502 100644 --- a/src/backend/wayland/wl_types.h +++ b/src/backend/wayland/wl_types.h @@ -137,6 +137,15 @@ struct wl_ctx_entry { }; /* ------------------------------------------------------------------ */ +/* Per-output state — one entry per active wlr_output */ +struct wl_wlr_output_state { + struct wlr_output *output; + struct wlr_scene_output *scene_output; + struct wlr_scene_rect *bg_rect; + struct wl_listener frame_listener; + struct wl_list link; /* wl_compositor_state.outputs */ +}; + /* Global compositor state */ /* ------------------------------------------------------------------ */ @@ -144,7 +153,9 @@ struct wl_compositor_state { struct wl_display *display; struct wlr_backend *backend; struct wlr_renderer *renderer; - struct wlr_output *output; /* headless output (screen 0) */ + struct wlr_output *output; /* first/primary output (backward compat) */ + struct wl_list outputs; /* wl_wlr_output_state.link */ + int output_count; struct wlr_output_layout *output_layout; /* Phase 34 */ /* Phase 35: core Wayland protocol globals */ struct wlr_compositor *compositor; @@ -210,9 +221,8 @@ struct wl_compositor_state { struct wlr_session *session; /* DRM session (standalone only) */ struct wlr_allocator *allocator; struct wlr_scene *scene; - struct wlr_scene_output *scene_output; + struct wlr_scene_output *scene_output; /* first output's scene (backward compat) */ struct wl_listener backend_new_output_listener; - struct wl_listener output_frame_listener; char socket_name[64]; char host_wayland_display[64]; /* saved from env before overwrite */ int started; /* non-zero after successful display_open */