From 782bd0dc479e3d60ebb8825f8df3aa644cac1e12 Mon Sep 17 00:00:00 2001 From: galister <22305755+galister@users.noreply.github.com> Date: Sat, 10 Jan 2026 16:21:43 +0900 Subject: [PATCH] wayland server fixes --- wayvr/src/backend/wayvr/comp.rs | 82 ++++++++++++++++++++++++++++--- wayvr/src/backend/wayvr/mod.rs | 86 +++++++++++++++++++++++++-------- 2 files changed, 141 insertions(+), 27 deletions(-) diff --git a/wayvr/src/backend/wayvr/comp.rs b/wayvr/src/backend/wayvr/comp.rs index 5f6a589a..2886c3b6 100644 --- a/wayvr/src/backend/wayvr/comp.rs +++ b/wayvr/src/backend/wayvr/comp.rs @@ -3,33 +3,43 @@ use smithay::backend::allocator::dmabuf::Dmabuf; use smithay::backend::renderer::{BufferType, buffer_type}; use smithay::desktop::{PopupKind, PopupManager}; use smithay::input::{Seat, SeatHandler, SeatState}; +use smithay::reexports::rustix::fs::{OFlags, fcntl_setfl}; use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel; -use smithay::reexports::wayland_server; use smithay::reexports::wayland_server::Resource; use smithay::reexports::wayland_server::protocol::{wl_buffer, wl_output, wl_seat}; +use smithay::reexports::wayland_server::{self, DisplayHandle}; use smithay::wayland::buffer::BufferHandler; use smithay::wayland::dmabuf::{ DmabufFeedback, DmabufGlobal, DmabufHandler, DmabufState, ImportNotifier, get_dmabuf, }; use smithay::wayland::fractional_scale::with_fractional_scale; use smithay::wayland::output::OutputHandler; +use smithay::wayland::selection::{ + ext_data_control as selection_ext, + primary_selection::{PrimarySelectionHandler, PrimarySelectionState, set_primary_focus}, + wlr_data_control as selection_wlr, +}; use smithay::wayland::shm::{ShmHandler, ShmState, with_buffer_contents}; use smithay::wayland::single_pixel_buffer::get_single_pixel_buffer; use smithay::{ - delegate_compositor, delegate_data_device, delegate_dmabuf, delegate_output, delegate_seat, - delegate_shm, delegate_xdg_shell, + delegate_compositor, delegate_data_control, delegate_data_device, delegate_dmabuf, + delegate_ext_data_control, delegate_output, delegate_primary_selection, delegate_seat, + delegate_shm, delegate_single_pixel_buffer, delegate_xdg_shell, }; use std::collections::HashSet; +use std::fs::File; +use std::io::Write; use std::os::fd::OwnedFd; use std::sync::{Arc, Mutex}; use smithay::utils::Serial; use smithay::wayland::compositor::{self, BufferAssignment, SurfaceAttributes, send_surface_state}; -use smithay::wayland::selection::SelectionHandler; use smithay::wayland::selection::data_device::{ ClientDndGrabHandler, DataDeviceHandler, DataDeviceState, ServerDndGrabHandler, + set_data_device_focus, }; +use smithay::wayland::selection::{self, SelectionHandler}; use smithay::wayland::shell::xdg::{ PopupSurface, PositionerState, ToplevelSurface, XdgShellHandler, XdgShellState, }; @@ -51,9 +61,13 @@ pub struct Application { pub seat_state: SeatState, pub shm: ShmState, pub data_device: DataDeviceState, + pub primary_selection_state: PrimarySelectionState, + pub ext_data_control_state: selection_ext::DataControlState, + pub wlr_data_control_state: selection_wlr::DataControlState, pub wayvr_tasks: SyncEventQueue, pub redraw_requests: HashSet, pub popup_manager: PopupManager, + pub display_handle: DisplayHandle, } impl Application { @@ -205,7 +219,13 @@ impl SeatHandler for Application { &mut self.seat_state } - fn focus_changed(&mut self, _seat: &Seat, _focused: Option<&WlSurface>) {} + fn focus_changed(&mut self, seat: &Seat, focused: Option<&WlSurface>) { + let dh = &self.display_handle; + let client = focused.and_then(|s| dh.get_client(s.id()).ok()); + set_data_device_focus(dh, seat, client.clone()); + set_primary_focus(dh, seat, client); + } + fn cursor_image( &mut self, _seat: &Seat, @@ -231,7 +251,35 @@ impl DataDeviceHandler for Application { } impl SelectionHandler for Application { - type SelectionUserData = (); + type SelectionUserData = Arc<[u8]>; + + fn send_selection( + &mut self, + _ty: selection::SelectionTarget, + _mime_type: String, + fd: OwnedFd, + _seat: Seat, + user_data: &Self::SelectionUserData, + ) { + let buf = user_data.clone(); + std::thread::spawn(move || { + // Clear O_NONBLOCK, otherwise File::write_all() will stop halfway. + if let Err(err) = fcntl_setfl(&fd, OFlags::empty()) { + log::warn!("error clearing flags on selection target fd: {err:?}"); + } + if let Err(err) = File::from(fd).write_all(&buf) { + log::warn!("error writing selection: {err:?}"); + } + }); + } + + fn new_selection( + &mut self, + _ty: selection::SelectionTarget, + _source: Option, + _seat: Seat, + ) { + } } #[derive(Default)] @@ -371,6 +419,24 @@ impl DmabufHandler for Application { } } +impl PrimarySelectionHandler for Application { + fn primary_selection_state(&self) -> &PrimarySelectionState { + &self.primary_selection_state + } +} + +impl selection_wlr::DataControlHandler for Application { + fn data_control_state(&self) -> &selection_wlr::DataControlState { + &self.wlr_data_control_state + } +} + +impl selection_ext::DataControlHandler for Application { + fn data_control_state(&self) -> &selection_ext::DataControlState { + &self.ext_data_control_state + } +} + delegate_dmabuf!(Application); delegate_xdg_shell!(Application); delegate_compositor!(Application); @@ -378,6 +444,10 @@ delegate_shm!(Application); delegate_seat!(Application); delegate_data_device!(Application); delegate_output!(Application); +delegate_primary_selection!(Application); +delegate_data_control!(Application); +delegate_ext_data_control!(Application); +delegate_single_pixel_buffer!(Application); const fn wl_transform_to_frame_transform( transform: wl_output::Transform, diff --git a/wayvr/src/backend/wayvr/mod.rs b/wayvr/src/backend/wayvr/mod.rs index 128ec64a..0519c19c 100644 --- a/wayvr/src/backend/wayvr/mod.rs +++ b/wayvr/src/backend/wayvr/mod.rs @@ -15,11 +15,15 @@ use smithay::{ input::{SeatState, keyboard::XkbConfig}, output::{Mode, Output}, reexports::wayland_server::{self, backend::ClientId}, + utils::{Logical, Size}, wayland::{ compositor::{self, SurfaceData, with_states}, dmabuf::{DmabufFeedbackBuilder, DmabufState}, - selection::data_device::DataDeviceState, - shell::xdg::{ToplevelSurface, XdgShellState, XdgToplevelSurfaceData}, + selection::{ + data_device::DataDeviceState, ext_data_control as selection_ext, + primary_selection::PrimarySelectionState, wlr_data_control as selection_wlr, + }, + shell::xdg::{SurfaceCachedState, ToplevelSurface, XdgShellState, XdgToplevelSurfaceData}, shm::ShmState, }, }; @@ -148,8 +152,23 @@ impl WvrServerState { let mut seat_state = SeatState::new(); let shm = ShmState::new::(&dh, Vec::new()); let data_device = DataDeviceState::new::(&dh); + let primary_selection_state = PrimarySelectionState::new::(&dh); let mut seat = seat_state.new_wl_seat(&dh, "wayvr"); + fn filter_allow_any(_: &wayland_server::Client) -> bool { + true + } + let ext_data_control_state = selection_ext::DataControlState::new::( + &dh, + Some(&primary_selection_state), + filter_allow_any, + ); + let wlr_data_control_state = selection_wlr::DataControlState::new::( + &dh, + Some(&primary_selection_state), + filter_allow_any, + ); + let dummy_milli_hz = 60000; /* refresh rate in millihertz */ let output = Output::new( @@ -214,11 +233,15 @@ impl WvrServerState { let state = Application { image_importer: dma_importer, + display_handle: dh, compositor, xdg_shell, seat_state, shm, data_device, + primary_selection_state, + wlr_data_control_state, + ext_data_control_state, wayvr_tasks: tasks.clone(), redraw_requests: HashSet::new(), dmabuf_state, @@ -300,29 +323,50 @@ impl WvrServerState { continue; }; - // Size, icon & fallback title comes from process - let ([size_x, size_y], pos, fallback_title, icon, is_cage) = - match wvr_server.processes.get(&process_handle) { - Some(Process::Managed(p)) => ( - p.resolution, - p.pos_mode, - Some(p.app_name.clone()), - p.icon.as_ref().cloned(), - p.exec_path.ends_with("cage"), - ), - _ => ([1920, 1080], PositionMode::Float, None, None, false), - }; + let (min_size, max_size) = with_states(toplevel.wl_surface(), |state| { + let mut guard = state.cached_state.get::(); + let mut min_size = guard.current().min_size; + let mut max_size = guard.current().max_size; - let window_handle = wvr_server.wm.create_window( - toplevel.clone(), - process_handle, - size_x, - size_y, - ); + if min_size.is_empty() { + min_size = Size::new(1, 1); + } + + if max_size.is_empty() { + max_size = Size::new(4096, 4096); + } + + (min_size, max_size) + }); + + // Size, icon & fallback title comes from process + let (size, pos, fallback_title, icon, is_cage) = + match wvr_server.processes.get(&process_handle) { + Some(Process::Managed(p)) => { + let size: Size = + Size::new(p.resolution[0] as _, p.resolution[1] as _); + ( + size.clamp(min_size, max_size), + p.pos_mode, + Some(p.app_name.clone()), + p.icon.as_ref().cloned(), + p.exec_path.ends_with("cage"), + ) + } + _ => (min_size, PositionMode::Float, None, None, false), + }; let mut title: Arc = fallback_title .unwrap_or_else(|| format!("P{}", client.pid)) .into(); + + let window_handle = wvr_server.wm.create_window( + toplevel.clone(), + process_handle, + size.w as _, + size.h as _, + ); + let mut icon = icon; // Try to get title from xdg_toplevel, unless it's running in cage @@ -374,7 +418,7 @@ impl WvrServerState { app, window_handle, icon, - [size_x, size_y], + [size.w as _, size.h as _], pos, ) .context("Could not create WvrWindow overlay")