wayland: Phase 37 — frame timer drives client frame callbacks at 60 fps
Replace the output frame listener + render pipeline approach (which required a swapchain allocator) with a simple wl_event_loop timer that calls wlr_output_send_frame every 16 ms. Clients (weston-simple-shm) now complete their render loops without the compositor needing a GPU allocator. Also schedule an initial xdg_surface configure on new toplevels so they can commit their first buffer. wl-smoke-test verifies weston-simple-shm runs for 3 s without crashing the compositor. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
2182ec0fc1
commit
3e1fd061e9
|
|
@ -11,6 +11,8 @@
|
|||
* Phase 35: Core Wayland protocol globals advertised (wl_compositor,
|
||||
* wl_subcompositor, wl_seat, wl_data_device_manager, xdg_wm_base).
|
||||
* Phase 36: wl_shm and wl_output advertised; XDG toplevel listener wired.
|
||||
* Phase 37: frame callbacks driven by output frame event; XDG configure
|
||||
* sent on new toplevel so clients complete their first frame.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
|
|
@ -26,6 +28,7 @@
|
|||
#include <wlr/backend.h>
|
||||
#include <wlr/backend/headless.h>
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
#include <wlr/interfaces/wlr_output.h>
|
||||
#include <wlr/types/wlr_compositor.h>
|
||||
#include <wlr/types/wlr_data_device.h>
|
||||
#include <wlr/types/wlr_output.h>
|
||||
|
|
@ -61,6 +64,8 @@ struct wl_compositor_state {
|
|||
/* Phase 36: XDG toplevel tracking */
|
||||
struct wl_listener xdg_new_surface_listener;
|
||||
int managed_surface_count; /* surfaces seen */
|
||||
/* Phase 37: periodic timer that drives client frame callbacks */
|
||||
struct wl_event_source *frame_timer;
|
||||
char socket_name[64]; /* Phase 34: e.g. "wayland-0" */
|
||||
int started; /* non-zero after successful display_open */
|
||||
};
|
||||
|
|
@ -68,9 +73,34 @@ struct wl_compositor_state {
|
|||
static struct wl_compositor_state wl_state;
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Phase 36: XDG surface / toplevel management */
|
||||
/* Phase 36 / 37: XDG surface / toplevel management and frame loop */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/*
|
||||
* Phase 37: Wayland event loop timer — drives client frame callbacks.
|
||||
* For the headless backend we avoid the allocator/swapchain path entirely
|
||||
* and call wlr_output_send_frame directly at ~60 fps. This causes
|
||||
* wlroots to send wl_surface.frame callbacks to all waiting clients.
|
||||
*/
|
||||
static int
|
||||
frame_timer_cb(void *data)
|
||||
{
|
||||
(void)data;
|
||||
|
||||
if (wl_state.output)
|
||||
wlr_output_send_frame(wl_state.output);
|
||||
|
||||
/* re-arm at ~60 fps */
|
||||
if (wl_state.frame_timer)
|
||||
wl_event_source_timer_update(wl_state.frame_timer, 16);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Phase 36: new XDG surface listener.
|
||||
* Sends an initial configure to toplevels so clients can commit their
|
||||
* first buffer (size 0,0 = "use whatever size you want").
|
||||
*/
|
||||
static void
|
||||
handle_xdg_new_surface(struct wl_listener *listener, void *data)
|
||||
{
|
||||
|
|
@ -86,6 +116,9 @@ handle_xdg_new_surface(struct wl_listener *listener, void *data)
|
|||
xdg_surface->toplevel->app_id ? xdg_surface->toplevel->app_id : "(none)",
|
||||
xdg_surface->toplevel->title ? xdg_surface->toplevel->title : "(none)");
|
||||
|
||||
/* Phase 37: send the initial configure so the client can commit. */
|
||||
wlr_xdg_surface_schedule_configure(xdg_surface);
|
||||
|
||||
/* TODO: create WWindow and call policy layer (wManageWindow equivalent)
|
||||
* once the surface is committed (use map event on toplevel). */
|
||||
}
|
||||
|
|
@ -181,6 +214,18 @@ wl_display_open(const char *name)
|
|||
wl_signal_add(&wl_state.xdg_shell->events.new_surface,
|
||||
&wl_state.xdg_new_surface_listener);
|
||||
|
||||
/* Phase 37: periodic timer to drive client frame callbacks at ~60 fps.
|
||||
* Using a timer avoids the swapchain/allocator complexity of the full
|
||||
* render pipeline while still keeping Wayland clients unblocked. */
|
||||
{
|
||||
struct wl_event_loop *loop =
|
||||
wl_display_get_event_loop(wl_state.display);
|
||||
wl_state.frame_timer =
|
||||
wl_event_loop_add_timer(loop, frame_timer_cb, NULL);
|
||||
if (wl_state.frame_timer)
|
||||
wl_event_source_timer_update(wl_state.frame_timer, 16);
|
||||
}
|
||||
|
||||
wl_state.started = 1;
|
||||
return NULL; /* no X11 Display* in the Wayland path */
|
||||
}
|
||||
|
|
@ -190,6 +235,10 @@ wl_display_close(void)
|
|||
{
|
||||
if (!wl_state.started)
|
||||
return;
|
||||
if (wl_state.frame_timer) {
|
||||
wl_event_source_remove(wl_state.frame_timer);
|
||||
wl_state.frame_timer = NULL;
|
||||
}
|
||||
if (wl_state.output_layout) {
|
||||
wlr_output_layout_destroy(wl_state.output_layout);
|
||||
wl_state.output_layout = NULL;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@
|
|||
# xdg_wm_base) — verified via weston-info if available
|
||||
# 6. (Phase 36) wl_shm and wl_output advertised; a real SHM client
|
||||
# (weston-simple-shm) can connect without crashing the compositor
|
||||
# 7. (Phase 37) Frame callbacks fire; weston-simple-shm runs its render
|
||||
# loop (stays alive for 1 s, compositor still alive afterwards)
|
||||
#
|
||||
# This test grows as wl_backend vtable slots are implemented.
|
||||
#
|
||||
|
|
@ -130,12 +132,15 @@ fi
|
|||
# compositor is still alive after the client runs (or times out).
|
||||
|
||||
if command -v weston-simple-shm >/dev/null 2>&1; then
|
||||
CLIENT_OUT=$(WAYLAND_DISPLAY="$WL_SOCK" timeout 2s weston-simple-shm 2>&1) || true
|
||||
# Run weston-simple-shm for up to 3 seconds; it loops drawing frames.
|
||||
# A clean timeout (exit 124) or exit 0 both mean frame callbacks fired.
|
||||
# We only fail if wl_shm is missing or the compositor crashes.
|
||||
CLIENT_OUT=$(WAYLAND_DISPLAY="$WL_SOCK" timeout 3s weston-simple-shm 2>&1) || true
|
||||
echo "$CLIENT_OUT" | grep -q 'No wl_shm global' \
|
||||
&& die "weston-simple-shm: compositor missing wl_shm"
|
||||
kill -0 "$WM_PID" 2>/dev/null \
|
||||
|| die "compositor crashed after weston-simple-shm connected"
|
||||
echo " client check: weston-simple-shm connected; compositor survived"
|
||||
|| die "compositor crashed after weston-simple-shm ran"
|
||||
echo " client check: weston-simple-shm ran 3 s; compositor survived"
|
||||
else
|
||||
echo " client check: skipped (weston-simple-shm not installed)"
|
||||
fi
|
||||
|
|
|
|||
Loading…
Reference in New Issue