From 3ebe50ef31eab632c67567726625d52605cf6a64 Mon Sep 17 00:00:00 2001 From: Marco Allegretti Date: Tue, 10 Mar 2026 21:20:28 +0100 Subject: [PATCH] fix(compositor): correct remaining Smithay 0.7 API issues - Remove nonexistent TextInputHandler trait impl - Add missing cursor_position_hint to PointerConstraintsHandler - Rewrite winit/drm backends: ListeningSocketSource + insert_client for per-client WeftClientState; Generic> for dispatch - Remove calloop direct dep; use via smithay::reexports::calloop - Remove unused imports in input.rs --- crates/weft-compositor/Cargo.toml | 1 - crates/weft-compositor/src/backend/drm.rs | 107 ++++++++++---------- crates/weft-compositor/src/backend/winit.rs | 55 +++++++--- crates/weft-compositor/src/input.rs | 5 +- crates/weft-compositor/src/state.rs | 20 ++-- 5 files changed, 105 insertions(+), 83 deletions(-) diff --git a/crates/weft-compositor/Cargo.toml b/crates/weft-compositor/Cargo.toml index c10e0f2..59c9042 100644 --- a/crates/weft-compositor/Cargo.toml +++ b/crates/weft-compositor/Cargo.toml @@ -21,7 +21,6 @@ smithay = { version = "0.7", default-features = false, features = [ "wayland_frontend", "desktop", ] } -calloop = { version = "0.14", features = ["executor"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } anyhow = "1" diff --git a/crates/weft-compositor/src/backend/drm.rs b/crates/weft-compositor/src/backend/drm.rs index fcb68a8..f38d3bb 100644 --- a/crates/weft-compositor/src/backend/drm.rs +++ b/crates/weft-compositor/src/backend/drm.rs @@ -5,66 +5,68 @@ pub fn run() -> anyhow::Result<()> { } // Linux DRM/KMS backend. +// GPU enumeration and rendering are deferred; this skeleton establishes the +// session, socket, and event loop that the full implementation will extend. #[cfg(target_os = "linux")] pub fn run() -> anyhow::Result<()> { - use std::time::Duration; + use std::sync::Arc; use smithay::{ backend::{ - allocator::gbm::{GbmAllocator, GbmBufferFlags, GbmDevice}, - drm::{DrmDevice, DrmDeviceFd, DrmNode, NodeType}, - egl::EGLDevice, - libinput::{LibinputInputBackend, LibinputSessionInterface}, - renderer::{ - damage::OutputDamageTracker, - gles::GlesRenderer, - multigpu::{gbm::GbmGlesBackend, GpuManager, MultiRenderer}, - }, - session::{ - libseat::{LibSeatSession, LibSeatSessionNotifier}, - Session, - }, + session::{libseat::LibSeatSession, Session}, udev::{UdevBackend, UdevEvent}, }, - desktop::{space::space_render_elements, Space, Window}, - output::{Mode as OutputMode, Output, PhysicalProperties, Subpixel}, - reexports::{ - calloop::{ - timer::{TimeoutAction, Timer}, - EventLoop, Interest, Mode, PostAction, - }, - wayland_server::Display, - }, - utils::Transform, + reexports::calloop::{generic::Generic, EventLoop, Interest, Mode, PostAction}, + wayland::socket::ListeningSocketSource, }; - use crate::{input, state::WeftCompositorState}; + use crate::state::{WeftClientState, WeftCompositorState}; - let mut display: Display = Display::new()?; + let mut display = + smithay::reexports::wayland_server::Display::::new()?; let display_handle = display.handle(); let mut event_loop: EventLoop<'static, WeftCompositorState> = EventLoop::try_new()?; let loop_handle = event_loop.handle(); let loop_signal = event_loop.get_signal(); - // Open a libseat session to gain DRM device access without root. - let (session, notifier) = LibSeatSession::new() + // Gain DRM device access without root via libseat. + let (session, _notifier) = LibSeatSession::new() .map_err(|e| anyhow::anyhow!("libseat session failed: {e}"))?; - // Discover GPU nodes via udev. + let listening_socket = ListeningSocketSource::new_auto() + .map_err(|e| anyhow::anyhow!("Wayland socket creation failed: {e}"))?; + let socket_name = listening_socket.socket_name().to_os_string(); + std::env::set_var("WAYLAND_DISPLAY", &socket_name); + tracing::info!(?socket_name, "Wayland compositor socket open"); + + loop_handle + .insert_source(listening_socket, |client_stream, _, state| { + state + .display_handle + .insert_client(client_stream, Arc::new(WeftClientState::default())) + .unwrap(); + }) + .map_err(|e| anyhow::anyhow!("socket source insertion failed: {e}"))?; + + loop_handle + .insert_source( + Generic::new(display, Interest::READ, Mode::Level), + |_, display, state| { + // Safety: the display is owned by this Generic source and is never + // dropped while the event loop runs. + unsafe { + display.get_mut().dispatch_clients(state).unwrap(); + } + Ok(PostAction::Continue) + }, + ) + .map_err(|e| anyhow::anyhow!("display source insertion failed: {e}"))?; + + // Enumerate GPU nodes via udev; hotplug events arrive through calloop. let udev_backend = UdevBackend::new(session.seat())?; - - let mut state = WeftCompositorState::new( - display_handle, - loop_signal.clone(), - loop_handle.clone(), - session.seat(), - ); - - // Register the udev backend with calloop so device hotplug is handled. - loop_handle.insert_source(udev_backend, { - let signal = loop_signal.clone(); - move |event, _, _state| match event { + loop_handle + .insert_source(udev_backend, move |event, _, _state| match event { UdevEvent::Added { device_id, path } => { tracing::info!(?device_id, ?path, "GPU device added"); } @@ -73,22 +75,19 @@ pub fn run() -> anyhow::Result<()> { } UdevEvent::Removed { device_id } => { tracing::info!(?device_id, "GPU device removed"); - signal.stop(); } - } - })?; + }) + .map_err(|e| anyhow::anyhow!("udev source insertion failed: {e}"))?; + + let mut state = WeftCompositorState::new( + display_handle, + loop_signal, + loop_handle, + session.seat(), + ); tracing::info!("DRM/KMS backend initialised; entering event loop"); - - loop { - display.dispatch_clients(&mut state)?; - display.flush_clients()?; - event_loop.dispatch(Some(Duration::from_millis(16)), &mut state)?; - - if loop_signal.is_stopped() { - break; - } - } + event_loop.run(None, &mut state, |_| {})?; Ok(()) } diff --git a/crates/weft-compositor/src/backend/winit.rs b/crates/weft-compositor/src/backend/winit.rs index 7e95001..d14e517 100644 --- a/crates/weft-compositor/src/backend/winit.rs +++ b/crates/weft-compositor/src/backend/winit.rs @@ -1,4 +1,4 @@ -use std::time::Duration; +use std::{sync::Arc, time::Duration}; use smithay::{ backend::{ @@ -9,11 +9,15 @@ use smithay::{ winit::{self, WinitEvent}, }, output::{Mode as OutputMode, Output, PhysicalProperties, Subpixel}, - reexports::calloop::EventLoop, + reexports::calloop::{generic::Generic, EventLoop, Interest, Mode, PostAction}, utils::{Rectangle, Transform}, + wayland::socket::ListeningSocketSource, }; -use crate::{input, state::WeftCompositorState}; +use crate::{ + input, + state::{WeftClientState, WeftCompositorState}, +}; pub fn run() -> anyhow::Result<()> { let mut display = @@ -49,13 +53,38 @@ pub fn run() -> anyhow::Result<()> { ); output.set_preferred(mode); - // Open the Wayland socket so clients can connect. - let socket_name = display - .add_socket_auto() + // Create the listening socket; each connecting client is inserted with + // default per-client data so CompositorHandler::client_compositor_state works. + let listening_socket = ListeningSocketSource::new_auto() .map_err(|e| anyhow::anyhow!("Wayland socket creation failed: {e}"))?; + let socket_name = listening_socket.socket_name().to_os_string(); std::env::set_var("WAYLAND_DISPLAY", &socket_name); tracing::info!(?socket_name, "Wayland compositor socket open"); + loop_handle + .insert_source(listening_socket, |client_stream, _, state| { + state + .display_handle + .insert_client(client_stream, Arc::new(WeftClientState::default())) + .unwrap(); + }) + .map_err(|e| anyhow::anyhow!("socket source insertion failed: {e}"))?; + + // Register the display fd so calloop dispatches Wayland client messages when readable. + loop_handle + .insert_source( + Generic::new(display, Interest::READ, Mode::Level), + |_, display, state| { + // Safety: the display is owned by this Generic source and is never dropped + // while the event loop runs. + unsafe { + display.get_mut().dispatch_clients(state).unwrap(); + } + Ok(PostAction::Continue) + }, + ) + .map_err(|e| anyhow::anyhow!("display source insertion failed: {e}"))?; + let mut state = WeftCompositorState::new( display_handle, loop_signal, @@ -67,8 +96,8 @@ pub fn run() -> anyhow::Result<()> { let mut damage_tracker = OutputDamageTracker::from_output(&output); let start_time = std::time::Instant::now(); - // WinitEventLoop implements calloop's EventSource; insert it so Winit events - // arrive through the same dispatch loop as all other compositor sources. + // WinitEventLoop implements calloop's EventSource; Winit events arrive + // through the same dispatch loop as all other compositor sources. loop_handle .insert_source(winit, move |event, _, state| match event { WinitEvent::Resized { size, .. } => { @@ -123,7 +152,7 @@ pub fn run() -> anyhow::Result<()> { state.popups.cleanup(); let _ = state.display_handle.flush_clients(); - // Request the next redraw to drive continuous rendering. + // Request next redraw to drive continuous rendering. backend.window().request_redraw(); } WinitEvent::CloseRequested => { @@ -134,13 +163,7 @@ pub fn run() -> anyhow::Result<()> { }) .map_err(|e| anyhow::anyhow!("winit source insertion failed: {e}"))?; - // The idle callback dispatches pending Wayland client requests after each - // calloop iteration so protocol handlers in state receive them promptly. - event_loop.run(None, &mut state, move |state| { - if let Err(e) = display.dispatch_clients(state) { - tracing::error!("Wayland client dispatch failed: {e}"); - } - })?; + event_loop.run(None, &mut state, |_| {})?; Ok(()) } diff --git a/crates/weft-compositor/src/input.rs b/crates/weft-compositor/src/input.rs index 7fc0e03..507ae8f 100644 --- a/crates/weft-compositor/src/input.rs +++ b/crates/weft-compositor/src/input.rs @@ -8,10 +8,9 @@ use smithay::{ TouchCancelEvent, TouchDownEvent, TouchFrameEvent, TouchMotionEvent, TouchUpEvent, }, input::{ - keyboard::{FilterResult, KeysymHandle, ModifiersState}, - pointer::{AxisFrame, ButtonEvent, MotionEvent, RelativeMotionEvent}, + keyboard::FilterResult, + pointer::{AxisFrame, ButtonEvent, MotionEvent}, }, - reexports::wayland_server::protocol::wl_pointer, utils::{Logical, Point, Serial, SERIAL_COUNTER}, }; diff --git a/crates/weft-compositor/src/state.rs b/crates/weft-compositor/src/state.rs index 2889e5e..1d70d79 100644 --- a/crates/weft-compositor/src/state.rs +++ b/crates/weft-compositor/src/state.rs @@ -31,12 +31,13 @@ use smithay::{ output::OutputManagerState, pointer_constraints::{PointerConstraintsHandler, PointerConstraintsState}, presentation::{PresentationHandler, PresentationState}, + seat::WaylandFocus, shell::{ wlr_layer::{Layer, LayerSurface, WlrLayerShellHandler, WlrLayerShellState}, xdg::{PopupSurface, PositionerState, ToplevelSurface, XdgShellHandler, XdgShellState}, }, shm::{ShmHandler, ShmState}, - text_input::{TextInputHandler, TextInputManagerState}, + text_input::TextInputManagerState, }, }; @@ -310,14 +311,7 @@ impl PresentationHandler for WeftCompositorState { delegate_presentation!(WeftCompositorState); -// --- TextInputHandler --- - -impl TextInputHandler for WeftCompositorState { - fn text_input_state(&mut self) -> &mut TextInputManagerState { - &mut self.text_input_state - } -} - +// TextInputManagerState has no handler trait; delegate macro only requires SeatHandler. delegate_text_input_manager!(WeftCompositorState); // --- InputMethodHandler --- @@ -352,6 +346,14 @@ impl PointerConstraintsHandler for WeftCompositorState { _pointer: &smithay::input::pointer::PointerHandle, ) { } + + fn cursor_position_hint( + &mut self, + _surface: &WlSurface, + _pointer: &smithay::input::pointer::PointerHandle, + _location: smithay::utils::Point, + ) { + } } delegate_pointer_constraints!(WeftCompositorState);