decompose wl_backend.c: extract wl_output.c

This commit is contained in:
Window Maker 2026-06-02 16:07:34 +02:00
parent 053ed4e6b3
commit e3a8c5d74b
3 changed files with 112 additions and 135 deletions

View File

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

View File

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

View File

@ -0,0 +1,109 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <time.h>
#include <wayland-server-core.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/util/log.h>
#include <wlr/backend/session.h>
#include <wlr/backend/wayland.h>
#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);
}