1use std::borrow::Cow;
22use std::fs::File;
23use std::time::Instant;
24
25use gbm::{BufferObject, Device as RawGbmDevice};
26use pyo3::prelude::*;
27use pyo3::types::PyBytes;
28use std::sync::Mutex;
29use std::collections::hash_map::DefaultHasher;
30use std::hash::{Hash, Hasher};
31use smithay::backend::renderer::utils::RendererSurfaceState;
32use smithay::backend::allocator::dmabuf::Dmabuf;
33use smithay::backend::allocator::{ Buffer, Fourcc};
34use smithay::backend::renderer::damage::OutputDamageTracker;
35use smithay::backend::renderer::{
36 Bind, ExportMem, gles::GlesRenderer, pixman::PixmanRenderer, ImportDma,
37};
38use smithay::backend::allocator::Modifier;
39use smithay::backend::drm::DrmNode;
40use smithay::output::WeakOutput;
41use smithay::wayland::image_capture_source::{
42 ImageCaptureSource, ImageCaptureSourceHandler, ImageCaptureSourceState,
43 OutputCaptureSourceHandler, OutputCaptureSourceState,
44};
45use smithay::wayland::image_copy_capture::{
46 BufferConstraints, CaptureFailureReason, DmabufConstraints, Frame as CopyFrame,
47 FrameRef as CopyFrameRef, ImageCopyCaptureHandler, ImageCopyCaptureState,
48 Session as CopySession, SessionRef as CopySessionRef,
49};
50use smithay::{
51 delegate_image_capture_source, delegate_image_copy_capture, delegate_output_capture_source,
52};
53use smithay::input::dnd::{DndFocus, Source};
54use std::sync::Arc;
55use crate::wayland::cursor::{Cursor, CursorJob};
56use crate::wayland::keymap::KeymapPolicy;
57use smithay::reexports::wayland_protocols_misc::zwp_virtual_keyboard_v1::server::{
58 zwp_virtual_keyboard_manager_v1::{self, ZwpVirtualKeyboardManagerV1},
59 zwp_virtual_keyboard_v1::{self, ZwpVirtualKeyboardV1},
60};
61use smithay::reexports::wayland_server::{DataInit, Dispatch, GlobalDispatch, New};
62use smithay::wayland::viewporter::ViewporterState;
63use smithay::delegate_viewporter;
64use smithay::wayland::pointer_warp::{PointerWarpHandler, PointerWarpManager};
65use smithay::reexports::wayland_server::protocol::wl_pointer::WlPointer;
66use smithay::reexports::wayland_server::protocol::wl_shm;
67use smithay::wayland::relative_pointer::RelativePointerManagerState;
68use smithay::wayland::pointer_constraints::{PointerConstraintsHandler, PointerConstraintsState};
69use smithay::input::pointer::PointerHandle;
70use smithay::wayland::single_pixel_buffer::SinglePixelBufferState;
71use smithay::delegate_single_pixel_buffer;
72use smithay::desktop::{PopupKind, PopupManager};
73use smithay::wayland::presentation::PresentationState;
74use smithay::delegate_presentation;
75use smithay::wayland::foreign_toplevel_list::{
76 ForeignToplevelHandle, ForeignToplevelListHandler, ForeignToplevelListState,
77};
78use smithay::wayland::shell::xdg::decoration::{
79 XdgDecorationHandler, XdgDecorationState,
80};
81use smithay::desktop::{layer_map_for_output, LayerSurface as DesktopLayerSurface};
82use smithay::wayland::shell::wlr_layer::{
83 WlrLayerShellHandler, WlrLayerShellState, Layer as WlrLayer, LayerSurface as WlrLayerSurface,
84};
85use smithay::delegate_layer_shell;
86use smithay::reexports::wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1::Mode;
87use smithay::{delegate_foreign_toplevel_list, delegate_xdg_decoration};
88use smithay::wayland::selection::wlr_data_control::{DataControlHandler, DataControlState};
89use smithay::wayland::selection::ext_data_control::{
90 DataControlHandler as ExtDataControlHandler, DataControlState as ExtDataControlState,
91};
92use smithay::wayland::cursor_shape::CursorShapeManagerState;
93use smithay::{delegate_cursor_shape, delegate_ext_data_control};
94use smithay::delegate_data_control;
95use smithay::wayland::xdg_activation::{
96 XdgActivationHandler, XdgActivationState, XdgActivationToken, XdgActivationTokenData,
97};
98use smithay::delegate_xdg_activation;
99use smithay::wayland::selection::primary_selection::{
100 set_primary_focus, PrimarySelectionHandler, PrimarySelectionState,
101};
102use smithay::delegate_primary_selection;
103
104use smithay::{
105 delegate_compositor, delegate_data_device, delegate_dmabuf, delegate_fractional_scale,
106 delegate_output, delegate_seat, delegate_shm,
107 delegate_xdg_shell, delegate_relative_pointer, delegate_pointer_warp,
108 delegate_pointer_constraints,
109 desktop::{Space, Window},
110 input::{
111 keyboard::{KeyboardTarget, KeysymHandle, ModifiersState},
112 pointer::{
113 AxisFrame, ButtonEvent, CursorIcon, CursorImageAttributes, CursorImageStatus, GestureHoldBeginEvent,
114 GestureHoldEndEvent, GesturePinchBeginEvent, GesturePinchEndEvent,
115 GesturePinchUpdateEvent, GestureSwipeBeginEvent, GestureSwipeEndEvent,
116 GestureSwipeUpdateEvent, MotionEvent, PointerTarget, RelativeMotionEvent,
117 },
118 touch::{DownEvent, OrientationEvent, ShapeEvent, TouchTarget, UpEvent},
119 Seat, SeatHandler, SeatState,
120 },
121 output::Output,
122 reexports::{
123 wayland_protocols::xdg::shell::server::xdg_toplevel::State as XdgState,
124 wayland_server::{
125 backend::{ClientData, ClientId, DisconnectReason, GlobalId, ObjectId},
126 protocol::{wl_buffer::WlBuffer, wl_surface::WlSurface},
127 Client, DisplayHandle, Resource,
128 },
129 },
130 utils::{Clock, IsAlive, Monotonic, Serial, Rectangle, Point, Logical},
131 wayland::{
132 buffer::BufferHandler,
133 compositor::{
134 with_states, BufferAssignment, CompositorClientState, CompositorHandler,
135 CompositorState, SurfaceAttributes,
136 },
137 dmabuf::{DmabufGlobal, DmabufHandler, DmabufState, ImportNotifier, get_dmabuf},
138 fractional_scale::{FractionalScaleHandler, FractionalScaleManagerState},
139 output::{OutputHandler, OutputManagerState},
140 seat::WaylandFocus,
141 selection::{
142 data_device::{
143 request_data_device_client_selection, set_data_device_focus, DataDeviceHandler,
144 DataDeviceState, WaylandDndGrabHandler,
145 },
146 SelectionHandler, SelectionSource, SelectionTarget,
147 },
148 shell::xdg::{
149 PopupSurface, PositionerState, ToplevelSurface, XdgShellHandler, XdgShellState,
150 XdgToplevelSurfaceData,
151 },
152 shm::{with_buffer_contents, ShmHandler, ShmState, BufferAccessError},
153 },
154};
155
156use crate::encoders::overlay::OverlayState;
157use crate::encoders::vaapi::VaapiEncoder;
158use crate::nvenc::NvencEncoder;
159use crate::{RustCaptureSettings, StripeState};
160
161use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
162
163static SERIAL_COUNTER: AtomicU32 = AtomicU32::new(1);
164
165pub fn next_serial() -> Serial {
175 Serial::from(SERIAL_COUNTER.fetch_add(1, Ordering::SeqCst))
176}
177
178pub fn wayland_time() -> u32 {
187 let mut ts = libc::timespec { tv_sec: 0, tv_nsec: 0 };
188 unsafe {
189 libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts);
190 }
191 (ts.tv_sec as u32).wrapping_mul(1000).wrapping_add((ts.tv_nsec as u32) / 1_000_000)
192}
193
194pub fn wayland_utime() -> u64 {
203 let mut ts = libc::timespec { tv_sec: 0, tv_nsec: 0 };
204 unsafe {
205 libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts);
206 }
207 (ts.tv_sec as u64).wrapping_mul(1_000_000).wrapping_add((ts.tv_nsec as u64) / 1_000)
208}
209
210#[allow(clippy::large_enum_variant)]
215pub enum GpuEncoder {
216 Vaapi(VaapiEncoder),
217 Nvenc(NvencEncoder),
218}
219
220pub struct WlCapture {
225 pub settings: RustCaptureSettings,
226 pub callback: Option<Arc<Py<PyAny>>>,
230 pub video_encoder: Option<GpuEncoder>,
233 pub vaapi_state: StripeState,
234 pub recording_sink: Option<Arc<crate::recording_sink::RecordingSink>>,
235 pub deliver_tx: Option<std::sync::mpsc::SyncSender<Vec<crate::encoders::software::EncodedStripe>>>,
236 pub deliver_join: Option<std::thread::JoinHandle<()>>,
237 pub deliver_discard: Option<Arc<AtomicBool>>,
240 pub pending_hw_delivery: Option<Vec<crate::encoders::software::EncodedStripe>>,
241 pub pending_hw_damage: bool,
242 pub encode_pool: Option<Arc<crate::WlFramePool>>,
243 pub encode_join: Option<std::thread::JoinHandle<Option<GpuEncoder>>>,
244 pub encode_controls: Arc<crate::WlEncodeControls>,
245 pub encode_stats: Arc<crate::WlEncodeStats>,
246 pub pool_last_render: Vec<u64>,
247 pub render_seq: u64,
248 pub pool_content_gen: Vec<u64>,
249 pub content_gen: u64,
250 pub frame_counter: u16,
251 pub pending_force_idr: bool,
252 pub needs_full_render: bool,
253 pub last_tick: Option<Instant>,
256 pub hw_error_streak: u32,
259 pub hw_rebuilt: bool,
263}
264
265impl WlCapture {
266 pub fn request_idr(&mut self) {
272 if self.encode_pool.is_some() {
273 self.encode_controls.force_idr.store(true, Ordering::Relaxed);
274 } else {
275 self.pending_force_idr = true;
276 }
277 self.needs_full_render = true;
278 }
279}
280
281pub struct OutputNode {
286 pub id: u32,
287 pub output: Output,
288 pub global: GlobalId,
289 pub pos: (i32, i32),
294 pub damage_tracker: OutputDamageTracker,
295 pub frame_buffer: Vec<u8>,
298 pub offscreen_buffer: Option<(BufferObject<()>, Dmabuf)>,
300 pub overlay_state: OverlayState,
303 pub capture: Option<WlCapture>,
304 pub frame_seq: u64,
306 pub target_seeded: bool,
310 pub content_hold_until: Option<Instant>,
316}
317
318pub struct PendingHostLayout {
323 pub epoch: u64,
324 pub want: (i32, i32),
325 pub geometry_waiters: Vec<std::sync::mpsc::Sender<(i32, i32, f64)>>,
326}
327
328pub struct OutputCopySession {
333 pub session: CopySession,
334 pub output: WeakOutput,
335 pub pending: Option<CopyFrame>,
336 pub delivered_once: bool,
339}
340
341pub fn windows_on_output(space: &Space<Window>, display_id: u32) -> impl Iterator<Item = &Window> {
345 space.elements().filter(move |w| {
346 window_output_id(w) == display_id
347 && !window_meta(w).map(|m| m.parked.load(Ordering::Relaxed)).unwrap_or(false)
348 })
349}
350
351pub fn output_content_covers(
355 space: &Space<Window>,
356 display_id: u32,
357 logical_w: f64,
358 logical_h: f64,
359) -> bool {
360 windows_on_output(space, display_id).any(|w| {
361 let size = w.geometry().size;
362 size.w as f64 >= logical_w && size.h as f64 >= logical_h
363 })
364}
365
366impl OutputNode {
367 pub fn logical_geometry(&self) -> Option<Rectangle<i32, Logical>> {
370 let mode = self.output.current_mode()?;
371 let scale = self.output.current_scale().fractional_scale();
372 Some(Rectangle::new(
373 Point::from(self.pos),
374 (
375 (mode.size.w as f64 / scale).round() as i32,
376 (mode.size.h as f64 / scale).round() as i32,
377 )
378 .into(),
379 ))
380 }
381}
382
383static NEXT_WINDOW_ID: AtomicU32 = AtomicU32::new(1);
384
385pub struct WindowMeta {
389 pub id: u32,
390 pub output: AtomicU32,
391 pub placed: AtomicBool,
396 pub parked: AtomicBool,
399}
400
401pub const PARKED_POS: (i32, i32) = (-(1 << 20), -(1 << 20));
405
406pub const PARKED_LOGICAL_SIZE: (i32, i32) = (320, 240);
413
414pub fn window_meta(window: &Window) -> Option<&WindowMeta> {
417 window.user_data().get::<WindowMeta>()
418}
419
420pub fn window_output_id(window: &Window) -> u32 {
422 window_meta(window).map(|m| m.output.load(Ordering::Relaxed)).unwrap_or(0)
423}
424
425pub type ScreenshotRequest = (u32, std::sync::mpsc::Sender<Result<Vec<u8>, String>>);
428
429pub struct AppState {
475 pub compositor_state: CompositorState,
476 pub fractional_scale_state: FractionalScaleManagerState,
477 pub viewporter_state: ViewporterState,
478 pub presentation_state: PresentationState,
479 pub shm_state: ShmState,
480 pub single_pixel_buffer: SinglePixelBufferState,
481 pub dmabuf_state: DmabufState,
482 pub dmabuf_global: Option<DmabufGlobal>,
483 pub ext_data_control_state: ExtDataControlState,
484 pub cursor_shape_state: CursorShapeManagerState,
485 pub image_capture_source_state: ImageCaptureSourceState,
486 pub output_capture_source_state: OutputCaptureSourceState,
487 pub image_copy_capture_state: ImageCopyCaptureState,
488 pub copy_sessions: Vec<OutputCopySession>,
489 #[allow(dead_code)]
490 pub output_state: OutputManagerState,
491 pub seat_state: SeatState<AppState>,
492 pub shell_state: XdgShellState,
493 pub layer_shell_state: WlrLayerShellState,
494 pub space: Space<Window>,
495 pub data_device_state: DataDeviceState,
496 pub data_control_state: DataControlState,
497 pub dh: DisplayHandle,
498 #[allow(dead_code)]
499 pub seat: Seat<AppState>,
500 pub output_nodes: Vec<OutputNode>,
503 pub pending_windows: Vec<Window>,
504
505 pub foreign_toplevel_list: ForeignToplevelListState,
506 pub xdg_decoration_state: XdgDecorationState,
507 pub xdg_activation_state: XdgActivationState,
508 pub primary_selection_state: PrimarySelectionState,
509 pub popups: PopupManager,
510
511 pub gles_renderer: Option<GlesRenderer>,
512 pub pixman_renderer: Option<PixmanRenderer>,
513
514 pub gbm_device: Option<RawGbmDevice<File>>,
515
516 pub settings: RustCaptureSettings,
519 pub cursor_callback_set: bool,
522 pub cursor_tx: std::sync::mpsc::Sender<CursorJob>,
524 pub clipboard_callback: Option<Py<PyAny>>,
525 pub pending_clipboard_read: Option<String>,
526 pub current_selection_mime: Option<String>,
530
531 pub last_log_time: Instant,
532 pub start_time: Instant,
533 pub clock: Clock<Monotonic>,
534
535 pub use_gpu: bool,
536
537 pub cursor_helper: Cursor,
538
539 pub keymap_policy: KeymapPolicy,
542 pub host: Option<crate::wayland::host::HostSession>,
545 pub host_layout_pending: std::collections::HashMap<u32, PendingHostLayout>,
550
551 pub current_cursor_icon: Option<CursorImageStatus>,
552 pub cursor_surface_pending: bool,
557 pub cursor_buffer: Option<WlBuffer>,
558 pub render_cursor_on_framebuffer: bool,
559 pub pointer_warp_state: PointerWarpManager,
560 pub relative_pointer_state: RelativePointerManagerState,
561 pub pointer_constraints_state: PointerConstraintsState,
562 pub render_node_path: String,
563 pub auto_gpu_selected: bool,
564 pub pending_screenshot: Option<ScreenshotRequest>,
567 pub command_rx: Option<smithay::reexports::calloop::channel::Channel<crate::ThreadCommand>>,
571 pub last_input_at: Option<Instant>,
575 pub frame_idle_long: bool,
580 pub last_idle_service_at: Option<Instant>,
583 pub deliver_reaper: Vec<std::thread::JoinHandle<()>>,
587 pub encode_reaper: Vec<std::thread::JoinHandle<Option<crate::GpuEncoder>>>,
591}
592
593impl PointerConstraintsHandler for AppState {
597 fn new_constraint(&mut self, _surface: &WlSurface, _pointer: &PointerHandle<Self>) {}
598
599 fn cursor_position_hint(
600 &mut self,
601 _surface: &WlSurface,
602 _pointer: &PointerHandle<Self>,
603 _location: Point<f64, Logical>,
604 ) {}
605}
606
607impl ForeignToplevelListHandler for AppState {
610 fn foreign_toplevel_list_state(&mut self) -> &mut ForeignToplevelListState {
611 &mut self.foreign_toplevel_list
612 }
613}
614
615impl XdgActivationHandler for AppState {
622 fn activation_state(&mut self) -> &mut XdgActivationState {
623 &mut self.xdg_activation_state
624 }
625
626 fn token_created(&mut self, _token: XdgActivationToken, _data: XdgActivationTokenData) -> bool {
627 true
628 }
629
630 fn request_activation(
631 &mut self,
632 _token: XdgActivationToken,
633 token_data: XdgActivationTokenData,
634 surface: WlSurface,
635 ) {
636 if token_data.timestamp.elapsed().as_secs() < 10 {
637 let window = self.space.elements().find(|w| w.wl_surface().as_deref() == Some(&surface)).cloned();
638 if let Some(window) = window {
639 self.space.raise_element(&window, true);
640 }
641 }
642 }
643}
644
645impl PrimarySelectionHandler for AppState {
650 fn primary_selection_state(&mut self) -> &mut PrimarySelectionState {
651 &mut self.primary_selection_state
652 }
653}
654
655impl XdgDecorationHandler for AppState {
665 fn new_decoration(&mut self, toplevel: ToplevelSurface) {
666 toplevel.with_pending_state(|state| {
667 state.decoration_mode = Some(Mode::ServerSide);
668 });
669 toplevel.send_configure();
670 }
671
672 fn request_mode(&mut self, toplevel: ToplevelSurface, mode: Mode) {
673 toplevel.with_pending_state(|state| {
674 state.decoration_mode = Some(mode);
675 });
676 toplevel.send_configure();
677 }
678
679 fn unset_mode(&mut self, toplevel: ToplevelSurface) {
680 toplevel.with_pending_state(|state| {
681 state.decoration_mode = Some(Mode::ServerSide);
682 });
683 toplevel.send_configure();
684 }
685}
686
687impl WlrLayerShellHandler for AppState {
693 fn shell_state(&mut self) -> &mut WlrLayerShellState {
694 &mut self.layer_shell_state
695 }
696
697 fn new_layer_surface(
698 &mut self,
699 surface: WlrLayerSurface,
700 output: Option<smithay::reexports::wayland_server::protocol::wl_output::WlOutput>,
701 _layer: WlrLayer,
702 namespace: String,
703 ) {
704 let smithay_output = if let Some(wlo) = output.as_ref() {
705 self.output_nodes.iter().map(|n| &n.output).find(|o| o.owns(wlo))
706 } else {
707 self.primary_output()
708 };
709
710 if let Some(output) = smithay_output {
711 let mode = output.current_mode().unwrap();
712
713 surface.with_pending_state(|state| {
714 state.size = Some(((mode.size.w as f64) as i32, (mode.size.h as f64) as i32).into());
715 });
716 surface.send_configure();
717
718 let layer = DesktopLayerSurface::new(surface, namespace);
719 let _ = layer_map_for_output(output).map_layer(&layer);
720 }
721 }
722
723 fn layer_destroyed(&mut self, _surface: WlrLayerSurface) {}
724}
725
726impl CompositorHandler for AppState {
729 fn compositor_state(&mut self) -> &mut CompositorState {
730 &mut self.compositor_state
731 }
732 fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState {
733 &client.get_data::<ClientState>().unwrap().compositor_state
734 }
735
736 fn commit(&mut self, surface: &WlSurface) {
768 smithay::backend::renderer::utils::on_commit_buffer_handler::<Self>(surface);
769
770 for node in &self.output_nodes {
771 let mut layer_map = layer_map_for_output(&node.output);
772 let found = layer_map.layers().any(|layer| layer.wl_surface() == surface);
773 if found {
774 layer_map.arrange();
775 break;
776 }
777 }
778
779 if let Some(CursorImageStatus::Surface(ref cursor_surface)) = self.current_cursor_icon
780 && cursor_surface == surface {
781 let status = CursorImageStatus::Surface(surface.clone());
782 self.cursor_surface_pending = false;
783 self.send_cursor_image(&status);
784 }
785
786 if let Some(handle) = with_states(surface, |states| states.data_map.get::<ForeignToplevelHandle>().cloned())
787 && let Some(window) = self.space.elements().find(|w| w.wl_surface().as_deref() == Some(surface))
788 && let Some(_toplevel) = window.toplevel() {
789 let (title, app_id) = with_states(surface, |states| {
790 let attributes = states.data_map.get::<XdgToplevelSurfaceData>().unwrap().lock().unwrap();
791 (attributes.title.clone(), attributes.app_id.clone())
792 });
793
794 handle.send_title(&title.unwrap_or_default());
795 handle.send_app_id(&app_id.unwrap_or_default());
796 handle.send_done();
797 }
798
799 let mapped = self
800 .space
801 .elements()
802 .find(|w| w.toplevel().map(|tl| tl.wl_surface() == surface).unwrap_or(false))
803 .cloned();
804 if let Some(window) = mapped {
805 window.on_commit();
806 let has_buffer = smithay::backend::renderer::utils::with_renderer_surface_state(
810 surface,
811 |s| s.buffer().is_some(),
812 )
813 .unwrap_or(false);
814 if !has_buffer {
815 self.space.unmap_elem(&window);
816 self.pending_windows.push(window);
817 return;
818 }
819 }
820
821 if let Some(idx) = self.pending_windows.iter().position(|w| {
822 w.toplevel().map(|tl| tl.wl_surface() == surface).unwrap_or(false)
823 }) {
824 let window = self.pending_windows.remove(idx);
825 let toplevel = window.toplevel().unwrap();
826
827 let needs_placement = window_meta(&window)
832 .map(|meta| !meta.placed.swap(true, Ordering::Relaxed))
833 .unwrap_or(false);
834
835 if needs_placement {
836 let mut target_id = self.pointer_display();
840 let mut park = false;
846 if self.would_cover_screen(&window, target_id) {
847 let empty = self.output_nodes.iter().map(|n| n.id).find(|oid| {
848 !self
849 .space
850 .elements()
851 .chain(self.pending_windows.iter())
852 .any(|w| window_output_id(w) == *oid)
853 });
854 match empty {
855 Some(oid) => target_id = oid,
856 None => park = true,
857 }
858 }
859 if let Some(meta) = window_meta(&window) {
860 meta.output.store(target_id, Ordering::Relaxed);
861 meta.parked.store(park, Ordering::Relaxed);
862 }
863 let (logical_width, logical_height) = if park {
864 PARKED_LOGICAL_SIZE
865 } else if let Some(size) = self.logical_size_of(target_id) {
866 size
867 } else {
868 let scale = self.settings.scale.max(0.1);
869 (
870 (self.settings.width as f64 / scale).round() as i32,
871 (self.settings.height as f64 / scale).round() as i32,
872 )
873 };
874
875 toplevel.with_pending_state(|state| {
876 state.states.set(XdgState::Activated);
877 state.states.set(XdgState::Fullscreen);
878 state.size = Some((logical_width, logical_height).into());
879 });
880 toplevel.send_configure();
881
882 self.pending_windows.push(window);
883 } else if smithay::backend::renderer::utils::with_renderer_surface_state(
884 surface,
885 |s| s.buffer().is_none(),
886 )
887 .unwrap_or(true)
888 {
889 let tl = toplevel.clone();
895 self.pending_windows.push(window);
896 self.send_forced_fullscreen_configure(&tl);
897 } else {
898 let target_id = window_output_id(&window);
899 let parked = window_meta(&window)
900 .map(|meta| meta.parked.load(Ordering::Relaxed))
901 .unwrap_or(false);
902 let node_idx = self.node_idx_for_id(target_id).unwrap_or(0);
903 let (target_output, pos) = {
904 let node = &self.output_nodes[node_idx];
905 (node.output.clone(), node.pos)
906 };
907 self.space.map_element(
910 window.clone(),
911 if parked { PARKED_POS } else { pos },
912 !parked,
913 );
914 window.on_commit();
915
916 if !parked {
917 target_output.enter(surface);
918 let scale = target_output.current_scale().fractional_scale();
919 with_states(surface, |states| {
920 smithay::wayland::compositor::send_surface_state(
921 surface, states, scale.ceil() as i32, smithay::utils::Transform::Normal,
922 );
923 smithay::wayland::fractional_scale::with_fractional_scale(states, |fs| {
924 fs.set_preferred_scale(scale);
925 });
926 });
927
928 if let Some(geo_out) = self.output_nodes[node_idx].logical_geometry() {
929 let (expected_w, expected_h) = (geo_out.size.w, geo_out.size.h);
930 let geo = window.geometry();
931 if (geo.size.w - expected_w).abs() > 1
932 || (geo.size.h - expected_h).abs() > 1
933 {
934 toplevel.with_pending_state(|state| {
935 state.states.set(XdgState::Activated);
936 state.states.set(XdgState::Fullscreen);
937 state.size = Some((expected_w, expected_h).into());
938 });
939 toplevel.send_configure();
940 }
941 }
942
943 let serial = next_serial();
944 let target = FocusTarget::Window(window.clone());
945 if let Some(keyboard) = self.seat.get_keyboard() {
946 keyboard.set_focus(self, Some(target.clone()), serial);
947 }
948 } else {
949 let geo = window.geometry();
954 if (geo.size.w - PARKED_LOGICAL_SIZE.0).abs() > 1
955 || (geo.size.h - PARKED_LOGICAL_SIZE.1).abs() > 1
956 {
957 let tl = toplevel.clone();
958 self.send_forced_fullscreen_configure(&tl);
959 }
960 }
961 }
962 }
963 }
964}
965
966
967impl AppState {
968 pub(crate) fn primary_output(&self) -> Option<&Output> {
970 self.output_nodes.first().map(|n| &n.output)
971 }
972
973 pub(crate) fn node_idx_for_id(&self, id: u32) -> Option<usize> {
974 self.output_nodes.iter().position(|n| n.id == id)
975 }
976
977 pub(crate) fn node_idx_under(&self, p: Point<f64, Logical>) -> Option<usize> {
979 self.output_nodes.iter().position(|n| {
980 n.logical_geometry()
981 .map(|g| g.to_f64().contains(p))
982 .unwrap_or(false)
983 })
984 }
985
986 pub(crate) fn layout_physical_to_logical(&self, x: f64, y: f64) -> Point<f64, Logical> {
991 let mut best: Option<(f64, Point<f64, Logical>)> = None;
992 for node in &self.output_nodes {
993 let Some(mode) = node.output.current_mode() else { continue };
994 let scale = node.output.current_scale().fractional_scale();
995 let (px, py) = (node.pos.0 as f64, node.pos.1 as f64);
996 let cx = x.max(px).min(px + mode.size.w as f64 - 1.0);
997 let cy = y.max(py).min(py + mode.size.h as f64 - 1.0);
998 let d2 = (x - cx).powi(2) + (y - cy).powi(2);
999 let logical = Point::from((
1000 node.pos.0 as f64 + (cx - px) / scale,
1001 node.pos.1 as f64 + (cy - py) / scale,
1002 ));
1003 if best.as_ref().map(|(bd, _)| d2 < *bd).unwrap_or(true) {
1004 best = Some((d2, logical));
1005 }
1006 }
1007 best.map(|(_, p)| p).unwrap_or_else(|| (0.0, 0.0).into())
1008 }
1009
1010 pub(crate) fn clamp_logical(&self, p: Point<f64, Logical>) -> Point<f64, Logical> {
1012 let mut best: Option<(f64, Point<f64, Logical>)> = None;
1013 for node in &self.output_nodes {
1014 let Some(geo) = node.logical_geometry() else { continue };
1015 let scale = node.output.current_scale().fractional_scale();
1016 let g = geo.to_f64();
1017 let margin = 1.0 / scale.max(0.1);
1018 let cx = p.x.max(g.loc.x).min(g.loc.x + g.size.w - margin);
1019 let cy = p.y.max(g.loc.y).min(g.loc.y + g.size.h - margin);
1020 let d2 = (p.x - cx).powi(2) + (p.y - cy).powi(2);
1021 if best.as_ref().map(|(bd, _)| d2 < *bd).unwrap_or(true) {
1022 best = Some((d2, (cx, cy).into()));
1023 }
1024 }
1025 best.map(|(_, p)| p).unwrap_or(p)
1026 }
1027
1028 pub(crate) fn layout_logical_to_physical(&self, p: Point<f64, Logical>) -> (f64, f64) {
1031 let idx = self.node_idx_under(p).unwrap_or(0);
1032 let Some(node) = self.output_nodes.get(idx) else { return (p.x, p.y) };
1033 let scale = node.output.current_scale().fractional_scale();
1034 (
1035 node.pos.0 as f64 + (p.x - node.pos.0 as f64) * scale,
1036 node.pos.1 as f64 + (p.y - node.pos.1 as f64) * scale,
1037 )
1038 }
1039
1040 pub(crate) fn logical_size_of(&self, id: u32) -> Option<(i32, i32)> {
1042 let idx = self.node_idx_for_id(id)?;
1043 let geo = self.output_nodes[idx].logical_geometry()?;
1044 Some((geo.size.w, geo.size.h))
1045 }
1046
1047 pub(crate) fn pointer_display(&self) -> u32 {
1049 self.seat
1050 .get_pointer()
1051 .map(|p| p.current_location())
1052 .and_then(|pos| self.node_idx_under(pos))
1053 .map(|idx| self.output_nodes[idx].id)
1054 .unwrap_or(0)
1055 }
1056
1057 pub(crate) fn would_cover_screen(&self, window: &Window, id: u32) -> bool {
1061 let Some(client) = window.wl_surface().and_then(|s| s.client()) else { return false };
1062 self.space
1063 .elements()
1064 .chain(self.pending_windows.iter())
1065 .filter(|w| !std::ptr::eq(*w, window) && w.wl_surface() != window.wl_surface())
1066 .any(|w| {
1067 window_output_id(w) == id
1068 && !window_meta(w).map(|m| m.parked.load(Ordering::Relaxed)).unwrap_or(false)
1069 && w.wl_surface()
1070 .and_then(|s| s.client())
1071 .is_some_and(|c| c.id() == client.id())
1072 })
1073 }
1074
1075 pub(crate) fn park_window(&mut self, window: &Window, id: u32) {
1079 let old_id = window_output_id(window);
1080 if let Some(meta) = window_meta(window) {
1081 meta.output.store(id, Ordering::Relaxed);
1082 meta.parked.store(true, Ordering::Relaxed);
1083 }
1084 if let (Some(surface), Some(idx)) = (window.wl_surface(), self.node_idx_for_id(old_id)) {
1085 self.output_nodes[idx].output.leave(&surface);
1086 }
1087 self.space.map_element(window.clone(), PARKED_POS, false);
1088 if let Some(toplevel) = window.toplevel() {
1089 let toplevel = toplevel.clone();
1090 self.send_forced_fullscreen_configure(&toplevel);
1091 }
1092 }
1093
1094 pub(crate) fn place_window_on_output(&mut self, window: &Window, id: u32) -> bool {
1098 let Some(idx) = self.node_idx_for_id(id) else { return false };
1099 let old_id = window_output_id(window);
1100 let (new_output, pos) = {
1101 let node = &self.output_nodes[idx];
1102 (node.output.clone(), node.pos)
1103 };
1104 let old_output = self
1105 .node_idx_for_id(old_id)
1106 .map(|i| self.output_nodes[i].output.clone());
1107 if let Some(meta) = window_meta(window) {
1108 meta.output.store(id, Ordering::Relaxed);
1109 meta.parked.store(false, Ordering::Relaxed);
1110 }
1111 self.space.map_element(window.clone(), pos, true);
1112 if let Some(surface) = window.wl_surface() {
1113 if let Some(old) = old_output
1114 && old_id != id {
1115 old.leave(&surface);
1116 }
1117 new_output.enter(&surface);
1118 let scale = new_output.current_scale().fractional_scale();
1119 with_states(&surface, |states| {
1120 smithay::wayland::compositor::send_surface_state(
1121 &surface, states, scale.ceil() as i32, smithay::utils::Transform::Normal,
1122 );
1123 smithay::wayland::fractional_scale::with_fractional_scale(states, |fs| {
1124 fs.set_preferred_scale(scale);
1125 });
1126 });
1127 }
1128 if let Some(toplevel) = window.toplevel() {
1129 let toplevel = toplevel.clone();
1130 self.send_forced_fullscreen_configure(&toplevel);
1131 }
1132 true
1133 }
1134
1135 pub(crate) fn process_pending_clipboard_read(&mut self) {
1148 let Some(mime) = self.pending_clipboard_read.take() else { return };
1149 if crate::PY_SHUTDOWN.load(std::sync::atomic::Ordering::Relaxed) {
1150 return;
1151 }
1152 let Some(cb) = self
1153 .clipboard_callback
1154 .as_ref()
1155 .map(|c| Python::attach(|py| c.clone_ref(py)))
1156 else {
1157 return;
1158 };
1159 let Ok((reader, writer)) = std::io::pipe() else { return };
1160 if request_data_device_client_selection::<AppState>(&self.seat, mime.clone(), writer.into())
1161 .is_err()
1162 {
1163 return;
1164 }
1165 std::thread::spawn(move || {
1166 use std::io::Read;
1167 use std::os::fd::AsRawFd;
1168 const CAP: usize = 64 * 1024 * 1024;
1169 const IDLE_DEADLINE: std::time::Duration = std::time::Duration::from_secs(10);
1170 let mut last_data = Instant::now();
1171 let mut buf = Vec::new();
1172 let mut chunk = [0u8; 65536];
1173 loop {
1174 let Some(remaining) = IDLE_DEADLINE.checked_sub(last_data.elapsed()) else {
1175 return;
1176 };
1177 let mut pfd = libc::pollfd {
1178 fd: reader.as_raw_fd(),
1179 events: libc::POLLIN,
1180 revents: 0,
1181 };
1182 let timeout_ms = remaining.as_millis().min(i32::MAX as u128).max(1) as i32;
1183 let ready = unsafe { libc::poll(&mut pfd, 1, timeout_ms) };
1184 if ready < 0 {
1185 if std::io::Error::last_os_error().kind() == std::io::ErrorKind::Interrupted {
1186 continue;
1187 }
1188 return;
1189 }
1190 if ready == 0 {
1191 continue;
1192 }
1193 match (&reader).read(&mut chunk) {
1194 Ok(0) => break,
1195 Ok(n) => {
1196 last_data = Instant::now();
1197 let room = CAP - buf.len();
1198 let take_n = n.min(room);
1199 buf.extend_from_slice(&chunk[..take_n]);
1200 if buf.len() == CAP {
1201 break;
1202 }
1203 }
1204 Err(e) if e.kind() == std::io::ErrorKind::Interrupted
1205 || e.kind() == std::io::ErrorKind::WouldBlock => continue,
1206 Err(_) => return,
1207 }
1208 }
1209 if buf.is_empty() {
1210 return;
1211 }
1212 if crate::PY_SHUTDOWN.load(std::sync::atomic::Ordering::Relaxed) {
1213 return;
1214 }
1215 Python::attach(|py| {
1216 let bytes = PyBytes::new(py, &buf);
1217 let _ = cb.call1(py, (mime.as_str(), bytes));
1218 });
1219 });
1220 }
1221
1222
1223 pub(crate) fn send_cursor_image(&mut self, image: &CursorImageStatus) {
1242 if !self.cursor_callback_set {
1243 return;
1244 }
1245 let job = match image {
1246 CursorImageStatus::Named(icon) => {
1247 self.cursor_buffer = None;
1248 CursorJob::Named { name: cursor_icon_to_str(icon) }
1249 }
1250 CursorImageStatus::Hidden => {
1251 self.cursor_buffer = None;
1252 CursorJob::Hide
1253 }
1254 CursorImageStatus::Surface(surface) => {
1255 let mut hot_x = 0;
1256 let mut hot_y = 0;
1257 let mut is_cursor_role = false;
1258
1259 with_states(surface, |states| {
1262 if states.role == Some("cursor_image") {
1263 is_cursor_role = true;
1264 }
1265 let buffer_scale = states
1266 .cached_state
1267 .get::<SurfaceAttributes>()
1268 .current()
1269 .buffer_scale
1270 .max(1);
1271 if let Some(attributes) = states.data_map.get::<Mutex<CursorImageAttributes>>()
1272 && let Ok(guard) = attributes.lock() {
1273 hot_x = guard.hotspot.x * buffer_scale;
1274 hot_y = guard.hotspot.y * buffer_scale;
1275 }
1276 });
1277
1278 if !is_cursor_role {
1279 return;
1280 }
1281
1282 let buffer_found = with_states(surface, |states| {
1283 let mut attrs = states.cached_state.get::<SurfaceAttributes>();
1284
1285 if let Some(BufferAssignment::NewBuffer(b)) = &attrs.current().buffer {
1286 return Some(b.clone());
1287 }
1288
1289 if let Some(mutex) = states.data_map.get::<Mutex<RendererSurfaceState>>()
1290 && let Ok(renderer_state) = mutex.try_lock()
1291 && let Some(b) = renderer_state.buffer() {
1292 let wl_buffer: &wayland_server::protocol::wl_buffer::WlBuffer = b;
1293 return Some(wl_buffer.clone());
1294 }
1295 None
1296 });
1297
1298 let Some(buffer) = buffer_found else { return };
1299
1300 let shm_result = with_buffer_contents(&buffer, |ptr, len, spec| {
1301 let slice = unsafe { std::slice::from_raw_parts(ptr, len) };
1302 let mut hasher = DefaultHasher::new();
1303 spec.width.hash(&mut hasher);
1304 spec.height.hash(&mut hasher);
1305 spec.stride.hash(&mut hasher);
1306 spec.offset.hash(&mut hasher);
1307 spec.format.hash(&mut hasher);
1308 let start = (spec.offset.max(0) as usize).min(len);
1309 let span = (spec.stride.max(0) as usize)
1310 .saturating_mul(spec.height.max(0) as usize);
1311 let end = start.saturating_add(span).min(len);
1312 slice[start..end].hash(&mut hasher);
1313 let hash = hasher.finish();
1314 (hash, spec.width, spec.height, spec.stride, spec.format, spec.offset, slice.to_vec())
1315 });
1316
1317 let job = match shm_result {
1318 Ok((hash, width, height, stride, format, buf_offset, raw_bytes)) => {
1319 Some(CursorJob::Shm {
1320 hash,
1321 width,
1322 height,
1323 stride,
1324 offset: buf_offset,
1325 opaque: format == wl_shm::Format::Xrgb8888,
1326 bytes: raw_bytes,
1327 hot_x,
1328 hot_y,
1329 })
1330 }
1331 Err(BufferAccessError::NotManaged) => {
1332 let mut gles_job = None;
1333 let dmabuf_opt = get_dmabuf(&buffer).ok().cloned();
1334 if let Some(mut dmabuf) = dmabuf_opt
1335 && let Some(renderer) = self.gles_renderer.as_mut() {
1336 let width = dmabuf.width() as i32;
1337 let height = dmabuf.height() as i32;
1338
1339 match renderer.bind(&mut dmabuf) {
1340 Ok(frame) => {
1341 let rect = Rectangle::new((0, 0).into(), (width, height).into());
1342 match renderer.copy_framebuffer(&frame, rect, Fourcc::Abgr8888) {
1343 Ok(mapping) => match renderer.map_texture(&mapping) {
1344 Ok(data) => {
1345 let mut hasher = DefaultHasher::new();
1346 data.hash(&mut hasher);
1347 gles_job = Some(CursorJob::Gles {
1348 hash: hasher.finish(),
1349 width,
1350 height,
1351 bytes: data.to_vec(),
1352 hot_x,
1353 hot_y,
1354 });
1355 }
1356 Err(e) => eprintln!("Failed to map texture: {:?}", e),
1357 },
1358 Err(e) => eprintln!("Failed to copy framebuffer: {:?}", e),
1359 }
1360 }
1361 Err(e) => eprintln!("Failed to bind dmabuf to renderer: {:?}", e),
1362 }
1363 }
1364 gles_job
1365 }
1366 Err(_) => None,
1367 };
1368
1369 self.cursor_buffer = Some(buffer);
1370 let Some(job) = job else { return };
1371 job
1372 }
1373 };
1374 let _ = self.cursor_tx.send(job);
1375 }
1376
1377 pub(crate) fn flush_pending_cursor(&mut self) {
1380 if !self.cursor_surface_pending {
1381 return;
1382 }
1383 self.cursor_surface_pending = false;
1384 if let Some(icon) = self.current_cursor_icon.clone() {
1385 self.send_cursor_image(&icon);
1386 }
1387 }
1388
1389 pub(crate) fn apply_keymap_policy(&mut self) {
1392 let text = self.keymap_policy.keymap_text();
1393 self.apply_keymap_text(text);
1394 }
1395
1396 pub(crate) fn apply_keymap_text(&mut self, text: String) {
1399 if text.is_empty() {
1400 return;
1401 }
1402 if let Some(host) = &self.host {
1406 host.set_keymap(&text);
1407 }
1408 if let Some(keyboard) = self.seat.get_keyboard()
1409 && let Err(e) = keyboard.set_keymap_from_string(self, text) {
1410 eprintln!("[Wayland] keymap swap failed: {e:?}");
1411 }
1412 }
1413
1414 pub(crate) fn bind_keysyms(&mut self, keysyms: &[u32]) -> Vec<(u32, u32)> {
1419 let pressed: std::collections::HashSet<u32> = self
1420 .seat
1421 .get_keyboard()
1422 .map(|k| k.pressed_keys().iter().map(|c| c.raw()).collect())
1423 .unwrap_or_default();
1424 let (out, changed) = self.keymap_policy.bind_many(keysyms, &pressed);
1425 if changed {
1426 self.apply_keymap_policy();
1427 }
1428 out
1429 }
1430
1431 pub(crate) fn bind_keysyms_plain(&mut self, keysyms: &[u32]) -> Vec<u32> {
1435 let pressed: std::collections::HashSet<u32> = self
1436 .seat
1437 .get_keyboard()
1438 .map(|k| k.pressed_keys().iter().map(|c| c.raw()).collect())
1439 .unwrap_or_default();
1440 let (out, changed) = self.keymap_policy.bind_many_plain(keysyms, &pressed);
1441 if changed {
1442 self.apply_keymap_policy();
1443 }
1444 out
1445 }
1446
1447 pub(crate) fn send_forced_fullscreen_configure(&mut self, toplevel: &ToplevelSurface) {
1453 let output_id = self
1454 .space
1455 .elements()
1456 .chain(self.pending_windows.iter())
1457 .find(|w| w.toplevel().map(|t| t == toplevel).unwrap_or(false))
1458 .map(window_output_id)
1459 .unwrap_or(0);
1460 let parked = self
1461 .space
1462 .elements()
1463 .chain(self.pending_windows.iter())
1464 .find(|w| w.toplevel().map(|t| t == toplevel).unwrap_or(false))
1465 .and_then(window_meta)
1466 .map(|meta| meta.parked.load(Ordering::Relaxed))
1467 .unwrap_or(false);
1468 let (logical_width, logical_height) = if parked {
1469 PARKED_LOGICAL_SIZE
1470 } else if let Some(size) = self.logical_size_of(output_id) {
1471 size
1472 } else {
1473 let scale = self.settings.scale.max(0.1);
1474 (
1475 (self.settings.width as f64 / scale).round() as i32,
1476 (self.settings.height as f64 / scale).round() as i32,
1477 )
1478 };
1479 toplevel.with_pending_state(|state| {
1480 state.states.set(XdgState::Fullscreen);
1481 state.states.set(XdgState::Activated);
1482 state.size = Some((logical_width, logical_height).into());
1483 });
1484 toplevel.send_configure();
1485 }
1486}
1487
1488const CLIPBOARD_MIME_PREFERENCE: &[&str] = &[
1491 "image/png",
1492 "image/jpeg",
1493 "image/webp",
1494 "image/bmp",
1495 "image/svg+xml",
1496 "image/svg",
1497 "text/plain;charset=utf-8",
1498 "UTF8_STRING",
1499 "text/plain",
1500 "STRING",
1501 "TEXT",
1502];
1503
1504impl SelectionHandler for AppState {
1508 type SelectionUserData = std::sync::Arc<(String, Vec<u8>)>;
1509
1510 fn new_selection(
1519 &mut self,
1520 ty: SelectionTarget,
1521 source: Option<SelectionSource>,
1522 seat: Seat<Self>,
1523 ) {
1524 if ty != SelectionTarget::Clipboard {
1525 return;
1526 }
1527 let Some(source) = source else {
1528 self.current_selection_mime = None;
1529 return;
1530 };
1531 let mimes = source.mime_types();
1532 let mime = CLIPBOARD_MIME_PREFERENCE
1533 .iter()
1534 .find(|want| mimes.iter().any(|m| m == *want))
1535 .map(|s| s.to_string());
1536 self.current_selection_mime = mime.clone();
1539 if crate::PY_SHUTDOWN.load(std::sync::atomic::Ordering::Relaxed) {
1540 return;
1541 }
1542 if self.clipboard_callback.is_none() {
1543 return;
1544 }
1545 let Some(mime) = mime else { return };
1546 self.pending_clipboard_read = Some(mime);
1547 let _ = seat;
1548 }
1549
1550 fn send_selection(
1554 &mut self,
1555 ty: SelectionTarget,
1556 _mime_type: String,
1557 fd: std::os::fd::OwnedFd,
1558 _seat: Seat<Self>,
1559 user_data: &Self::SelectionUserData,
1560 ) {
1561 if ty != SelectionTarget::Clipboard && ty != SelectionTarget::Primary {
1562 return;
1563 }
1564 let payload = user_data.clone();
1565 std::thread::spawn(move || {
1566 let _ = crate::wayland::wlclient::write_fd_all(
1567 &fd,
1568 &payload.1,
1569 crate::wayland::wlclient::IO_TIMEOUT,
1570 );
1571 });
1572 }
1573}
1574
1575impl DataDeviceHandler for AppState {
1578 fn data_device_state(&mut self) -> &mut DataDeviceState {
1579 &mut self.data_device_state
1580 }
1581}
1582impl DataControlHandler for AppState {
1585 fn data_control_state(&mut self) -> &mut DataControlState {
1586 &mut self.data_control_state
1587 }
1588}
1589impl smithay::wayland::tablet_manager::TabletSeatHandler for AppState {}
1592
1593impl ExtDataControlHandler for AppState {
1597 fn data_control_state(&mut self) -> &mut ExtDataControlState {
1598 &mut self.ext_data_control_state
1599 }
1600}
1601
1602impl WaylandDndGrabHandler for AppState {}
1604impl BufferHandler for AppState {
1606 fn buffer_destroyed(&mut self, _buffer: &WlBuffer) {}
1607}
1608impl ShmHandler for AppState {
1610 fn shm_state(&self) -> &ShmState {
1611 &self.shm_state
1612 }
1613}
1614impl OutputHandler for AppState {}
1616
1617impl DmabufHandler for AppState {
1623 fn dmabuf_state(&mut self) -> &mut DmabufState {
1624 &mut self.dmabuf_state
1625 }
1626
1627 fn dmabuf_imported(
1628 &mut self,
1629 _global: &DmabufGlobal,
1630 dmabuf: Dmabuf,
1631 notifier: ImportNotifier,
1632 ) {
1633 if let Some(renderer) = self.gles_renderer.as_mut() {
1634 if renderer.import_dmabuf(&dmabuf, None).is_ok() {
1635 let _ = notifier.successful::<AppState>();
1636 } else {
1637 notifier.failed();
1638 }
1639 } else {
1640 notifier.failed();
1641 }
1642 }
1643}
1644
1645impl ImageCaptureSourceHandler for AppState {
1646 fn source_destroyed(&mut self, _source: ImageCaptureSource) {}
1647}
1648
1649impl OutputCaptureSourceHandler for AppState {
1650 fn output_capture_source_state(&mut self) -> &mut OutputCaptureSourceState {
1651 &mut self.output_capture_source_state
1652 }
1653
1654 fn output_source_created(&mut self, source: ImageCaptureSource, output: &Output) {
1655 source.user_data().insert_if_missing(|| output.downgrade());
1656 }
1657}
1658
1659impl ImageCopyCaptureHandler for AppState {
1665 fn image_copy_capture_state(&mut self) -> &mut ImageCopyCaptureState {
1666 &mut self.image_copy_capture_state
1667 }
1668
1669 fn capture_constraints(&mut self, source: &ImageCaptureSource) -> Option<BufferConstraints> {
1670 let output = source.user_data().get::<WeakOutput>()?.upgrade()?;
1671 output_capture_constraints(&output, self.gles_renderer.as_ref(), &self.render_node_path)
1672 }
1673
1674 fn new_session(&mut self, session: CopySession) {
1675 let Some(output) = session.source().user_data().get::<WeakOutput>().cloned() else {
1676 session.stop();
1677 return;
1678 };
1679 match self.capture_constraints(&session.source()) {
1680 Some(constraints) => {
1681 session.update_constraints(constraints);
1682 self.copy_sessions.push(OutputCopySession {
1683 session,
1684 output,
1685 pending: None,
1686 delivered_once: false,
1687 });
1688 }
1689 None => session.stop(),
1690 }
1691 }
1692
1693 fn frame(&mut self, session: &CopySessionRef, frame: CopyFrame) {
1694 match self.copy_sessions.iter_mut().find(|cs| cs.session == *session) {
1695 Some(cs) => cs.pending = Some(frame),
1696 None => frame.fail(CaptureFailureReason::Unknown),
1697 }
1698 }
1699
1700 fn frame_aborted(&mut self, frame: CopyFrameRef) {
1701 for cs in self.copy_sessions.iter_mut() {
1702 if cs.pending.as_ref().is_some_and(|f| *f == frame) {
1703 cs.pending = None;
1704 }
1705 }
1706 }
1707
1708 fn session_destroyed(&mut self, session: CopySessionRef) {
1709 self.copy_sessions.retain(|cs| cs.session != session);
1710 }
1711}
1712
1713impl AppState {
1714 pub fn copy_frame_pending_for(&self, output: &Output) -> bool {
1715 self.copy_sessions
1716 .iter()
1717 .any(|cs| cs.pending.is_some() && cs.output.upgrade().as_ref() == Some(output))
1718 }
1719}
1720
1721pub fn output_capture_constraints(
1725 output: &Output,
1726 renderer: Option<&GlesRenderer>,
1727 render_node_path: &str,
1728) -> Option<BufferConstraints> {
1729 let mode = output.current_mode()?;
1730 let dma = renderer.and_then(|renderer| {
1731 let node = DrmNode::from_path(render_node_path).ok()?;
1732 let mut formats: Vec<(Fourcc, Vec<Modifier>)> = Vec::new();
1733 for f in Bind::<Dmabuf>::supported_formats(renderer)? {
1734 match formats.iter_mut().find(|(code, _)| *code == f.code) {
1735 Some((_, mods)) => mods.push(f.modifier),
1736 None => formats.push((f.code, vec![f.modifier])),
1737 }
1738 }
1739 (!formats.is_empty()).then_some(DmabufConstraints { node, formats })
1740 });
1741 Some(BufferConstraints {
1742 size: (mode.size.w, mode.size.h).into(),
1743 shm: vec![wl_shm::Format::Argb8888, wl_shm::Format::Xrgb8888],
1744 dma,
1745 })
1746}
1747
1748impl FractionalScaleHandler for AppState {
1751 fn new_fractional_scale(
1752 &mut self,
1753 surface: smithay::reexports::wayland_server::protocol::wl_surface::WlSurface,
1754 ) {
1755 if let Some(output) = self.primary_output() {
1756 let scale = output.current_scale().fractional_scale();
1757 with_states(&surface, |states| {
1758 smithay::wayland::compositor::send_surface_state(
1759 &surface, states, scale.ceil() as i32, smithay::utils::Transform::Normal,
1760 );
1761 smithay::wayland::fractional_scale::with_fractional_scale(states, |fs| {
1762 fs.set_preferred_scale(scale);
1763 });
1764 });
1765 }
1766 }
1767}
1768
1769#[derive(Debug, Clone, PartialEq)]
1773#[allow(clippy::large_enum_variant)]
1774pub enum FocusTarget {
1775 Window(Window),
1776 Popup(PopupKind),
1777 LayerSurface(DesktopLayerSurface),
1778}
1779
1780impl From<Window> for FocusTarget {
1782 fn from(w: Window) -> Self { FocusTarget::Window(w) }
1783}
1784
1785impl From<PopupKind> for FocusTarget {
1787 fn from(p: PopupKind) -> Self { FocusTarget::Popup(p) }
1788}
1789
1790impl From<DesktopLayerSurface> for FocusTarget {
1792 fn from(l: DesktopLayerSurface) -> Self { FocusTarget::LayerSurface(l) }
1793}
1794
1795impl IsAlive for FocusTarget {
1798 fn alive(&self) -> bool {
1799 match self {
1800 FocusTarget::Window(w) => w.alive(),
1801 FocusTarget::Popup(p) => p.alive(),
1802 FocusTarget::LayerSurface(l) => l.alive(),
1803 }
1804 }
1805}
1806
1807impl WaylandFocus for FocusTarget {
1810 fn wl_surface(&self) -> Option<Cow<'_, WlSurface>> {
1811 match self {
1812 FocusTarget::Window(w) => w.wl_surface(),
1813 FocusTarget::Popup(p) => Some(Cow::Borrowed(p.wl_surface())),
1814 FocusTarget::LayerSurface(l) => Some(Cow::Borrowed(l.wl_surface())),
1815 }
1816 }
1817 fn same_client_as(&self, object_id: &ObjectId) -> bool {
1818 match self {
1819 FocusTarget::Window(w) => w.same_client_as(object_id),
1820 FocusTarget::Popup(p) => p.wl_surface().id().same_client_as(object_id),
1821 FocusTarget::LayerSurface(l) => l.wl_surface().id().same_client_as(object_id),
1822 }
1823 }
1824}
1825
1826impl KeyboardTarget<AppState> for FocusTarget {
1829 fn enter(
1830 &self,
1831 seat: &Seat<AppState>,
1832 data: &mut AppState,
1833 keys: Vec<KeysymHandle<'_>>,
1834 serial: Serial,
1835 ) {
1836 if let Some(surface) = self.wl_surface() {
1837 smithay::input::keyboard::KeyboardTarget::enter(
1838 surface.as_ref(),
1839 seat,
1840 data,
1841 keys,
1842 serial,
1843 );
1844 }
1845 }
1846 fn leave(&self, seat: &Seat<AppState>, data: &mut AppState, serial: Serial) {
1847 if let Some(surface) = self.wl_surface() {
1848 smithay::input::keyboard::KeyboardTarget::leave(surface.as_ref(), seat, data, serial);
1849 }
1850 }
1851 fn key(
1852 &self,
1853 seat: &Seat<AppState>,
1854 data: &mut AppState,
1855 key: KeysymHandle<'_>,
1856 state: smithay::backend::input::KeyState,
1857 serial: Serial,
1858 time: u32,
1859 ) {
1860 if let Some(surface) = self.wl_surface() {
1861 smithay::input::keyboard::KeyboardTarget::key(
1862 surface.as_ref(),
1863 seat,
1864 data,
1865 key,
1866 state,
1867 serial,
1868 time,
1869 );
1870 }
1871 }
1872 fn modifiers(
1873 &self,
1874 seat: &Seat<AppState>,
1875 data: &mut AppState,
1876 modifiers: ModifiersState,
1877 serial: Serial,
1878 ) {
1879 if let Some(surface) = self.wl_surface() {
1880 smithay::input::keyboard::KeyboardTarget::modifiers(
1881 surface.as_ref(),
1882 seat,
1883 data,
1884 modifiers,
1885 serial,
1886 );
1887 }
1888 }
1889}
1890
1891impl DndFocus<AppState> for FocusTarget {
1894 type OfferData<S: Source> = <WlSurface as DndFocus<AppState>>::OfferData<S>;
1895
1896 fn enter<S: Source>(
1897 &self,
1898 data: &mut AppState,
1899 dh: &DisplayHandle,
1900 source: Arc<S>,
1901 seat: &Seat<AppState>,
1902 location: Point<f64, Logical>,
1903 serial: &Serial,
1904 ) -> Option<Self::OfferData<S>> {
1905 if let Some(surface) = self.wl_surface() {
1906 <WlSurface as DndFocus<AppState>>::enter(
1907 surface.as_ref(),
1908 data,
1909 dh,
1910 source,
1911 seat,
1912 location,
1913 serial,
1914 )
1915 } else {
1916 None
1917 }
1918 }
1919
1920 fn motion<S: Source>(
1921 &self,
1922 data: &mut AppState,
1923 offer: Option<&mut Self::OfferData<S>>,
1924 seat: &Seat<AppState>,
1925 location: Point<f64, Logical>,
1926 time: u32,
1927 ) {
1928 if let Some(surface) = self.wl_surface() {
1929 <WlSurface as DndFocus<AppState>>::motion(
1930 surface.as_ref(),
1931 data,
1932 offer,
1933 seat,
1934 location,
1935 time,
1936 )
1937 }
1938 }
1939
1940 fn leave<S: Source>(
1941 &self,
1942 data: &mut AppState,
1943 offer: Option<&mut Self::OfferData<S>>,
1944 seat: &Seat<AppState>,
1945 ) {
1946 if let Some(surface) = self.wl_surface() {
1947 <WlSurface as DndFocus<AppState>>::leave(surface.as_ref(), data, offer, seat)
1948 }
1949 }
1950
1951 fn drop<S: Source>(
1952 &self,
1953 data: &mut AppState,
1954 offer: Option<&mut Self::OfferData<S>>,
1955 seat: &Seat<AppState>,
1956 ) {
1957 if let Some(surface) = self.wl_surface() {
1958 <WlSurface as DndFocus<AppState>>::drop(surface.as_ref(), data, offer, seat)
1959 }
1960 }
1961}
1962
1963impl PointerTarget<AppState> for FocusTarget {
1966 fn enter(&self, seat: &Seat<AppState>, data: &mut AppState, event: &MotionEvent) {
1967 if let Some(surface) = self.wl_surface() {
1968 smithay::input::pointer::PointerTarget::enter(surface.as_ref(), seat, data, event);
1969 }
1970 }
1971 fn motion(&self, seat: &Seat<AppState>, data: &mut AppState, event: &MotionEvent) {
1972 if let Some(surface) = self.wl_surface() {
1973 smithay::input::pointer::PointerTarget::motion(surface.as_ref(), seat, data, event);
1974 }
1975 }
1976 fn relative_motion(
1977 &self,
1978 seat: &Seat<AppState>,
1979 data: &mut AppState,
1980 event: &RelativeMotionEvent,
1981 ) {
1982 if let Some(surface) = self.wl_surface() {
1983 smithay::input::pointer::PointerTarget::relative_motion(
1984 surface.as_ref(),
1985 seat,
1986 data,
1987 event,
1988 );
1989 }
1990 }
1991 fn button(&self, seat: &Seat<AppState>, data: &mut AppState, event: &ButtonEvent) {
1992 if let Some(surface) = self.wl_surface() {
1993 smithay::input::pointer::PointerTarget::button(surface.as_ref(), seat, data, event);
1994 }
1995 }
1996 fn axis(&self, seat: &Seat<AppState>, data: &mut AppState, frame: AxisFrame) {
1997 if let Some(surface) = self.wl_surface() {
1998 smithay::input::pointer::PointerTarget::axis(surface.as_ref(), seat, data, frame);
1999 }
2000 }
2001 fn frame(&self, seat: &Seat<AppState>, data: &mut AppState) {
2002 if let Some(surface) = self.wl_surface() {
2003 smithay::input::pointer::PointerTarget::frame(surface.as_ref(), seat, data);
2004 }
2005 }
2006 fn leave(&self, seat: &Seat<AppState>, data: &mut AppState, serial: Serial, time: u32) {
2007 if let Some(surface) = self.wl_surface() {
2008 smithay::input::pointer::PointerTarget::leave(
2009 surface.as_ref(),
2010 seat,
2011 data,
2012 serial,
2013 time,
2014 );
2015 }
2016 }
2017 fn gesture_swipe_begin(
2018 &self,
2019 seat: &Seat<AppState>,
2020 data: &mut AppState,
2021 event: &GestureSwipeBeginEvent,
2022 ) {
2023 if let Some(surface) = self.wl_surface() {
2024 smithay::input::pointer::PointerTarget::gesture_swipe_begin(
2025 surface.as_ref(),
2026 seat,
2027 data,
2028 event,
2029 );
2030 }
2031 }
2032 fn gesture_swipe_update(
2033 &self,
2034 seat: &Seat<AppState>,
2035 data: &mut AppState,
2036 event: &GestureSwipeUpdateEvent,
2037 ) {
2038 if let Some(surface) = self.wl_surface() {
2039 smithay::input::pointer::PointerTarget::gesture_swipe_update(
2040 surface.as_ref(),
2041 seat,
2042 data,
2043 event,
2044 );
2045 }
2046 }
2047 fn gesture_swipe_end(
2048 &self,
2049 seat: &Seat<AppState>,
2050 data: &mut AppState,
2051 event: &GestureSwipeEndEvent,
2052 ) {
2053 if let Some(surface) = self.wl_surface() {
2054 smithay::input::pointer::PointerTarget::gesture_swipe_end(
2055 surface.as_ref(),
2056 seat,
2057 data,
2058 event,
2059 );
2060 }
2061 }
2062 fn gesture_pinch_begin(
2063 &self,
2064 seat: &Seat<AppState>,
2065 data: &mut AppState,
2066 event: &GesturePinchBeginEvent,
2067 ) {
2068 if let Some(surface) = self.wl_surface() {
2069 smithay::input::pointer::PointerTarget::gesture_pinch_begin(
2070 surface.as_ref(),
2071 seat,
2072 data,
2073 event,
2074 );
2075 }
2076 }
2077 fn gesture_pinch_update(
2078 &self,
2079 seat: &Seat<AppState>,
2080 data: &mut AppState,
2081 event: &GesturePinchUpdateEvent,
2082 ) {
2083 if let Some(surface) = self.wl_surface() {
2084 smithay::input::pointer::PointerTarget::gesture_pinch_update(
2085 surface.as_ref(),
2086 seat,
2087 data,
2088 event,
2089 );
2090 }
2091 }
2092 fn gesture_pinch_end(
2093 &self,
2094 seat: &Seat<AppState>,
2095 data: &mut AppState,
2096 event: &GesturePinchEndEvent,
2097 ) {
2098 if let Some(surface) = self.wl_surface() {
2099 smithay::input::pointer::PointerTarget::gesture_pinch_end(
2100 surface.as_ref(),
2101 seat,
2102 data,
2103 event,
2104 );
2105 }
2106 }
2107 fn gesture_hold_begin(
2108 &self,
2109 seat: &Seat<AppState>,
2110 data: &mut AppState,
2111 event: &GestureHoldBeginEvent,
2112 ) {
2113 if let Some(surface) = self.wl_surface() {
2114 smithay::input::pointer::PointerTarget::gesture_hold_begin(
2115 surface.as_ref(),
2116 seat,
2117 data,
2118 event,
2119 );
2120 }
2121 }
2122 fn gesture_hold_end(
2123 &self,
2124 seat: &Seat<AppState>,
2125 data: &mut AppState,
2126 event: &GestureHoldEndEvent,
2127 ) {
2128 if let Some(surface) = self.wl_surface() {
2129 smithay::input::pointer::PointerTarget::gesture_hold_end(
2130 surface.as_ref(),
2131 seat,
2132 data,
2133 event,
2134 );
2135 }
2136 }
2137}
2138
2139impl TouchTarget<AppState> for FocusTarget {
2142 fn down(&self, seat: &Seat<AppState>, data: &mut AppState, event: &DownEvent, serial: Serial) {
2143 if let Some(surface) = self.wl_surface() {
2144 smithay::input::touch::TouchTarget::down(surface.as_ref(), seat, data, event, serial);
2145 }
2146 }
2147 fn up(&self, seat: &Seat<AppState>, data: &mut AppState, event: &UpEvent, serial: Serial) {
2148 if let Some(surface) = self.wl_surface() {
2149 smithay::input::touch::TouchTarget::up(surface.as_ref(), seat, data, event, serial);
2150 }
2151 }
2152 fn motion(
2153 &self,
2154 seat: &Seat<AppState>,
2155 data: &mut AppState,
2156 event: &smithay::input::touch::MotionEvent,
2157 serial: Serial,
2158 ) {
2159 if let Some(surface) = self.wl_surface() {
2160 smithay::input::touch::TouchTarget::motion(
2161 surface.as_ref(),
2162 seat,
2163 data,
2164 event,
2165 serial,
2166 );
2167 }
2168 }
2169 fn frame(&self, seat: &Seat<AppState>, data: &mut AppState, serial: Serial) {
2170 if let Some(surface) = self.wl_surface() {
2171 smithay::input::touch::TouchTarget::frame(surface.as_ref(), seat, data, serial);
2172 }
2173 }
2174 fn cancel(&self, seat: &Seat<AppState>, data: &mut AppState, serial: Serial) {
2175 if let Some(surface) = self.wl_surface() {
2176 smithay::input::touch::TouchTarget::cancel(surface.as_ref(), seat, data, serial);
2177 }
2178 }
2179 fn shape(
2180 &self,
2181 seat: &Seat<AppState>,
2182 data: &mut AppState,
2183 event: &ShapeEvent,
2184 serial: Serial,
2185 ) {
2186 if let Some(surface) = self.wl_surface() {
2187 smithay::input::touch::TouchTarget::shape(surface.as_ref(), seat, data, event, serial);
2188 }
2189 }
2190 fn orientation(
2191 &self,
2192 seat: &Seat<AppState>,
2193 data: &mut AppState,
2194 event: &OrientationEvent,
2195 serial: Serial,
2196 ) {
2197 if let Some(surface) = self.wl_surface() {
2198 smithay::input::touch::TouchTarget::orientation(
2199 surface.as_ref(),
2200 seat,
2201 data,
2202 event,
2203 serial,
2204 );
2205 }
2206 }
2207}
2208
2209pub fn cursor_icon_to_str(icon: &CursorIcon) -> &'static str {
2213 match icon {
2214 CursorIcon::Default => "default",
2215 CursorIcon::ContextMenu => "context-menu",
2216 CursorIcon::Help => "help",
2217 CursorIcon::Pointer => "pointer",
2218 CursorIcon::Progress => "progress",
2219 CursorIcon::Wait => "wait",
2220 CursorIcon::Cell => "cell",
2221 CursorIcon::Crosshair => "crosshair",
2222 CursorIcon::Text => "text",
2223 CursorIcon::VerticalText => "vertical-text",
2224 CursorIcon::Alias => "alias",
2225 CursorIcon::Copy => "copy",
2226 CursorIcon::Move => "move",
2227 CursorIcon::NoDrop => "no-drop",
2228 CursorIcon::NotAllowed => "not-allowed",
2229 CursorIcon::Grab => "grab",
2230 CursorIcon::Grabbing => "grabbing",
2231 CursorIcon::AllScroll => "all-scroll",
2232 CursorIcon::ColResize => "col-resize",
2233 CursorIcon::RowResize => "row-resize",
2234 CursorIcon::NResize => "n-resize",
2235 CursorIcon::EResize => "e-resize",
2236 CursorIcon::SResize => "s-resize",
2237 CursorIcon::WResize => "w-resize",
2238 CursorIcon::NeResize => "ne-resize",
2239 CursorIcon::NwResize => "nw-resize",
2240 CursorIcon::SeResize => "se-resize",
2241 CursorIcon::SwResize => "sw-resize",
2242 CursorIcon::EwResize => "ew-resize",
2243 CursorIcon::NsResize => "ns-resize",
2244 CursorIcon::NeswResize => "nesw-resize",
2245 CursorIcon::NwseResize => "nwse-resize",
2246 CursorIcon::ZoomIn => "zoom-in",
2247 CursorIcon::ZoomOut => "zoom-out",
2248 _ => "default",
2249 }
2250}
2251
2252impl SeatHandler for AppState {
2255 type KeyboardFocus = FocusTarget;
2256 type PointerFocus = FocusTarget;
2257 type TouchFocus = FocusTarget;
2258 fn seat_state(&mut self) -> &mut SeatState<AppState> {
2259 &mut self.seat_state
2260 }
2261
2262 fn cursor_image(&mut self, _seat: &Seat<AppState>, image: CursorImageStatus) {
2266 self.current_cursor_icon = Some(image.clone());
2267 self.cursor_surface_pending = matches!(image, CursorImageStatus::Surface(_));
2268 if !self.cursor_surface_pending {
2269 self.send_cursor_image(&image);
2270 }
2271 }
2272
2273 fn focus_changed(&mut self, seat: &Seat<AppState>, focus: Option<&Self::KeyboardFocus>) {
2278 let dh = &self.dh;
2279 let client = focus
2280 .and_then(|t| t.wl_surface())
2281 .and_then(|s| dh.get_client(s.id()).ok());
2282 set_data_device_focus(dh, seat, client.clone());
2283 set_primary_focus(dh, seat, client);
2284 }
2285}
2286
2287impl PointerWarpHandler for AppState {
2294 fn warp_pointer(
2295 &mut self,
2296 surface: WlSurface,
2297 _pointer: WlPointer,
2298 pos: Point<f64, Logical>,
2299 serial: Serial,
2300 ) {
2301 let surface_origin = self.space.elements().find_map(|window| {
2302 if window.wl_surface().as_deref() == Some(&surface) {
2303 self.space.element_location(window)
2304 } else {
2305 None
2306 }
2307 });
2308
2309 if let Some(origin) = surface_origin {
2310 let global_pos = origin.to_f64() + pos;
2311 let time = wayland_time();
2312
2313 if let Some(pointer) = self.seat.get_pointer() {
2314 let under = self.space.element_under(global_pos).map(|(w, loc)| {
2315 (FocusTarget::Window(w.clone()), loc.to_f64())
2316 });
2317
2318 pointer.motion(
2319 self,
2320 under,
2321 &MotionEvent {
2322 location: global_pos,
2323 serial,
2324 time,
2325 },
2326 );
2327 }
2328 }
2329 }
2330}
2331
2332impl XdgShellHandler for AppState {
2334 fn xdg_shell_state(&mut self) -> &mut XdgShellState {
2335 &mut self.shell_state
2336 }
2337 fn new_toplevel(&mut self, surface: ToplevelSurface) {
2343 let target_id = self.pointer_display();
2344 let window = Window::new_wayland_window(surface.clone());
2345 window.user_data().insert_if_missing_threadsafe(|| WindowMeta {
2346 id: NEXT_WINDOW_ID.fetch_add(1, Ordering::Relaxed),
2347 output: AtomicU32::new(target_id),
2348 placed: AtomicBool::new(false),
2349 parked: AtomicBool::new(false),
2350 });
2351 self.pending_windows.push(window);
2352 let (title, app_id) = with_states(surface.wl_surface(), |states| {
2353 let attributes = states.data_map.get::<XdgToplevelSurfaceData>().unwrap().lock().unwrap();
2354 (attributes.title.clone(), attributes.app_id.clone())
2355 });
2356
2357 let handle = self.foreign_toplevel_list.new_toplevel::<AppState>(title.unwrap_or_default(), app_id.unwrap_or_default());
2358
2359 with_states(surface.wl_surface(), |states| states.data_map.insert_if_missing(|| handle));
2360 }
2361 fn new_popup(&mut self, surface: PopupSurface, _positioner: PositionerState) {
2365 if let Err(err) = self.popups.track_popup(PopupKind::Xdg(surface.clone())) {
2366 eprintln!("Failed to track popup: {:?}", err);
2367 }
2368 let _ = surface.send_configure();
2369 }
2370 fn grab(
2373 &mut self,
2374 surface: PopupSurface,
2375 _seat: smithay::reexports::wayland_server::protocol::wl_seat::WlSeat,
2376 serial: Serial,
2377 ) {
2378 let kind = PopupKind::Xdg(surface);
2379 if let Ok(root_surface) = smithay::desktop::find_popup_root_surface(&kind)
2380 && let Some(window) = self.space.elements().find(|w| w.wl_surface().as_deref() == Some(&root_surface)).cloned() {
2381 let _ = self.popups.grab_popup(FocusTarget::Window(window), kind, &self.seat, serial);
2382 }
2383 }
2384 fn reposition_request(
2388 &mut self,
2389 surface: PopupSurface,
2390 _positioner: PositionerState,
2391 token: u32,
2392 ) {
2393 if let Err(err) = self.popups.track_popup(PopupKind::Xdg(surface.clone())) {
2394 eprintln!("Failed to track popup: {:?}", err);
2395 }
2396 let _ = surface.send_repositioned(token);
2397 }
2398 fn fullscreen_request(
2403 &mut self,
2404 surface: ToplevelSurface,
2405 output: Option<smithay::reexports::wayland_server::protocol::wl_output::WlOutput>,
2406 ) {
2407 if let Some(id) = output
2408 .as_ref()
2409 .and_then(|wlo| self.output_nodes.iter().find(|n| n.output.owns(wlo)).map(|n| n.id))
2410 {
2411 let mapped = self
2412 .space
2413 .elements()
2414 .find(|w| w.toplevel().map(|tl| *tl == surface).unwrap_or(false))
2415 .cloned();
2416 if let Some(window) = mapped {
2417 if self.place_window_on_output(&window, id) {
2418 return;
2419 }
2420 } else if let Some(meta) = self
2421 .pending_windows
2422 .iter()
2423 .find(|w| w.toplevel().map(|tl| *tl == surface).unwrap_or(false))
2424 .and_then(window_meta)
2425 {
2426 meta.output.store(id, Ordering::Relaxed);
2430 meta.placed.store(true, Ordering::Relaxed);
2431 }
2432 }
2433 self.send_forced_fullscreen_configure(&surface);
2434 }
2435 fn unfullscreen_request(&mut self, surface: ToplevelSurface) {
2439 self.send_forced_fullscreen_configure(&surface);
2440 }
2441 fn maximize_request(&mut self, surface: ToplevelSurface) {
2443 self.send_forced_fullscreen_configure(&surface);
2444 }
2445 fn unmaximize_request(&mut self, surface: ToplevelSurface) {
2447 self.send_forced_fullscreen_configure(&surface);
2448 }
2449 fn toplevel_destroyed(&mut self, surface: ToplevelSurface) {
2455 if let Some(idx) = self.pending_windows.iter().position(|w| w.toplevel().map(|t| *t == surface).unwrap_or(false)) {
2456 self.pending_windows.remove(idx);
2457 }
2458 let mapped = self
2459 .space
2460 .elements()
2461 .find(|w| w.toplevel().map(|t| *t == surface).unwrap_or(false))
2462 .cloned();
2463 if let Some(window) = mapped {
2464 self.space.unmap_elem(&window);
2465 }
2466 if let Some(handle) = with_states(surface.wl_surface(), |states| states.data_map.get::<ForeignToplevelHandle>().cloned()) {
2467 self.foreign_toplevel_list.remove_toplevel(&handle);
2468 }
2469 }
2470}
2471
2472pub struct PfVirtualKeyboard {
2483 inner: Mutex<PfVkState>,
2484}
2485
2486#[derive(Default)]
2487struct PfVkState {
2488 syms: Option<std::collections::HashMap<u32, u32>>,
2490 pressed: std::collections::HashMap<u32, u32>,
2493}
2494
2495impl GlobalDispatch<ZwpVirtualKeyboardManagerV1, ()> for AppState {
2496 fn bind(
2497 _state: &mut Self,
2498 _dh: &DisplayHandle,
2499 _client: &Client,
2500 resource: New<ZwpVirtualKeyboardManagerV1>,
2501 _global_data: &(),
2502 data_init: &mut DataInit<'_, Self>,
2503 ) {
2504 data_init.init(resource, ());
2505 }
2506}
2507
2508impl Dispatch<ZwpVirtualKeyboardManagerV1, ()> for AppState {
2509 fn request(
2510 _state: &mut Self,
2511 _client: &Client,
2512 _resource: &ZwpVirtualKeyboardManagerV1,
2513 request: zwp_virtual_keyboard_manager_v1::Request,
2514 _data: &(),
2515 _dh: &DisplayHandle,
2516 data_init: &mut DataInit<'_, Self>,
2517 ) {
2518 if let zwp_virtual_keyboard_manager_v1::Request::CreateVirtualKeyboard { seat: _, id } =
2519 request
2520 {
2521 data_init.init(id, PfVirtualKeyboard { inner: Mutex::new(PfVkState::default()) });
2522 }
2523 }
2524}
2525
2526impl Dispatch<ZwpVirtualKeyboardV1, PfVirtualKeyboard> for AppState {
2527 fn request(
2528 state: &mut Self,
2529 _client: &Client,
2530 resource: &ZwpVirtualKeyboardV1,
2531 request: zwp_virtual_keyboard_v1::Request,
2532 data: &PfVirtualKeyboard,
2533 _dh: &DisplayHandle,
2534 _data_init: &mut DataInit<'_, Self>,
2535 ) {
2536 use smithay::input::keyboard::xkb;
2537 match request {
2538 zwp_virtual_keyboard_v1::Request::Keymap { format, fd, size } => {
2539 if format != 1 {
2540 return;
2541 }
2542 let ctx = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
2543 let keymap = unsafe {
2544 xkb::Keymap::new_from_fd(
2545 &ctx,
2546 fd,
2547 size as usize,
2548 xkb::KEYMAP_FORMAT_TEXT_V1,
2549 xkb::KEYMAP_COMPILE_NO_FLAGS,
2550 )
2551 };
2552 let Ok(Some(keymap)) = keymap else {
2553 eprintln!("[Wayland] virtual-keyboard keymap failed to compile; ignoring.");
2554 return;
2555 };
2556 let syms = crate::wayland::keymap::level0_syms(&keymap);
2557 let missing: Vec<u32> = syms
2560 .values()
2561 .copied()
2562 .filter(|&s| !state.keymap_policy.resolves_plain(s))
2563 .collect();
2564 if !missing.is_empty() {
2565 let _ = state.bind_keysyms_plain(&missing);
2566 }
2567 data.inner.lock().unwrap().syms = Some(syms);
2568 }
2569 zwp_virtual_keyboard_v1::Request::Key { time: _, key, state: key_state } => {
2570 let mut vk = data.inner.lock().unwrap();
2571 if vk.syms.is_none() {
2572 drop(vk);
2573 resource.post_error(
2574 zwp_virtual_keyboard_v1::Error::NoKeymap,
2575 "`key` sent before keymap.",
2576 );
2577 return;
2578 }
2579 let vk_kc = key.wrapping_add(8);
2580 let seat_kc = if key_state == 1 {
2581 let sym = vk.syms.as_ref().and_then(|m| m.get(&vk_kc)).copied();
2582 let kc = match sym {
2586 Some(sym) => {
2587 let bound = state.bind_keysyms_plain(&[sym])[0];
2588 if bound != 0 { bound } else { vk_kc }
2589 }
2590 None => vk_kc,
2591 };
2592 vk.pressed.insert(vk_kc, kc);
2593 kc
2594 } else {
2595 vk.pressed.remove(&vk_kc).unwrap_or(vk_kc)
2596 };
2597 drop(vk);
2598 let pressed = key_state == 1;
2599 if let Some(keyboard) = state.seat.get_keyboard() {
2600 let keyboard = keyboard.clone();
2601 let serial = next_serial();
2602 let time = wayland_time();
2603 keyboard.input(
2604 state,
2605 smithay::backend::input::Keycode::new(seat_kc),
2606 if pressed {
2607 smithay::backend::input::KeyState::Pressed
2608 } else {
2609 smithay::backend::input::KeyState::Released
2610 },
2611 serial,
2612 time,
2613 |_, _, _| smithay::input::keyboard::FilterResult::<()>::Forward,
2614 );
2615 }
2616 }
2617 zwp_virtual_keyboard_v1::Request::Modifiers { .. } => {}
2618 zwp_virtual_keyboard_v1::Request::Destroy => {}
2619 _ => {}
2620 }
2621 }
2622
2623 fn destroyed(
2626 state: &mut Self,
2627 _client: ClientId,
2628 _resource: &ZwpVirtualKeyboardV1,
2629 data: &PfVirtualKeyboard,
2630 ) {
2631 let held: Vec<u32> = data.inner.lock().unwrap().pressed.drain().map(|(_, kc)| kc).collect();
2632 if held.is_empty() {
2633 return;
2634 }
2635 if let Some(keyboard) = state.seat.get_keyboard() {
2636 let keyboard = keyboard.clone();
2637 for kc in held {
2638 let serial = next_serial();
2639 let time = wayland_time();
2640 keyboard.input(
2641 state,
2642 smithay::backend::input::Keycode::new(kc),
2643 smithay::backend::input::KeyState::Released,
2644 serial,
2645 time,
2646 |_, _, _| smithay::input::keyboard::FilterResult::<()>::Forward,
2647 );
2648 }
2649 }
2650 }
2651}
2652
2653#[derive(Default)]
2656pub struct ClientState {
2657 pub compositor_state: CompositorClientState,
2658}
2659impl ClientData for ClientState {
2661 fn initialized(&self, _client_id: ClientId) {}
2662 fn disconnected(&self, _client_id: ClientId, _reason: DisconnectReason) {}
2663}
2664
2665delegate_compositor!(AppState);
2666delegate_shm!(AppState);
2667delegate_output!(AppState);
2668delegate_seat!(AppState);
2669delegate_xdg_shell!(AppState);
2670delegate_dmabuf!(AppState);
2671delegate_image_capture_source!(AppState);
2672delegate_output_capture_source!(AppState);
2673delegate_image_copy_capture!(AppState);
2674delegate_ext_data_control!(AppState);
2675delegate_cursor_shape!(AppState);
2676delegate_fractional_scale!(AppState);
2677delegate_data_device!(AppState);
2678delegate_data_control!(AppState);
2679delegate_pointer_warp!(AppState);
2680delegate_relative_pointer!(AppState);
2681delegate_pointer_constraints!(AppState);
2682delegate_foreign_toplevel_list!(AppState);
2683delegate_xdg_decoration!(AppState);
2684delegate_layer_shell!(AppState);
2685delegate_single_pixel_buffer!(AppState);
2686delegate_viewporter!(AppState);
2687delegate_presentation!(AppState);
2688delegate_xdg_activation!(AppState);
2689delegate_primary_selection!(AppState);
2690
2691pub(crate) fn rgba_readback_stride(buf_len: usize, height: usize, width: usize) -> usize {
2698 let row = width.saturating_mul(4);
2699 if height == 0 {
2700 return row;
2701 }
2702 (buf_len / height).max(row)
2703}
2704
2705#[cfg(test)]
2706mod stride_tests {
2707 use super::rgba_readback_stride;
2708
2709 #[test]
2711 fn packed_readback_is_width_times_four() {
2712 assert_eq!(rgba_readback_stride(64 * 4 * 48, 48, 64), 64 * 4);
2713 assert_eq!(rgba_readback_stride(3 * 4 * 2, 2, 3), 12);
2714 }
2715
2716 #[test]
2719 fn padded_readback_recovers_real_stride() {
2720 let (w, h, pad) = (3usize, 2usize, 4usize);
2721 let stride = w * 4 + pad;
2722 assert_eq!(rgba_readback_stride(stride * h, h, w), stride);
2723 }
2724
2725 #[test]
2728 fn padded_extraction_has_no_skew() {
2729 let (w, h) = (3usize, 2usize);
2730 let stride = 16usize;
2731 let mut buf = vec![0u8; stride * h];
2732 for y in 0..h {
2733 for x in 0..w {
2734 let o = y * stride + x * 4;
2735 buf[o] = x as u8 + 1;
2736 buf[o + 1] = y as u8 + 1;
2737 }
2738 }
2739 let s = rgba_readback_stride(buf.len(), h, w);
2740 assert_eq!(s, stride);
2741 for y in 0..h {
2742 for x in 0..w {
2743 let o = y * s + x * 4;
2744 assert_eq!(buf[o], x as u8 + 1);
2745 assert_eq!(buf[o + 1], y as u8 + 1);
2746 }
2747 }
2748 }
2749
2750 #[test]
2752 fn zero_height_no_divide_by_zero() {
2753 assert_eq!(rgba_readback_stride(0, 0, 10), 40);
2754 }
2755
2756 #[test]
2758 fn truncated_buffer_keeps_full_row() {
2759 assert_eq!(rgba_readback_stride(10, 5, 64), 64 * 4);
2760 }
2761}