decompose wl_backend.c: extract wl_output.c
This commit is contained in:
parent
053ed4e6b3
commit
e3a8c5d74b
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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) */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
Loading…
Reference in New Issue