From e3a8c5d74b2345c9d66e7550248fda272bfc4118 Mon Sep 17 00:00:00 2001 From: Window Maker Date: Tue, 2 Jun 2026 16:07:34 +0200 Subject: [PATCH] decompose wl_backend.c: extract wl_output.c --- src/Makefile.am | 1 + src/backend/wayland/wl_backend.c | 137 +------------------------------ src/backend/wayland/wl_output.c | 109 ++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 135 deletions(-) create mode 100644 src/backend/wayland/wl_output.c diff --git a/src/Makefile.am b/src/Makefile.am index aa49f39..5f6c78b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -190,6 +190,7 @@ wmaker_SOURCES += \ backend/wayland/wl_grab.c \ backend/wayland/wl_keyboard.c \ backend/wayland/wl_popup.c \ + backend/wayland/wl_output.c \ backend/wayland/wl_xdg.c \ backend/wayland/wl_xwayland.c \ backend/wayland/wl_dock.c \ diff --git a/src/backend/wayland/wl_backend.c b/src/backend/wayland/wl_backend.c index 41a1263..fb7b872 100644 --- a/src/backend/wayland/wl_backend.c +++ b/src/backend/wayland/wl_backend.c @@ -350,8 +350,8 @@ extern void wm_dock_create(WScreen *scr); /* Phase 20: nested backend real input device forward declaration. */ extern void handle_backend_new_input(struct wl_listener *listener, void *data); /* Phase 18: output lifecycle and frame rendering forward declarations. */ -static void handle_backend_new_output(struct wl_listener *listener, void *data); -static void handle_output_frame(struct wl_listener *listener, void *data); +extern void handle_backend_new_output(struct wl_listener *listener, void *data); +extern void handle_output_frame(struct wl_listener *listener, void *data); extern void handle_pointer_motion_absolute(struct wl_listener *listener, void *data); extern void handle_pointer_button(struct wl_listener *listener, void *data); extern void handle_keyboard_key(struct wl_listener *listener, void *data); @@ -366,139 +366,6 @@ extern const char *resize_edges_to_cursor(uint32_t edges); /* ------------------------------------------------------------------ */ -/* Phase 18: output frame rendering and backend new-output handler */ -/* ------------------------------------------------------------------ */ - -/* - * handle_output_frame — fires on every output vsync/frame event. - * Calls wlr_scene_output_commit which renders the scene graph (all mapped - * surfaces at their current positions) and sends Wayland frame callbacks - * to clients so they know they can submit the next buffer. - */ -static void -handle_output_frame(struct wl_listener *listener, void *data) -{ - (void)listener; (void)data; - if (wl_state.startup_pump_running) - return; - if (wl_state.session && !wl_state.session->active) - return; - if (!wl_state.scene_output) - return; - static int frame_count = 0; - ++frame_count; - if (wl_state.allocator) { - /* Full scene commit — renders client surfaces and fires - * wl_surface.frame callbacks so clients can submit next buffer. - * Also send frame_done unconditionally so clients aren't stalled. */ - wlr_scene_output_commit(wl_state.scene_output, NULL); - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - wlr_scene_output_send_frame_done(wl_state.scene_output, &now); - } else { - /* No GPU allocator available (e.g. headless without DRI access). - * Skip rendering but still fire wl_surface.frame callbacks so - * clients are not stalled waiting for frame pacing. Without - * this, weston-simple-shm would never submit its second buffer. */ - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - wlr_scene_output_send_frame_done(wl_state.scene_output, &now); - } -} - -/* - * handle_backend_new_output — fires when a new output is added to the backend. - * For the headless backend this fires synchronously after wlr_headless_add_output; - * for the nested Wayland backend it fires when the host compositor creates the - * output window. Initialises the render pipeline and wires the frame handler. - */ -static void -handle_backend_new_output(struct wl_listener *listener, void *data) -{ - struct wlr_output *output = data; - (void)listener; - - if (wl_state.output) - return; /* we only manage one output */ - - wl_state.output = output; - - /* Initialise the render pipeline for this output. */ - if (wl_state.allocator && wl_state.renderer) - wlr_output_init_render(output, wl_state.allocator, wl_state.renderer); - - /* Enable the output: select preferred mode, then commit via state API. - * Nested Wayland outputs: sway's initial xdg-shell configure sends 0×0 - * ("compositor defers to client"), so output->width/height stay 0 after - * the configure busy-loop. Without an explicit mode the swapchain tries - * to allocate a 0×0 GBM buffer which crashes. Fall back to 1920×1080 so - * the commit succeeds; sway will send a real configure once the window is - * visible and we handle it in the frame/configure signal path. */ - { - 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(output); - if (mode) { - wlr_output_state_set_mode(&state, mode); - } else { - /* Default size for nested mode (or headless with no preferred mode). */ - wlr_output_state_set_custom_mode(&state, 1920, 1080, 0); - } - if (!wlr_output_commit_state(output, &state)) - wwarning("wl_backend: initial output commit failed"); - wlr_output_state_finish(&state); - } - - /* Register with the output layout. */ - if (wl_state.output_layout) - wlr_output_layout_add_auto(wl_state.output_layout, output); - - /* Phase 23: apply cursor image now that this output is in the layout. - * wlr_xcursor_manager_set_cursor_image iterates the output layout and - * sets the hardware cursor on each output; for the nested backend this - * sends wl_pointer.set_cursor to the host compositor, making the cursor - * visible. Also warp to the current virtual pointer position so the - * cursor appears at the right location from the first frame. */ - if (wl_state.cursor && wl_state.xcursor_mgr) { - wlr_xcursor_manager_load(wl_state.xcursor_mgr, output->scale); - wlr_cursor_set_xcursor(wl_state.cursor, wl_state.xcursor_mgr, "left_ptr"); - wlr_cursor_warp(wl_state.cursor, NULL, - (double)wl_state.pointer_x, - (double)wl_state.pointer_y); - wwarning("wl_backend: cursor image set to 'left_ptr' on output %s", - output->name); - } - - /* Set window title when running as a nested compositor. */ - if (wl_state.is_nested && wl_state.wl_backend_inner && - wlr_backend_is_wl(wl_state.wl_backend_inner)) - wlr_wl_output_set_title(output, "Window Maker"); - - /* Create scene output and wire the frame renderer. */ - 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); - wwarning("wl_backend: scene output created for %s (%dx%d)", - output->name, output->width, output->height); - - /* Phase 20: root background rectangle. - * Inserted first in the scene tree so it is always below client - * surfaces. Default WindowMaker desktop colour: rgb:50/50/75. */ - { - float bg[4] = { 0x50/255.0f, 0x50/255.0f, 0x75/255.0f, 1.0f }; - wl_state.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)", - (int)output->width, (int)output->height); - } - } - - wwarning("wl_backend: new output %s (%dx%d)", - output->name, output->width, output->height); -} /* ------------------------------------------------------------------ */ /* Display lifecycle (Phase 32 / Phase 34) */ diff --git a/src/backend/wayland/wl_output.c b/src/backend/wayland/wl_output.c new file mode 100644 index 0000000..2ac350a --- /dev/null +++ b/src/backend/wayland/wl_output.c @@ -0,0 +1,109 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wl_types.h" + +/* ------------------------------------------------------------------ */ +/* Phase 18: output frame rendering and backend new-output handler */ +/* ------------------------------------------------------------------ */ + +void +handle_output_frame(struct wl_listener *listener, void *data) +{ + (void)listener; (void)data; + if (wl_state.startup_pump_running) + return; + if (wl_state.session && !wl_state.session->active) + return; + if (!wl_state.scene_output) + return; + static int frame_count = 0; + ++frame_count; + if (wl_state.allocator) { + wlr_scene_output_commit(wl_state.scene_output, NULL); + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + wlr_scene_output_send_frame_done(wl_state.scene_output, &now); + } else { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + wlr_scene_output_send_frame_done(wl_state.scene_output, &now); + } +} + +void +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; + + if (wl_state.allocator && wl_state.renderer) + wlr_output_init_render(output, wl_state.allocator, wl_state.renderer); + + { + 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(output); + if (mode) { + wlr_output_state_set_mode(&state, mode); + } else { + wlr_output_state_set_custom_mode(&state, 1920, 1080, 0); + } + if (!wlr_output_commit_state(output, &state)) + wwarning("wl_backend: initial output commit failed"); + wlr_output_state_finish(&state); + } + + if (wl_state.output_layout) + wlr_output_layout_add_auto(wl_state.output_layout, output); + + if (wl_state.cursor && wl_state.xcursor_mgr) { + wlr_xcursor_manager_load(wl_state.xcursor_mgr, output->scale); + wlr_cursor_set_xcursor(wl_state.cursor, wl_state.xcursor_mgr, "left_ptr"); + wlr_cursor_warp(wl_state.cursor, NULL, + (double)wl_state.pointer_x, + (double)wl_state.pointer_y); + wwarning("wl_backend: cursor image set to 'left_ptr' on output %s", + output->name); + } + + if (wl_state.is_nested && wl_state.wl_backend_inner && + wlr_backend_is_wl(wl_state.wl_backend_inner)) + 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); + 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( + wl_state.level_trees[WL_LAYER_BACKGROUND], + (int)output->width, (int)output->height, bg); + wwarning("wl_backend: root background created (%dx%d)", + (int)output->width, (int)output->height); + } + } + + wwarning("wl_backend: new output %s (%dx%d)", + output->name, output->width, output->height); +}