phase 3: multi-output support — handle all wlr_outputs (not just first), per-output scene_output/bg_rect/frame_listener, refresh heads on hotplug

This commit is contained in:
lkn 2026-06-06 18:02:18 +02:00
parent 3fc416a9b6
commit b33774db14
4 changed files with 59 additions and 18 deletions

View File

@ -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;

View File

@ -1,6 +1,7 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include <time.h>
#include <wayland-server-core.h>
#include <wlr/types/wlr_cursor.h>
@ -13,6 +14,9 @@
#include <wlr/backend/wayland.h>
#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);
}

View File

@ -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 */

View File

@ -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 */