1use crate::input_region::{self, RegionOp};
9use crate::pointer_focus::{
10 ButtonRouting, button_routing, focus_transition, keyboard_focus_after_popup_close,
11};
12use crate::positioner::PositionerGeometry;
13use std::collections::HashMap;
14use std::os::fd::{AsFd, AsRawFd, FromRawFd, OwnedFd};
15use std::sync::Arc;
16use std::sync::atomic::{AtomicBool, Ordering};
17use std::sync::mpsc;
18
19use calloop::generic::Generic;
20use calloop::{EventLoop, Interest, LoopSignal, PostAction};
21use wayland_protocols::wp::cursor_shape::v1::server::wp_cursor_shape_device_v1::{
22 self, WpCursorShapeDeviceV1,
23};
24use wayland_protocols::wp::cursor_shape::v1::server::wp_cursor_shape_manager_v1::{
25 self, WpCursorShapeManagerV1,
26};
27use wayland_protocols::wp::fractional_scale::v1::server::wp_fractional_scale_manager_v1::{
28 self, WpFractionalScaleManagerV1,
29};
30use wayland_protocols::wp::fractional_scale::v1::server::wp_fractional_scale_v1::WpFractionalScaleV1;
31use wayland_protocols::wp::presentation_time::server::wp_presentation::{
32 self, WpPresentation,
33};
34use wayland_protocols::wp::presentation_time::server::wp_presentation_feedback::{
35 Kind as WpPresentationFeedbackKind, WpPresentationFeedback,
36};
37use wayland_protocols::wp::linux_dmabuf::zv1::server::zwp_linux_buffer_params_v1::{
38 self, ZwpLinuxBufferParamsV1,
39};
40use wayland_protocols::wp::linux_dmabuf::zv1::server::zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1;
41use wayland_protocols::wp::linux_dmabuf::zv1::server::zwp_linux_dmabuf_v1::{
42 self, ZwpLinuxDmabufV1,
43};
44use wayland_protocols::wp::pointer_constraints::zv1::server::zwp_confined_pointer_v1::ZwpConfinedPointerV1;
45use wayland_protocols::wp::pointer_constraints::zv1::server::zwp_locked_pointer_v1::ZwpLockedPointerV1;
46use wayland_protocols::wp::pointer_constraints::zv1::server::zwp_pointer_constraints_v1::{
47 self, ZwpPointerConstraintsV1,
48};
49use wayland_protocols::wp::primary_selection::zv1::server::zwp_primary_selection_device_manager_v1::{
50 self, ZwpPrimarySelectionDeviceManagerV1,
51};
52use wayland_protocols::wp::primary_selection::zv1::server::zwp_primary_selection_device_v1::{
53 self, ZwpPrimarySelectionDeviceV1,
54};
55use wayland_protocols::wp::primary_selection::zv1::server::zwp_primary_selection_offer_v1::{
56 self, ZwpPrimarySelectionOfferV1,
57};
58use wayland_protocols::wp::primary_selection::zv1::server::zwp_primary_selection_source_v1::{
59 self, ZwpPrimarySelectionSourceV1,
60};
61use wayland_protocols::wp::relative_pointer::zv1::server::zwp_relative_pointer_manager_v1::{
62 self, ZwpRelativePointerManagerV1,
63};
64use wayland_protocols::wp::relative_pointer::zv1::server::zwp_relative_pointer_v1::ZwpRelativePointerV1;
65use wayland_protocols::wp::text_input::zv3::server::zwp_text_input_manager_v3::{
66 self, ZwpTextInputManagerV3,
67};
68use wayland_protocols::wp::text_input::zv3::server::zwp_text_input_v3::{
69 self, ZwpTextInputV3,
70};
71use wayland_protocols::wp::viewporter::server::wp_viewport::WpViewport;
72use wayland_protocols::wp::viewporter::server::wp_viewporter::{self, WpViewporter};
73use wayland_protocols::xdg::activation::v1::server::xdg_activation_token_v1::{
74 self, XdgActivationTokenV1,
75};
76use wayland_protocols::xdg::activation::v1::server::xdg_activation_v1::{
77 self, XdgActivationV1,
78};
79use wayland_protocols::xdg::decoration::zv1::server::zxdg_decoration_manager_v1::{
80 self, ZxdgDecorationManagerV1,
81};
82use wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1::{
83 self, ZxdgToplevelDecorationV1,
84};
85use wayland_protocols::xdg::shell::server::xdg_popup::{self, XdgPopup};
86use wayland_protocols::xdg::shell::server::xdg_positioner::XdgPositioner;
87use wayland_protocols::xdg::shell::server::xdg_surface::{self, XdgSurface};
88use wayland_protocols::xdg::shell::server::xdg_toplevel::{self, XdgToplevel};
89use wayland_protocols::xdg::shell::server::xdg_wm_base::{self, XdgWmBase};
90use wayland_server::protocol::wl_buffer::WlBuffer;
91use wayland_server::protocol::wl_callback::WlCallback;
92use wayland_server::protocol::wl_compositor::WlCompositor;
93use wayland_server::protocol::wl_data_device::{self, WlDataDevice};
94use wayland_server::protocol::wl_data_device_manager::{self, WlDataDeviceManager};
95use wayland_server::protocol::wl_data_offer::{self, WlDataOffer};
96use wayland_server::protocol::wl_data_source::{self, WlDataSource};
97use wayland_server::protocol::wl_keyboard::{self, WlKeyboard};
98use wayland_server::protocol::wl_output::{self, WlOutput};
99use wayland_server::protocol::wl_pointer::{self, WlPointer};
100use wayland_server::protocol::wl_region::WlRegion;
101use wayland_server::protocol::wl_seat::{self, WlSeat};
102use wayland_server::protocol::wl_shm::{self, WlShm};
103use wayland_server::protocol::wl_shm_pool::WlShmPool;
104use wayland_server::protocol::wl_subcompositor::WlSubcompositor;
105use wayland_server::protocol::wl_subsurface::WlSubsurface;
106use wayland_server::protocol::wl_surface::WlSurface;
107use wayland_server::backend::ObjectId;
108use wayland_server::{
109 Client, DataInit, Dispatch, Display, DisplayHandle, GlobalDispatch, New, Resource,
110};
111
112#[derive(Clone)]
118pub enum PixelData {
119 Bgra(Arc<Vec<u8>>),
120 Rgba(Arc<Vec<u8>>),
121 Nv12 {
122 data: Arc<Vec<u8>>,
123 y_stride: usize,
124 uv_stride: usize,
125 },
126 DmaBuf {
127 fd: Arc<OwnedFd>,
128 fourcc: u32,
129 modifier: u64,
130 stride: u32,
131 offset: u32,
132 y_invert: bool,
136 },
137 Nv12DmaBuf {
140 fd: Arc<OwnedFd>,
141 stride: u32,
142 uv_offset: u32,
143 width: u32,
144 height: u32,
145 sync_fd: Option<Arc<OwnedFd>>,
150 },
151 VaSurface {
153 surface_id: u32,
154 va_display: usize,
155 _fd: Arc<OwnedFd>,
156 },
157}
158
159pub struct EncodedFrame {
166 pub surface_id: u16,
167 pub client_id: u64,
168 pub width: u32,
169 pub height: u32,
170 pub data: Arc<Vec<u8>>,
171 pub is_keyframe: bool,
172 pub codec_flag: u8,
174}
175
176#[derive(Clone, Copy, Default)]
181pub struct ExternalOutputPlane {
182 pub offset: u32,
183 pub pitch: u32,
184}
185
186pub struct ExternalOutputBuffer {
187 pub fd: Arc<OwnedFd>,
188 pub fourcc: u32,
189 pub modifier: u64,
190 pub stride: u32,
191 pub offset: u32,
192 pub width: u32,
193 pub height: u32,
194 pub va_surface_id: u32,
195 pub va_display: usize,
196 pub planes: Vec<ExternalOutputPlane>,
198 pub nv12_fd: Option<Arc<OwnedFd>>,
202 pub nv12_stride: u32,
203 pub nv12_uv_offset: u32,
204 pub nv12_modifier: u64,
206 pub nv12_width: u32,
209 pub nv12_height: u32,
210}
211
212pub mod drm_fourcc {
213 pub const ARGB8888: u32 = u32::from_le_bytes(*b"AR24");
214 pub const XRGB8888: u32 = u32::from_le_bytes(*b"XR24");
215 pub const ABGR8888: u32 = u32::from_le_bytes(*b"AB24");
216 pub const XBGR8888: u32 = u32::from_le_bytes(*b"XB24");
217 pub const NV12: u32 = u32::from_le_bytes(*b"NV12");
218}
219
220impl PixelData {
221 pub fn to_rgba(&self, width: u32, height: u32) -> Vec<u8> {
222 let w = width as usize;
223 let h = height as usize;
224 match self {
225 PixelData::Rgba(data) => data.as_ref().clone(),
226 PixelData::Bgra(data) => {
227 let mut rgba = Vec::with_capacity(w * h * 4);
228 for px in data.chunks_exact(4) {
229 rgba.extend_from_slice(&[px[2], px[1], px[0], px[3]]);
230 }
231 rgba
232 }
233 PixelData::Nv12 {
234 data,
235 y_stride,
236 uv_stride,
237 } => {
238 let y_plane_size = *y_stride * h;
239 let uv_h = h.div_ceil(2);
240 let uv_plane_size = *uv_stride * uv_h;
241 if data.len() < y_plane_size + uv_plane_size {
242 return Vec::new();
243 }
244 let y_plane = &data[..y_plane_size];
245 let uv_plane = &data[y_plane_size..];
246 let mut rgba = Vec::with_capacity(w * h * 4);
247 for row in 0..h {
248 for col in 0..w {
249 let y = y_plane[row * y_stride + col];
250 let uv_idx = (row / 2) * uv_stride + (col / 2) * 2;
251 if uv_idx + 1 >= uv_plane.len() {
252 rgba.extend_from_slice(&[0, 0, 0, 255]);
253 continue;
254 }
255 let u = uv_plane[uv_idx];
256 let v = uv_plane[uv_idx + 1];
257 let [r, g, b] = yuv420_to_rgb(y, u, v);
258 rgba.extend_from_slice(&[r, g, b, 255]);
259 }
260 }
261 rgba
262 }
263 PixelData::DmaBuf {
264 fd,
265 fourcc,
266 stride,
267 offset,
268 ..
269 } => {
270 let raw = fd.as_raw_fd();
271 let stride_usize = *stride as usize;
272 let plane_offset = *offset as usize;
273 let map_size = plane_offset + stride_usize * h;
274 if map_size == 0 {
275 return Vec::new();
276 }
277 const DMA_BUF_SYNC_READ: u64 = 1;
284 const DMA_BUF_SYNC_START: u64 = 0;
285 const DMA_BUF_SYNC_END: u64 = 4;
286 const DMA_BUF_IOCTL_SYNC: libc::c_ulong = 0x40086200;
287 let did_sync = {
288 let mut pfd = libc::pollfd {
289 fd: raw,
290 events: libc::POLLIN,
291 revents: 0,
292 };
293 let ready = unsafe { libc::poll(&mut pfd, 1, 0) };
294 if ready > 0 {
295 let s: u64 = DMA_BUF_SYNC_START | DMA_BUF_SYNC_READ;
296 unsafe { libc::ioctl(raw, DMA_BUF_IOCTL_SYNC as _, &s) };
297 true
298 } else {
299 false
300 }
301 };
302 let ptr = unsafe {
303 libc::mmap(
304 std::ptr::null_mut(),
305 map_size,
306 libc::PROT_READ,
307 libc::MAP_SHARED,
308 raw,
309 0,
310 )
311 };
312 if ptr == libc::MAP_FAILED {
313 if did_sync {
314 let s: u64 = DMA_BUF_SYNC_END | DMA_BUF_SYNC_READ;
315 unsafe { libc::ioctl(raw, DMA_BUF_IOCTL_SYNC as _, &s) };
316 }
317 return Vec::new();
318 }
319 let slice = unsafe { std::slice::from_raw_parts(ptr as *const u8, map_size) };
320 let row_bytes = w * 4;
321 let mut pixels = Vec::with_capacity(w * h * 4);
322 for row in 0..h {
323 let start = plane_offset + row * stride_usize;
324 if start + row_bytes <= slice.len() {
325 pixels.extend_from_slice(&slice[start..start + row_bytes]);
326 }
327 }
328 let is_bgr_mem = matches!(*fourcc, drm_fourcc::ARGB8888 | drm_fourcc::XRGB8888);
329 let force_alpha = matches!(*fourcc, drm_fourcc::XRGB8888 | drm_fourcc::XBGR8888);
330 for px in pixels.chunks_exact_mut(4) {
331 if is_bgr_mem {
332 px.swap(0, 2);
333 }
334 if force_alpha {
335 px[3] = 255;
336 }
337 }
338 unsafe { libc::munmap(ptr, map_size) };
339 if did_sync {
340 let s: u64 = DMA_BUF_SYNC_END | DMA_BUF_SYNC_READ;
341 unsafe { libc::ioctl(raw, DMA_BUF_IOCTL_SYNC as _, &s) };
342 }
343 pixels
344 }
345 PixelData::Nv12DmaBuf {
346 fd,
347 stride,
348 uv_offset,
349 width: nv12_w,
350 height: nv12_h,
351 sync_fd,
352 } => {
353 if let Some(sync) = sync_fd {
359 let mut pfd = libc::pollfd {
360 fd: sync.as_raw_fd(),
361 events: libc::POLLIN,
362 revents: 0,
363 };
364 unsafe {
368 libc::poll(&mut pfd, 1, 10);
369 }
370 }
371 let nw = *nv12_w as usize;
372 let nh = *nv12_h as usize;
373 let stride_usize = *stride as usize;
374 let uv_off = *uv_offset as usize;
375 let y_plane_size = stride_usize * nh;
376 let uv_h = nh.div_ceil(2);
377 let uv_plane_size = stride_usize * uv_h;
378 let map_size = uv_off + uv_plane_size;
379 if map_size == 0 || nw == 0 || nh == 0 {
380 return Vec::new();
381 }
382 let raw = fd.as_raw_fd();
383 const DMA_BUF_SYNC_READ: u64 = 1;
384 const DMA_BUF_SYNC_START: u64 = 0;
385 const DMA_BUF_SYNC_END: u64 = 4;
386 const DMA_BUF_IOCTL_SYNC: libc::c_ulong = 0x40086200;
387 let s_start: u64 = DMA_BUF_SYNC_START | DMA_BUF_SYNC_READ;
388 let did_sync = unsafe { libc::ioctl(raw, DMA_BUF_IOCTL_SYNC as _, &s_start) == 0 };
389 let ptr = unsafe {
390 libc::mmap(
391 std::ptr::null_mut(),
392 map_size,
393 libc::PROT_READ,
394 libc::MAP_SHARED,
395 raw,
396 0,
397 )
398 };
399 if ptr == libc::MAP_FAILED {
400 if did_sync {
401 let s_end: u64 = DMA_BUF_SYNC_END | DMA_BUF_SYNC_READ;
402 unsafe { libc::ioctl(raw, DMA_BUF_IOCTL_SYNC as _, &s_end) };
403 }
404 return Vec::new();
405 }
406 let slice = unsafe { std::slice::from_raw_parts(ptr as *const u8, map_size) };
407 let y_plane = &slice[..y_plane_size.min(slice.len())];
408 let uv_plane = &slice[uv_off.min(slice.len())..];
409 let out_w = w.min(nw);
412 let out_h = h.min(nh);
413 let mut rgba = Vec::with_capacity(w * h * 4);
414 for row in 0..out_h {
415 for col in 0..out_w {
416 let y_idx = row * stride_usize + col;
417 let uv_idx = (row / 2) * stride_usize + (col / 2) * 2;
418 if y_idx >= y_plane.len() || uv_idx + 1 >= uv_plane.len() {
419 rgba.extend_from_slice(&[0, 0, 0, 255]);
420 continue;
421 }
422 let y = y_plane[y_idx];
423 let u = uv_plane[uv_idx];
424 let v = uv_plane[uv_idx + 1];
425 let [r, g, b] = yuv420_to_rgb(y, u, v);
426 rgba.extend_from_slice(&[r, g, b, 255]);
427 }
428 for _ in out_w..w {
430 rgba.extend_from_slice(&[0, 0, 0, 255]);
431 }
432 }
433 for _ in out_h..h {
434 for _ in 0..w {
435 rgba.extend_from_slice(&[0, 0, 0, 255]);
436 }
437 }
438 unsafe { libc::munmap(ptr, map_size) };
439 if did_sync {
440 let s_end: u64 = DMA_BUF_SYNC_END | DMA_BUF_SYNC_READ;
441 unsafe { libc::ioctl(raw, DMA_BUF_IOCTL_SYNC as _, &s_end) };
442 }
443 rgba
444 }
445 PixelData::VaSurface { .. } => Vec::new(),
446 }
447 }
448
449 pub fn is_empty(&self) -> bool {
450 match self {
451 PixelData::Bgra(v) | PixelData::Rgba(v) => v.is_empty(),
452 PixelData::Nv12 { data, .. } => data.is_empty(),
453 PixelData::DmaBuf { .. }
454 | PixelData::VaSurface { .. }
455 | PixelData::Nv12DmaBuf { .. } => false,
456 }
457 }
458
459 pub fn is_dmabuf(&self) -> bool {
460 matches!(self, PixelData::DmaBuf { .. })
461 }
462
463 pub fn is_va_surface(&self) -> bool {
464 matches!(self, PixelData::VaSurface { .. })
465 }
466}
467
468#[derive(Clone)]
469pub enum CursorImage {
470 Named(String),
471 Custom {
472 hotspot_x: u16,
473 hotspot_y: u16,
474 width: u16,
475 height: u16,
476 rgba: Vec<u8>,
477 },
478 Hidden,
479}
480
481pub enum CompositorEvent {
482 SurfaceCreated {
483 surface_id: u16,
484 title: String,
485 app_id: String,
486 parent_id: u16,
487 width: u16,
488 height: u16,
489 },
490 SurfaceDestroyed {
491 surface_id: u16,
492 },
493 SurfaceCommit {
494 surface_id: u16,
495 width: u32,
496 height: u32,
497 pixels: PixelData,
498 timestamp_ms: u32,
502 },
503 SurfaceEncoded {
506 frame: EncodedFrame,
507 timestamp_ms: u32,
508 },
509 VulkanEncoderUnavailable {
512 surface_id: u16,
513 client_id: u64,
514 },
515 SurfaceTitle {
516 surface_id: u16,
517 title: String,
518 },
519 SurfaceAppId {
520 surface_id: u16,
521 app_id: String,
522 },
523 SurfaceResized {
524 surface_id: u16,
525 width: u16,
526 height: u16,
527 },
528 ClipboardContent {
529 mime_type: String,
530 data: Vec<u8>,
531 },
532 SurfaceCursor {
533 surface_id: u16,
534 cursor: CursorImage,
535 },
536}
537
538pub enum CompositorCommand {
539 KeyInput {
540 surface_id: u16,
541 keycode: u32,
542 pressed: bool,
543 },
544 PointerMotion {
545 surface_id: u16,
546 x: f64,
547 y: f64,
548 },
549 PointerButton {
550 surface_id: u16,
551 button: u32,
552 pressed: bool,
553 },
554 PointerAxis {
555 surface_id: u16,
556 axis: u8,
557 value: f64,
558 },
559 SurfaceResize {
560 surface_id: u16,
561 width: u16,
562 height: u16,
563 scale_120: u16,
564 },
565 SurfaceFocus {
566 surface_id: u16,
567 },
568 SurfaceClose {
569 surface_id: u16,
570 },
571 ClipboardOffer {
572 mime_type: String,
573 data: Vec<u8>,
574 },
575 Capture {
576 surface_id: u16,
577 scale_120: u16,
578 reply: mpsc::SyncSender<Option<(u32, u32, Vec<u8>)>>,
579 },
580 RequestFrame {
581 surface_id: u16,
582 },
583 ReleaseKeys {
584 keycodes: Vec<u32>,
585 },
586 ClipboardListMimes {
588 reply: mpsc::SyncSender<Vec<String>>,
589 },
590 ClipboardGet {
592 mime_type: String,
593 reply: mpsc::SyncSender<Option<Vec<u8>>>,
594 },
595 SetExternalOutputBuffers {
603 surface_id: u32,
604 target_w: u32,
605 target_h: u32,
606 buffers: Vec<ExternalOutputBuffer>,
607 },
608 RegisterDownscaleTarget {
618 surface_id: u32,
619 target_w: u32,
620 target_h: u32,
621 },
622 ClearDownscaleTarget {
625 surface_id: u32,
626 target_w: u32,
627 target_h: u32,
628 },
629 TextInput {
631 text: String,
632 },
633 SetRefreshRate {
635 mhz: u32,
636 },
637 SetVulkanEncoder {
639 surface_id: u32,
640 client_id: u64,
641 codec: u8,
642 qp: u8,
643 width: u32,
644 height: u32,
645 },
646 SetVulkanEncoderQp {
648 surface_id: u32,
649 client_id: u64,
650 qp: u8,
651 },
652 RequestVulkanKeyframe {
654 surface_id: u32,
655 client_id: u64,
656 },
657 DestroyVulkanEncoder {
660 surface_id: u32,
661 client_id: Option<u64>,
662 },
663 Shutdown,
664}
665
666pub(crate) struct Surface {
672 pub surface_id: u16,
673 pub wl_surface: WlSurface,
674
675 pending_buffer: Option<WlBuffer>,
677 pending_buffer_scale: i32,
678 pending_damage: bool,
679 pending_frame_callbacks: Vec<WlCallback>,
680 pending_presentation_feedbacks: Vec<WpPresentationFeedback>,
681 pending_opaque: bool,
682 pending_input_region: Option<Option<Vec<RegionOp>>>,
685
686 pub buffer_scale: i32,
688 pub is_opaque: bool,
689 input_region: Option<Vec<RegionOp>>,
692
693 pub parent_surface_id: Option<ObjectId>,
695 pending_subsurface_position: Option<(i32, i32)>,
696 pub subsurface_position: (i32, i32),
697 pub children: Vec<ObjectId>,
698
699 xdg_surface: Option<XdgSurface>,
701 xdg_toplevel: Option<XdgToplevel>,
702 xdg_popup: Option<XdgPopup>,
703 pub xdg_geometry: Option<(i32, i32, i32, i32)>,
704
705 title: String,
706 app_id: String,
707
708 pending_viewport_destination: Option<(i32, i32)>,
710 pub viewport_destination: Option<(i32, i32)>,
715
716 is_cursor: bool,
717 cursor_hotspot: (i32, i32),
718}
719
720struct ShmPool {
721 resource: WlShmPool,
722 fd: OwnedFd,
723 inner: std::sync::Mutex<ShmPoolInner>,
724}
725
726struct ShmPoolInner {
727 size: usize,
728 mmap_ptr: *mut u8,
729}
730
731unsafe impl Send for ShmPoolInner {}
734
735impl ShmPool {
736 fn new(resource: WlShmPool, fd: OwnedFd, size: i32) -> Self {
737 let sz = size.max(0) as usize;
738 let ptr = if sz > 0 {
739 unsafe {
740 libc::mmap(
741 std::ptr::null_mut(),
742 sz,
743 libc::PROT_READ,
744 libc::MAP_SHARED,
745 fd.as_raw_fd(),
746 0,
747 )
748 }
749 } else {
750 libc::MAP_FAILED
751 };
752 ShmPool {
753 resource,
754 fd,
755 inner: std::sync::Mutex::new(ShmPoolInner {
756 size: sz,
757 mmap_ptr: if ptr == libc::MAP_FAILED {
758 std::ptr::null_mut()
759 } else {
760 ptr as *mut u8
761 },
762 }),
763 }
764 }
765
766 fn resize(&self, new_size: i32) {
767 let new_sz = new_size.max(0) as usize;
768 let mut inner = self.inner.lock().unwrap();
769 if new_sz <= inner.size {
770 return;
771 }
772 if !inner.mmap_ptr.is_null() {
773 unsafe {
774 libc::munmap(inner.mmap_ptr as *mut _, inner.size);
775 }
776 }
777 let ptr = unsafe {
778 libc::mmap(
779 std::ptr::null_mut(),
780 new_sz,
781 libc::PROT_READ,
782 libc::MAP_SHARED,
783 self.fd.as_raw_fd(),
784 0,
785 )
786 };
787 inner.mmap_ptr = if ptr == libc::MAP_FAILED {
788 std::ptr::null_mut()
789 } else {
790 ptr as *mut u8
791 };
792 inner.size = new_sz;
793 }
794
795 fn with_mmap<F, R>(&self, f: F) -> Option<R>
801 where
802 F: FnOnce(&[u8]) -> R,
803 {
804 let inner = self.inner.lock().unwrap();
805 if inner.mmap_ptr.is_null() {
806 return None;
807 }
808 let slice = unsafe { std::slice::from_raw_parts(inner.mmap_ptr, inner.size) };
809 Some(f(slice))
810 }
811
812 fn read_buffer(
813 &self,
814 offset: i32,
815 width: i32,
816 height: i32,
817 stride: i32,
818 format: wl_shm::Format,
819 ) -> Option<(u32, u32, PixelData)> {
820 let inner = self.inner.lock().unwrap();
821 if inner.mmap_ptr.is_null() {
822 return None;
823 }
824 if offset < 0 || width <= 0 || height <= 0 || stride < 0 {
829 return None;
830 }
831 let w = width as u32;
832 let h = height as u32;
833 let s = stride as usize;
834 let off = offset as usize;
835 let row_bytes = (w as usize).checked_mul(4)?;
836 let needed = s
837 .checked_mul(h as usize - 1)
838 .and_then(|body| body.checked_add(off))
839 .and_then(|n| n.checked_add(row_bytes))?;
840 if needed > inner.size {
841 return None;
842 }
843 let mut bgra = if s == row_bytes && off == 0 {
844 let total = row_bytes * h as usize;
845 unsafe { std::slice::from_raw_parts(inner.mmap_ptr, total) }.to_vec()
846 } else {
847 let mut packed = Vec::with_capacity(row_bytes * h as usize);
848 for row in 0..h as usize {
849 let src = unsafe {
850 std::slice::from_raw_parts(inner.mmap_ptr.add(off + row * s), row_bytes)
851 };
852 packed.extend_from_slice(src);
853 }
854 packed
855 };
856 if matches!(format, wl_shm::Format::Xrgb8888 | wl_shm::Format::Xbgr8888) {
857 for px in bgra.chunks_exact_mut(4) {
858 px[3] = 255;
859 }
860 }
861 if matches!(format, wl_shm::Format::Abgr8888 | wl_shm::Format::Xbgr8888) {
862 Some((w, h, PixelData::Rgba(Arc::new(bgra))))
863 } else {
864 Some((w, h, PixelData::Bgra(Arc::new(bgra))))
865 }
866 }
867}
868
869impl Drop for ShmPool {
870 fn drop(&mut self) {
871 let inner = self.inner.get_mut().unwrap();
872 if !inner.mmap_ptr.is_null() {
873 unsafe {
874 libc::munmap(inner.mmap_ptr as *mut _, inner.size);
875 }
876 }
877 }
878}
879
880unsafe impl Send for ShmPool {}
881
882struct ShmBufferData {
883 pool: Arc<ShmPool>,
889 offset: i32,
890 width: i32,
891 height: i32,
892 stride: i32,
893 format: wl_shm::Format,
894}
895
896struct DmaBufBufferData {
897 width: i32,
898 height: i32,
899 fourcc: u32,
900 modifier: u64,
901 planes: Vec<DmaBufPlane>,
902 y_invert: bool,
903}
904
905struct DmaBufPlane {
906 fd: OwnedFd,
907 offset: u32,
908 stride: u32,
909}
910
911struct DmaBufParamsPending {
912 resource: ZwpLinuxBufferParamsV1,
913 planes: Vec<DmaBufPlane>,
914 modifier: u64,
915}
916
917struct ClientState;
918struct XdgSurfaceData {
919 wl_surface_id: ObjectId,
920}
921struct XdgToplevelData {
922 wl_surface_id: ObjectId,
923}
924struct XdgPopupData {
925 wl_surface_id: ObjectId,
926}
927struct SubsurfaceData {
928 wl_surface_id: ObjectId,
929 parent_surface_id: ObjectId,
930}
931
932struct DataSourceData {
935 mime_types: std::sync::Mutex<Vec<String>>,
936}
937
938struct DataOfferData {
939 external: bool,
943}
944
945struct ExternalClipboard {
947 mime_type: String,
948 data: Vec<u8>,
949}
950
951struct PrimarySourceData {
952 mime_types: std::sync::Mutex<Vec<String>>,
953}
954struct PrimaryOfferData {
955 external: bool,
956}
957
958struct ActivationTokenData {
960 serial: u32,
961}
962
963struct PositionerState {
964 resource: XdgPositioner,
965 geometry: PositionerGeometry,
966}
967
968fn char_to_keycode(ch: char) -> Option<(u32, bool)> {
976 const KEY_1: u32 = 2;
977 const KEY_2: u32 = 3;
978 const KEY_3: u32 = 4;
979 const KEY_4: u32 = 5;
980 const KEY_5: u32 = 6;
981 const KEY_6: u32 = 7;
982 const KEY_7: u32 = 8;
983 const KEY_8: u32 = 9;
984 const KEY_9: u32 = 10;
985 const KEY_0: u32 = 11;
986 const KEY_MINUS: u32 = 12;
987 const KEY_EQUAL: u32 = 13;
988 const KEY_TAB: u32 = 15;
989 const KEY_Q: u32 = 16;
990 const KEY_W: u32 = 17;
991 const KEY_E: u32 = 18;
992 const KEY_R: u32 = 19;
993 const KEY_T: u32 = 20;
994 const KEY_Y: u32 = 21;
995 const KEY_U: u32 = 22;
996 const KEY_I: u32 = 23;
997 const KEY_O: u32 = 24;
998 const KEY_P: u32 = 25;
999 const KEY_LEFTBRACE: u32 = 26;
1000 const KEY_RIGHTBRACE: u32 = 27;
1001 const KEY_ENTER: u32 = 28;
1002 const KEY_A: u32 = 30;
1003 const KEY_S: u32 = 31;
1004 const KEY_D: u32 = 32;
1005 const KEY_F: u32 = 33;
1006 const KEY_G: u32 = 34;
1007 const KEY_H: u32 = 35;
1008 const KEY_J: u32 = 36;
1009 const KEY_K: u32 = 37;
1010 const KEY_L: u32 = 38;
1011 const KEY_SEMICOLON: u32 = 39;
1012 const KEY_APOSTROPHE: u32 = 40;
1013 const KEY_GRAVE: u32 = 41;
1014 const KEY_BACKSLASH: u32 = 43;
1015 const KEY_Z: u32 = 44;
1016 const KEY_X: u32 = 45;
1017 const KEY_C: u32 = 46;
1018 const KEY_V: u32 = 47;
1019 const KEY_B: u32 = 48;
1020 const KEY_N: u32 = 49;
1021 const KEY_M: u32 = 50;
1022 const KEY_COMMA: u32 = 51;
1023 const KEY_DOT: u32 = 52;
1024 const KEY_SLASH: u32 = 53;
1025 const KEY_SPACE: u32 = 57;
1026
1027 fn letter_kc(ch: char) -> u32 {
1028 match ch {
1029 'a' => KEY_A,
1030 'b' => KEY_B,
1031 'c' => KEY_C,
1032 'd' => KEY_D,
1033 'e' => KEY_E,
1034 'f' => KEY_F,
1035 'g' => KEY_G,
1036 'h' => KEY_H,
1037 'i' => KEY_I,
1038 'j' => KEY_J,
1039 'k' => KEY_K,
1040 'l' => KEY_L,
1041 'm' => KEY_M,
1042 'n' => KEY_N,
1043 'o' => KEY_O,
1044 'p' => KEY_P,
1045 'q' => KEY_Q,
1046 'r' => KEY_R,
1047 's' => KEY_S,
1048 't' => KEY_T,
1049 'u' => KEY_U,
1050 'v' => KEY_V,
1051 'w' => KEY_W,
1052 'x' => KEY_X,
1053 'y' => KEY_Y,
1054 'z' => KEY_Z,
1055 _ => KEY_SPACE,
1056 }
1057 }
1058
1059 let (kc, shift) = match ch {
1060 'a'..='z' => (letter_kc(ch), false),
1061 'A'..='Z' => (letter_kc(ch.to_ascii_lowercase()), true),
1062 '0' => (KEY_0, false),
1063 '1'..='9' => (KEY_1 + (ch as u32 - '1' as u32), false),
1064 ' ' => (KEY_SPACE, false),
1065 '-' => (KEY_MINUS, false),
1066 '=' => (KEY_EQUAL, false),
1067 '[' => (KEY_LEFTBRACE, false),
1068 ']' => (KEY_RIGHTBRACE, false),
1069 ';' => (KEY_SEMICOLON, false),
1070 '\'' => (KEY_APOSTROPHE, false),
1071 ',' => (KEY_COMMA, false),
1072 '.' => (KEY_DOT, false),
1073 '/' => (KEY_SLASH, false),
1074 '\\' => (KEY_BACKSLASH, false),
1075 '`' => (KEY_GRAVE, false),
1076 '\t' => (KEY_TAB, false),
1077 '\n' => (KEY_ENTER, false),
1078 '!' => (KEY_1, true),
1079 '@' => (KEY_2, true),
1080 '#' => (KEY_3, true),
1081 '$' => (KEY_4, true),
1082 '%' => (KEY_5, true),
1083 '^' => (KEY_6, true),
1084 '&' => (KEY_7, true),
1085 '*' => (KEY_8, true),
1086 '(' => (KEY_9, true),
1087 ')' => (KEY_0, true),
1088 '_' => (KEY_MINUS, true),
1089 '+' => (KEY_EQUAL, true),
1090 '{' => (KEY_LEFTBRACE, true),
1091 '}' => (KEY_RIGHTBRACE, true),
1092 ':' => (KEY_SEMICOLON, true),
1093 '"' => (KEY_APOSTROPHE, true),
1094 '<' => (KEY_COMMA, true),
1095 '>' => (KEY_DOT, true),
1096 '?' => (KEY_SLASH, true),
1097 '|' => (KEY_BACKSLASH, true),
1098 '~' => (KEY_GRAVE, true),
1099 _ => return None,
1100 };
1101 Some((kc, shift))
1102}
1103
1104const MOD_SHIFT: u32 = 1 << 0;
1110const MOD_LOCK: u32 = 1 << 1;
1111const MOD_CONTROL: u32 = 1 << 2;
1112const MOD_MOD1: u32 = 1 << 3; const MOD_MOD4: u32 = 1 << 6; fn keycode_to_mod(keycode: u32) -> u32 {
1118 match keycode {
1119 42 | 54 => MOD_SHIFT, 58 => MOD_LOCK, 29 | 97 => MOD_CONTROL, 56 | 100 => MOD_MOD1, 125 | 126 => MOD_MOD4, _ => 0,
1125 }
1126}
1127
1128struct TextInputState {
1130 resource: ZwpTextInputV3,
1131 enabled: bool,
1133}
1134
1135struct Compositor {
1137 display_handle: DisplayHandle,
1138 surfaces: HashMap<ObjectId, Surface>,
1139 regions: HashMap<ObjectId, (WlRegion, Vec<RegionOp>)>,
1144 toplevel_surface_ids: HashMap<u16, ObjectId>,
1145 last_request_frame_ms: HashMap<u16, u32>,
1153 next_surface_id: u16,
1154 shm_pools: HashMap<ObjectId, Arc<ShmPool>>,
1155 surface_meta: HashMap<ObjectId, super::render::SurfaceMeta>,
1159 dmabuf_params: HashMap<ObjectId, DmaBufParamsPending>,
1160 vulkan_renderer: Option<super::vulkan_render::VulkanRenderer>,
1161 output_width: i32,
1162 output_height: i32,
1163 output_refresh_mhz: u32,
1166 output_scale_120: u16,
1170 outputs: Vec<WlOutput>,
1171 keyboards: Vec<WlKeyboard>,
1172 pointers: Vec<WlPointer>,
1173 keyboard_keymap_data: Vec<u8>,
1174 mods_depressed: u32,
1176 mods_locked: u32,
1178 serial: u32,
1179 event_tx: mpsc::Sender<CompositorEvent>,
1180 event_notify: Arc<dyn Fn() + Send + Sync>,
1181 loop_signal: LoopSignal,
1182 pending_commits: HashMap<(u16, u32, u32), (u32, u32, PixelData)>,
1189 pending_encoded: Vec<EncodedFrame>,
1193 pending_native_sizes: HashMap<u16, (u32, u32, u32, u32)>,
1200 pending_recomposite_toplevels: HashMap<u16, bool>,
1218 focused_surface_id: u16,
1219 pointer_entered_id: Option<ObjectId>,
1221 pointer_entered_local: (f64, f64),
1224 pending_kb_reenter: bool,
1228
1229 gpu_device: String,
1230 verbose: bool,
1231 shutdown: Arc<AtomicBool>,
1232 last_reported_size: HashMap<u16, (u32, u32, u32, u32)>,
1236 surface_sizes: HashMap<u16, (i32, i32)>,
1240 positioners: HashMap<ObjectId, PositionerState>,
1242 fractional_scales: Vec<WpFractionalScaleV1>,
1245
1246 data_devices: Vec<WlDataDevice>,
1249 selection_source: Option<WlDataSource>,
1252 external_clipboard: Option<ExternalClipboard>,
1254
1255 primary_devices: Vec<ZwpPrimarySelectionDeviceV1>,
1257 primary_source: Option<ZwpPrimarySelectionSourceV1>,
1258 external_primary: Option<ExternalClipboard>,
1259
1260 relative_pointers: Vec<ZwpRelativePointerV1>,
1262
1263 text_inputs: Vec<TextInputState>,
1268 #[expect(dead_code)]
1271 text_input_serial: u32,
1272
1273 next_activation_token: u32,
1275
1276 popup_grab_stack: Vec<ObjectId>,
1281 popup_dismiss_button: Option<u32>,
1285 kb_focus_popup: Option<ObjectId>,
1295
1296 held_buffers: HashMap<ObjectId, WlBuffer>,
1303
1304 cursor_rgba: HashMap<ObjectId, (u32, u32, Vec<u8>)>,
1309}
1310
1311impl Compositor {
1312 fn next_serial(&mut self) -> u32 {
1313 self.serial = self.serial.wrapping_add(1);
1314 self.serial
1315 }
1316
1317 fn update_and_send_modifiers(&mut self, keycode: u32, pressed: bool) {
1322 let m = keycode_to_mod(keycode);
1323 if m == 0 {
1324 return;
1325 }
1326 if keycode == 58 {
1327 if pressed {
1329 self.mods_locked ^= MOD_LOCK;
1330 }
1331 } else if pressed {
1332 self.mods_depressed |= m;
1333 } else {
1334 self.mods_depressed &= !m;
1335 }
1336 let serial = self.next_serial();
1337 let focused_wl = self
1338 .toplevel_surface_ids
1339 .get(&self.focused_surface_id)
1340 .and_then(|root_id| self.surfaces.get(root_id))
1341 .map(|s| s.wl_surface.clone());
1342 for kb in &self.keyboards {
1343 if let Some(ref wl) = focused_wl
1344 && same_client(kb, wl)
1345 {
1346 kb.modifiers(serial, self.mods_depressed, 0, self.mods_locked, 0);
1347 }
1348 }
1349 }
1350
1351 fn keyboard_focus_wl(&self) -> Option<WlSurface> {
1356 if let Some(ref popup_id) = self.kb_focus_popup
1357 && let Some(surf) = self.surfaces.get(popup_id)
1358 {
1359 return Some(surf.wl_surface.clone());
1360 }
1361 self.toplevel_surface_ids
1362 .get(&self.focused_surface_id)
1363 .and_then(|root| self.surfaces.get(root))
1364 .map(|s| s.wl_surface.clone())
1365 }
1366
1367 fn send_keyboard_leave(&mut self, wl: &WlSurface) {
1369 let serial = self.next_serial();
1370 for kb in &self.keyboards {
1371 if same_client(kb, wl) {
1372 kb.leave(serial, wl);
1373 }
1374 }
1375 for ti in &self.text_inputs {
1376 if same_client(&ti.resource, wl) {
1377 ti.resource.leave(wl);
1378 }
1379 }
1380 }
1381
1382 fn send_keyboard_enter(&mut self, wl: &WlSurface) {
1384 let serial = self.next_serial();
1385 for kb in &self.keyboards {
1386 if same_client(kb, wl) {
1387 kb.enter(serial, wl, vec![]);
1388 }
1389 }
1390 for ti in &self.text_inputs {
1391 if same_client(&ti.resource, wl) {
1392 ti.resource.enter(wl);
1393 }
1394 }
1395 }
1396
1397 fn focus_popup(&mut self, popup_id: &ObjectId) {
1404 if self.kb_focus_popup.as_ref() == Some(popup_id) {
1405 return;
1406 }
1407 let Some(popup_wl) = self.surfaces.get(popup_id).map(|s| s.wl_surface.clone()) else {
1408 return;
1409 };
1410 if let Some(previous) = self.keyboard_focus_wl() {
1411 self.send_keyboard_leave(&previous);
1412 }
1413 self.kb_focus_popup = Some(popup_id.clone());
1414 self.send_keyboard_enter(&popup_wl);
1415 let _ = self.display_handle.flush_clients();
1416 }
1417
1418 fn unfocus_popup(&mut self, popup_id: &ObjectId) {
1425 let Some(next_holder) = keyboard_focus_after_popup_close(
1428 popup_id,
1429 self.kb_focus_popup.as_ref(),
1430 &self.popup_grab_stack,
1431 ) else {
1432 return;
1433 };
1434 if let Some(going) = self.surfaces.get(popup_id).map(|s| s.wl_surface.clone()) {
1435 self.send_keyboard_leave(&going);
1436 }
1437 self.kb_focus_popup = next_holder;
1438 if let Some(next) = self.keyboard_focus_wl() {
1439 self.send_keyboard_enter(&next);
1440 }
1441 let _ = self.display_handle.flush_clients();
1442 }
1443
1444 fn set_keyboard_focus(&mut self, new_surface_id: u16) {
1449 let old_id = self.focused_surface_id;
1450 if old_id == new_surface_id {
1451 return;
1468 }
1469
1470 if let Some(old_wl) = self.keyboard_focus_wl()
1475 && (old_id != 0 || self.kb_focus_popup.is_some())
1476 {
1477 self.send_keyboard_leave(&old_wl);
1478 }
1479 self.kb_focus_popup = None;
1481
1482 self.focused_surface_id = new_surface_id;
1483
1484 if let Some(root_id) = self.toplevel_surface_ids.get(&new_surface_id)
1486 && let Some(wl_surface) = self.surfaces.get(root_id).map(|s| s.wl_surface.clone())
1487 {
1488 self.send_keyboard_enter(&wl_surface);
1489 }
1490 }
1491
1492 fn allocate_surface_id(&mut self) -> u16 {
1493 let mut id = self.next_surface_id;
1494 let start = id;
1495 loop {
1496 if !self.toplevel_surface_ids.contains_key(&id) {
1497 break;
1498 }
1499 id = id.wrapping_add(1);
1500 if id == 0 {
1501 id = 1;
1502 }
1503 if id == start {
1504 break;
1505 }
1506 }
1507 self.next_surface_id = id.wrapping_add(1);
1508 if self.next_surface_id == 0 {
1509 self.next_surface_id = 1;
1510 }
1511 id
1512 }
1513
1514 fn flush_pending_commits(&mut self) {
1515 for (surface_id, (width, height, log_w, log_h)) in self.pending_native_sizes.drain() {
1522 let prev = self.last_reported_size.get(&surface_id).copied();
1523 self.last_reported_size
1534 .insert(surface_id, (width, height, log_w, log_h));
1535 if prev.map(|(pw, ph, _, _)| (pw, ph)) != Some((width, height)) {
1539 let _ = self.event_tx.send(CompositorEvent::SurfaceResized {
1540 surface_id,
1541 width: width as u16,
1542 height: height as u16,
1543 });
1544 }
1545 }
1546 let now_ms = elapsed_ms();
1549 #[allow(clippy::type_complexity)]
1550 let mut entries: Vec<((u16, u32, u32), (u32, u32, PixelData))> =
1551 self.pending_commits.drain().collect();
1552 entries.sort_by_key(|((sid, w, h), _)| (*sid, *w, *h));
1553 for ((surface_id, width, height), (_log_w, _log_h, pixels)) in entries {
1554 let _ = self.event_tx.send(CompositorEvent::SurfaceCommit {
1555 surface_id,
1556 width,
1557 height,
1558 pixels,
1559 timestamp_ms: now_ms,
1560 });
1561 }
1562 for frame in self.pending_encoded.drain(..) {
1566 let _ = self.event_tx.send(CompositorEvent::SurfaceEncoded {
1567 frame,
1568 timestamp_ms: now_ms,
1569 });
1570 }
1571 (self.event_notify)();
1572 }
1573
1574 fn read_shm_buffer(&self, buffer: &WlBuffer) -> Option<(u32, u32, PixelData)> {
1575 let data = buffer.data::<ShmBufferData>()?;
1576 let r = data.pool.read_buffer(
1577 data.offset,
1578 data.width,
1579 data.height,
1580 data.stride,
1581 data.format,
1582 );
1583 if r.is_none() {
1584 static N: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1585 let n = N.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1586 if n < 10 || n.is_multiple_of(100) {
1587 eprintln!(
1588 "[read_shm_buffer #{n}] pool.read_buffer=None off={} {}x{} stride={} fmt={:?}",
1589 data.offset, data.width, data.height, data.stride, data.format,
1590 );
1591 }
1592 }
1593 r
1594 }
1595
1596 fn read_dmabuf_buffer(&self, buffer: &WlBuffer) -> Option<(u32, u32, PixelData)> {
1597 let data = buffer.data::<DmaBufBufferData>()?;
1598 let width = data.width as u32;
1599 let height = data.height as u32;
1600 if width == 0 || height == 0 || data.planes.is_empty() {
1601 static N: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1602 let n = N.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1603 if n < 10 || n.is_multiple_of(100) {
1604 eprintln!(
1605 "[read_dmabuf_buffer #{n}] empty: {}x{} planes={}",
1606 width,
1607 height,
1608 data.planes.len()
1609 );
1610 }
1611 return None;
1612 }
1613 let plane = &data.planes[0];
1614 if matches!(
1615 data.fourcc,
1616 drm_fourcc::ARGB8888
1617 | drm_fourcc::XRGB8888
1618 | drm_fourcc::ABGR8888
1619 | drm_fourcc::XBGR8888
1620 ) {
1621 use std::os::fd::AsRawFd;
1624 let raw_fd = plane.fd.as_raw_fd();
1625 let _is_drm = {
1626 let mut link_buf = [0u8; 256];
1627 let path = format!("/proc/self/fd/{raw_fd}\0");
1628 let n = unsafe {
1629 libc::readlink(
1630 path.as_ptr() as *const _,
1631 link_buf.as_mut_ptr() as *mut _,
1632 255,
1633 )
1634 };
1635 n > 0 && link_buf[..n as usize].starts_with(b"/dev/dri/")
1636 };
1637
1638 let owned = plane.fd.try_clone().ok()?;
1642 return Some((
1643 width,
1644 height,
1645 PixelData::DmaBuf {
1646 fd: Arc::new(owned),
1647 fourcc: data.fourcc,
1648 modifier: data.modifier,
1649 stride: plane.stride,
1650 offset: plane.offset,
1651 y_invert: data.y_invert,
1652 },
1653 ));
1654 }
1655 static N: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1656 let n = N.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1657 if n < 10 || n.is_multiple_of(100) {
1658 eprintln!(
1659 "[read_dmabuf_buffer #{n}] unsupported fourcc=0x{:08x} ({}x{}) modifier=0x{:x}",
1660 data.fourcc, width, height, data.modifier,
1661 );
1662 }
1663 None
1664 }
1665
1666 fn read_buffer(&self, buffer: &WlBuffer) -> Option<(u32, u32, PixelData)> {
1667 if buffer.data::<ShmBufferData>().is_some() {
1671 return self.read_shm_buffer(buffer);
1672 }
1673 if buffer.data::<DmaBufBufferData>().is_some() {
1674 return self.read_dmabuf_buffer(buffer);
1675 }
1676 static N: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1677 let n = N.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1678 if n < 10 || n.is_multiple_of(100) {
1679 eprintln!(
1680 "[read_buffer #{n}] buffer has unknown role (neither Shm nor DmaBuf data attached)",
1681 );
1682 }
1683 None
1684 }
1685
1686 fn handle_surface_commit(&mut self, surface_id: &ObjectId) {
1687 let (root_id, toplevel_sid) = self.find_toplevel_root(surface_id);
1688
1689 let had_buffer = self
1694 .surfaces
1695 .get(surface_id)
1696 .is_some_and(|s| s.pending_buffer.is_some());
1697 {
1698 static N: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1699 let n = N.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1700 if n < 40 || n.is_multiple_of(200) {
1701 let children = self
1702 .surfaces
1703 .get(surface_id)
1704 .map(|s| s.children.len())
1705 .unwrap_or(0);
1706 eprintln!(
1707 "[commit-in #{n}] sid={surface_id:?} toplevel={toplevel_sid:?} root={root_id:?} had_buffer={had_buffer} children={children}",
1708 );
1709 }
1710 }
1711 self.apply_pending_state(surface_id);
1712
1713 let toplevel_sid = match toplevel_sid {
1714 Some(sid) => sid,
1715 None => {
1716 if let Some(held) = self.held_buffers.remove(surface_id) {
1719 held.release();
1720 }
1721 self.fire_surface_frame_callbacks(surface_id);
1724 let _ = self.display_handle.flush_clients();
1725 return;
1726 }
1727 };
1728
1729 self.composite_toplevel_into_pending(&root_id, toplevel_sid, false);
1730
1731 if let Some(held) = self.held_buffers.remove(surface_id) {
1736 held.release();
1737 }
1738
1739 const PACING_GRACE_MS: u32 = 500;
1754 let paced = self
1755 .last_request_frame_ms
1756 .get(&toplevel_sid)
1757 .is_some_and(|&t| elapsed_ms().wrapping_sub(t) < PACING_GRACE_MS);
1758 if !paced {
1759 self.fire_frame_callbacks_for_toplevel(toplevel_sid);
1760 }
1761
1762 if self.pending_kb_reenter {
1767 self.pending_kb_reenter = false;
1768 if let Some(wl) = self.keyboard_focus_wl() {
1780 self.send_keyboard_leave(&wl);
1781 self.send_keyboard_enter(&wl);
1782 }
1783 let _ = self.display_handle.flush_clients();
1784 }
1785
1786 if self.verbose {
1787 let cache_entries = self.surface_meta.len();
1788 let has_pending = self
1789 .pending_commits
1790 .keys()
1791 .any(|(sid, _, _)| *sid == toplevel_sid);
1792 static COMMIT_COUNT: std::sync::atomic::AtomicU64 =
1793 std::sync::atomic::AtomicU64::new(0);
1794 let n = COMMIT_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1795 if n < 5 || n.is_multiple_of(1000) {
1796 eprintln!(
1797 "[commit #{n}] sid={surface_id:?} root={root_id:?} cache={cache_entries} pending={has_pending} buf={had_buffer}",
1798 );
1799 }
1800 }
1801 }
1802
1803 fn composite_toplevel_into_pending(
1813 &mut self,
1814 root_id: &ObjectId,
1815 toplevel_sid: u16,
1816 encoder_only: bool,
1817 ) {
1818 let s120 = self.output_scale_120;
1822 let target_phys = self.surface_sizes.get(&toplevel_sid).map(|&(lw, lh)| {
1823 let pw = super::render::to_physical(lw as u32, s120 as u32);
1824 let ph = super::render::to_physical(lh as u32, s120 as u32);
1825 (pw, ph)
1826 });
1827 let composited = if let Some(ref mut vk) = self.vulkan_renderer {
1828 let composited = vk.render_tree_sized(
1829 root_id,
1830 &self.surfaces,
1831 &self.surface_meta,
1832 s120,
1833 target_phys,
1834 toplevel_sid,
1835 );
1836 self.pending_encoded.extend(vk.take_encoded_frames());
1837 composited
1838 } else {
1839 Vec::new()
1840 };
1841
1842 let s120_u32 = (s120 as u32).max(120);
1849 if let Some((nw, nh)) = target_phys {
1850 let nlog_w = (nw * 120).div_ceil(s120_u32);
1851 let nlog_h = (nh * 120).div_ceil(s120_u32);
1852 self.pending_native_sizes
1853 .insert(toplevel_sid, (nw, nh, nlog_w, nlog_h));
1854 } else if let Some((sid, nw, nh, _)) = composited
1855 .iter()
1856 .max_by_key(|(_, w, h, _)| (*w as u64) * (*h as u64))
1857 {
1858 let nlog_w = (nw * 120).div_ceil(s120_u32);
1859 let nlog_h = (nh * 120).div_ceil(s120_u32);
1860 self.pending_native_sizes
1861 .insert(*sid, (*nw, *nh, nlog_w, nlog_h));
1862 }
1863
1864 for (result_sid, w, h, pixels) in composited {
1865 if pixels.is_empty() {
1866 continue;
1867 }
1868 if encoder_only {
1873 continue;
1874 }
1875 let kind = match &pixels {
1876 PixelData::Bgra(_) => "bgra",
1877 PixelData::Rgba(_) => "rgba",
1878 PixelData::Nv12 { .. } => "nv12",
1879 PixelData::VaSurface { .. } => "va-surface",
1880 PixelData::Nv12DmaBuf { .. } => "nv12-dmabuf",
1881 PixelData::DmaBuf { fd, .. } => {
1882 use std::os::fd::AsRawFd;
1883 let raw = fd.as_raw_fd();
1884 let mut lb = [0u8; 128];
1885 let p = format!("/proc/self/fd/{raw}\0");
1886 let n = unsafe {
1887 libc::readlink(p.as_ptr() as *const _, lb.as_mut_ptr() as *mut _, 127)
1888 };
1889 if n > 0 && lb[..n as usize].starts_with(b"/dev/dri/") {
1890 "dmabuf-drm"
1891 } else {
1892 "dmabuf-anon"
1893 }
1894 }
1895 };
1896 if self.verbose {
1897 static LC: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1898 let lc = LC.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1899 if lc < 3 || lc.is_multiple_of(1000) {
1900 eprintln!("[pending #{lc}] {w}x{h} kind={kind}");
1901 }
1902 }
1903 let log_w = (w * 120).div_ceil(s120_u32);
1908 let log_h = (h * 120).div_ceil(s120_u32);
1909 self.pending_commits
1910 .insert((result_sid, w, h), (log_w, log_h, pixels));
1911 }
1912 }
1913
1914 fn surface_absolute_position(&self, surface_id: &ObjectId) -> (i32, i32) {
1918 let mut x = 0i32;
1919 let mut y = 0i32;
1920 let mut current = surface_id.clone();
1921 while let Some(surf) = self.surfaces.get(¤t) {
1922 x += surf.subsurface_position.0;
1923 y += surf.subsurface_position.1;
1924 match surf.parent_surface_id {
1925 Some(ref parent) => current = parent.clone(),
1926 None => break,
1927 }
1928 }
1929 (x, y)
1930 }
1931
1932 fn find_toplevel_root(&self, surface_id: &ObjectId) -> (ObjectId, Option<u16>) {
1933 let mut current = surface_id.clone();
1934 loop {
1935 match self.surfaces.get(¤t) {
1936 Some(surf) => {
1937 if let Some(ref parent) = surf.parent_surface_id {
1938 current = parent.clone();
1939 } else {
1940 return (
1941 current,
1942 if surf.surface_id > 0 {
1943 Some(surf.surface_id)
1944 } else {
1945 None
1946 },
1947 );
1948 }
1949 }
1950 None => return (current, None),
1951 }
1952 }
1953 }
1954
1955 fn collect_surface_tree(&self, root_id: &ObjectId) -> Vec<ObjectId> {
1956 let mut result = Vec::new();
1957 self.collect_tree_recursive(root_id, &mut result);
1958 result
1959 }
1960
1961 fn collect_tree_recursive(&self, surface_id: &ObjectId, result: &mut Vec<ObjectId>) {
1962 result.push(surface_id.clone());
1963 if let Some(surf) = self.surfaces.get(surface_id) {
1964 for child_id in &surf.children {
1965 self.collect_tree_recursive(child_id, result);
1966 }
1967 }
1968 }
1969
1970 fn hit_test_surface_at(
1975 &self,
1976 root_id: &ObjectId,
1977 x: f64,
1978 y: f64,
1979 ) -> Option<(WlSurface, f64, f64)> {
1980 self.hit_test_recursive(root_id, x, y, 0, 0).or_else(|| {
1981 self.surfaces
1983 .get(root_id)
1984 .map(|s| (s.wl_surface.clone(), x, y))
1985 })
1986 }
1987
1988 fn hit_test_recursive(
1989 &self,
1990 surface_id: &ObjectId,
1991 x: f64,
1992 y: f64,
1993 offset_x: i32,
1994 offset_y: i32,
1995 ) -> Option<(WlSurface, f64, f64)> {
1996 let surf = self.surfaces.get(surface_id)?;
1997 let sx = offset_x + surf.subsurface_position.0;
1998 let sy = offset_y + surf.subsurface_position.1;
1999
2000 for child_id in surf.children.iter().rev() {
2002 if let Some(hit) = self.hit_test_recursive(child_id, x, y, sx, sy) {
2003 return Some(hit);
2004 }
2005 }
2006
2007 if let Some(sm) = self.surface_meta.get(surface_id) {
2009 let s = sm.scale.max(1) as f64;
2010 let (w, h) = (sm.width, sm.height);
2011 let (lw, lh) = surf
2014 .viewport_destination
2015 .filter(|&(dw, dh)| dw > 0 && dh > 0)
2016 .map(|(dw, dh)| (dw as f64, dh as f64))
2017 .unwrap_or((w as f64 / s, h as f64 / s));
2018 let lx = x - sx as f64;
2019 let ly = y - sy as f64;
2020 if lx >= 0.0 && ly >= 0.0 && lx < lw && ly < lh {
2021 match surf.input_region {
2028 Some(ref ops) if !input_region::contains(ops, lx, ly) => {}
2029 _ => return Some((surf.wl_surface.clone(), lx, ly)),
2030 }
2031 }
2032 }
2033 None
2034 }
2035
2036 fn apply_pending_state(&mut self, surface_id: &ObjectId) {
2049 let (buffer, scale, is_cursor) = {
2050 let Some(surf) = self.surfaces.get_mut(surface_id) else {
2051 return;
2052 };
2053 let buffer = surf.pending_buffer.take();
2054 let scale = surf.pending_buffer_scale;
2055 surf.buffer_scale = scale;
2056 surf.viewport_destination = surf.pending_viewport_destination;
2057 surf.is_opaque = surf.pending_opaque;
2058 if let Some(region) = surf.pending_input_region.take() {
2059 surf.input_region = region;
2060 }
2061 surf.pending_damage = false;
2062 if let Some(pos) = surf.pending_subsurface_position.take() {
2063 surf.subsurface_position = pos;
2064 }
2065 (buffer, scale, surf.is_cursor)
2066 };
2067 let Some(buf) = buffer else { return };
2068
2069 if let Some(old) = self.held_buffers.remove(surface_id) {
2072 old.release();
2073 }
2074
2075 if !is_cursor && let Some(shm) = buf.data::<ShmBufferData>() {
2081 let w = shm.width as u32;
2082 let h = shm.height as u32;
2083 let stride = shm.stride as usize;
2084 let offset = shm.offset as usize;
2085 let format = shm.format;
2086 if shm.width > 0
2090 && shm.height > 0
2091 && shm.stride >= 0
2092 && shm.offset >= 0
2093 && let Some(ref mut vk) = self.vulkan_renderer
2094 {
2095 let swap_rb =
2096 !matches!(format, wl_shm::Format::Abgr8888 | wl_shm::Format::Xbgr8888);
2097 let force_opaque =
2098 matches!(format, wl_shm::Format::Xrgb8888 | wl_shm::Format::Xbgr8888);
2099 let row_bytes = w as usize * 4;
2100 let uploaded = shm
2101 .pool
2102 .with_mmap(|slice| {
2103 let needed = stride
2107 .checked_mul(h as usize - 1)
2108 .and_then(|body| body.checked_add(offset))
2109 .and_then(|n| n.checked_add(row_bytes));
2110 match needed {
2111 Some(n) if n <= slice.len() => {}
2112 _ => return false,
2113 }
2114 vk.upload_surface_shm_mmap(
2115 surface_id,
2116 slice,
2117 offset,
2118 stride,
2119 w,
2120 h,
2121 swap_rb,
2122 force_opaque,
2123 )
2124 })
2125 .unwrap_or(false);
2126 if uploaded {
2127 self.surface_meta.insert(
2128 surface_id.clone(),
2129 super::render::SurfaceMeta {
2130 width: w,
2131 height: h,
2132 scale,
2133 y_invert: false,
2134 },
2135 );
2136 buf.release();
2137 return;
2138 }
2139 }
2140 }
2141
2142 if let Some((w, h, pixels)) = self.read_buffer(&buf) {
2143 let y_invert = matches!(pixels, PixelData::DmaBuf { y_invert: true, .. });
2144
2145 if let Some(ref mut vk) = self.vulkan_renderer {
2147 vk.upload_surface(surface_id, &pixels, w, h);
2148 }
2149
2150 self.surface_meta.insert(
2152 surface_id.clone(),
2153 super::render::SurfaceMeta {
2154 width: w,
2155 height: h,
2156 scale,
2157 y_invert,
2158 },
2159 );
2160
2161 if is_cursor {
2164 let rgba = pixels.to_rgba(w, h);
2165 if !rgba.is_empty() {
2166 self.cursor_rgba.insert(surface_id.clone(), (w, h, rgba));
2167 }
2168 }
2169
2170 if pixels.is_dmabuf() {
2171 self.held_buffers.insert(surface_id.clone(), buf);
2174 } else {
2175 buf.release();
2178 }
2179 } else {
2180 buf.release();
2181 }
2182 }
2183
2184 fn fire_surface_frame_callbacks(&mut self, surface_id: &ObjectId) {
2185 let (callbacks, feedbacks) = {
2186 let Some(surf) = self.surfaces.get_mut(surface_id) else {
2187 return;
2188 };
2189 (
2190 std::mem::take(&mut surf.pending_frame_callbacks),
2191 std::mem::take(&mut surf.pending_presentation_feedbacks),
2192 )
2193 };
2194 let time = elapsed_ms();
2195 for cb in callbacks {
2196 cb.done(time);
2197 }
2198 if !feedbacks.is_empty() {
2199 let (sec, nsec) = monotonic_timespec();
2200 for fb in feedbacks {
2203 for output in &self.outputs {
2204 if same_client(&fb, output) {
2205 fb.sync_output(output);
2206 }
2207 }
2208 let refresh_ns = if self.output_refresh_mhz > 0 {
2210 (1_000_000_000_000u64 / self.output_refresh_mhz as u64) as u32
2211 } else {
2212 0
2213 };
2214 fb.presented(
2215 (sec >> 32) as u32,
2216 sec as u32,
2217 nsec as u32,
2218 refresh_ns,
2219 0, 0, WpPresentationFeedbackKind::empty(),
2222 );
2223 }
2224 }
2225 }
2226
2227 fn forget_pointer_focus(&mut self, gone: &ObjectId) {
2241 if self.pointer_entered_id.as_ref() == Some(gone) {
2242 self.pointer_entered_id = None;
2243 }
2244 }
2245
2246 fn cleanup_dead_surfaces(&mut self) {
2251 self.fractional_scales.retain(|fs| fs.is_alive());
2253 self.outputs.retain(|o| o.is_alive());
2254 self.keyboards.retain(|k| k.is_alive());
2255 self.pointers.retain(|p| p.is_alive());
2256 self.data_devices.retain(|d| d.is_alive());
2257 self.primary_devices.retain(|d| d.is_alive());
2258 self.relative_pointers.retain(|p| p.is_alive());
2259 self.text_inputs.retain(|ti| ti.resource.is_alive());
2260 self.shm_pools.retain(|_, p| p.resource.is_alive());
2261 self.dmabuf_params.retain(|_, p| p.resource.is_alive());
2262 self.positioners.retain(|_, p| p.resource.is_alive());
2263 self.regions.retain(|_, (r, _)| r.is_alive());
2266
2267 let dead: Vec<ObjectId> = self
2268 .surfaces
2269 .iter()
2270 .filter(|(_, surf)| !surf.wl_surface.is_alive())
2271 .map(|(id, _)| id.clone())
2272 .collect();
2273
2274 for proto_id in &dead {
2275 self.surface_meta.remove(proto_id);
2276 if let Some(ref mut vk) = self.vulkan_renderer {
2277 vk.remove_surface(proto_id);
2278 }
2279 if let Some(held) = self.held_buffers.remove(proto_id) {
2280 held.release();
2281 }
2282 self.forget_pointer_focus(proto_id);
2283 if self.kb_focus_popup.as_ref() == Some(proto_id) {
2287 self.popup_grab_stack.retain(|id| id != proto_id);
2288 self.kb_focus_popup = self.popup_grab_stack.last().cloned();
2289 if let Some(next) = self.keyboard_focus_wl() {
2290 self.send_keyboard_enter(&next);
2291 }
2292 }
2293 if let Some(surf) = self.surfaces.remove(proto_id) {
2294 for fb in surf.pending_presentation_feedbacks {
2297 fb.discarded();
2298 }
2299 if let Some(ref parent_id) = surf.parent_surface_id
2300 && let Some(parent) = self.surfaces.get_mut(parent_id)
2301 {
2302 parent.children.retain(|c| c != proto_id);
2303 }
2304 if surf.surface_id > 0 {
2305 self.toplevel_surface_ids.remove(&surf.surface_id);
2306 if self.focused_surface_id == surf.surface_id {
2311 self.focused_surface_id = 0;
2312 }
2313 self.last_request_frame_ms.remove(&surf.surface_id);
2314 self.last_reported_size.remove(&surf.surface_id);
2315 self.surface_sizes.remove(&surf.surface_id);
2316 if let Some(ref mut vk) = self.vulkan_renderer {
2317 vk.destroy_external_outputs_for_surface(surf.surface_id as u32);
2318 }
2319 let _ = self.event_tx.send(CompositorEvent::SurfaceDestroyed {
2320 surface_id: surf.surface_id,
2321 });
2322 (self.event_notify)();
2323 }
2324 }
2325 }
2326 }
2327
2328 fn fire_frame_callbacks_for_toplevel(&mut self, toplevel_sid: u16) {
2329 let Some(root_id) = self.toplevel_surface_ids.get(&toplevel_sid).cloned() else {
2330 return;
2331 };
2332 let tree = self.collect_surface_tree(&root_id);
2333 for sid in &tree {
2334 self.fire_surface_frame_callbacks(sid);
2335 }
2336 let _ = self.display_handle.flush_clients();
2337 }
2338
2339 fn handle_cursor_commit(&mut self, surface_id: &ObjectId) {
2340 self.apply_pending_state(surface_id);
2341 let hotspot = self
2342 .surfaces
2343 .get(surface_id)
2344 .map(|s| s.cursor_hotspot)
2345 .unwrap_or((0, 0));
2346 if let Some((w, h, rgba)) = self.cursor_rgba.get(surface_id)
2347 && !rgba.is_empty()
2348 {
2349 let _ = self.event_tx.send(CompositorEvent::SurfaceCursor {
2350 surface_id: self.focused_surface_id,
2351 cursor: CursorImage::Custom {
2352 hotspot_x: hotspot.0 as u16,
2353 hotspot_y: hotspot.1 as u16,
2354 width: *w as u16,
2355 height: *h as u16,
2356 rgba: rgba.clone(),
2357 },
2358 });
2359 }
2360 self.fire_surface_frame_callbacks(surface_id);
2361 let _ = self.display_handle.flush_clients();
2362 }
2363
2364 fn handle_command(&mut self, cmd: CompositorCommand) {
2365 match cmd {
2366 CompositorCommand::KeyInput {
2367 surface_id: _,
2368 keycode,
2369 pressed,
2370 } => {
2371 let serial = self.next_serial();
2372 let time = elapsed_ms();
2373 let state = if pressed {
2374 wl_keyboard::KeyState::Pressed
2375 } else {
2376 wl_keyboard::KeyState::Released
2377 };
2378 let focused_wl = self.keyboard_focus_wl();
2382 for kb in &self.keyboards {
2383 if let Some(ref wl) = focused_wl
2384 && same_client(kb, wl)
2385 {
2386 kb.key(serial, time, keycode, state);
2387 }
2388 }
2389 self.update_and_send_modifiers(keycode, pressed);
2394 let _ = self.display_handle.flush_clients();
2395 }
2396 CompositorCommand::TextInput { text } => {
2397 let focused_wl = self
2398 .toplevel_surface_ids
2399 .get(&self.focused_surface_id)
2400 .and_then(|root_id| self.surfaces.get(root_id))
2401 .map(|s| s.wl_surface.clone());
2402 let Some(focused_wl) = focused_wl else { return };
2403
2404 const KEY_LEFTSHIFT: u32 = 42;
2420 let saved_mods_depressed = self.mods_depressed;
2421 for ch in text.chars() {
2422 if let Some((kc, need_shift)) = char_to_keycode(ch) {
2423 let time = elapsed_ms();
2424 if need_shift {
2425 let serial = self.next_serial();
2426 for kb in &self.keyboards {
2427 if same_client(kb, &focused_wl) {
2428 kb.key(
2429 serial,
2430 time,
2431 KEY_LEFTSHIFT,
2432 wl_keyboard::KeyState::Pressed,
2433 );
2434 }
2435 }
2436 self.update_and_send_modifiers(KEY_LEFTSHIFT, true);
2437 }
2438 let serial = self.next_serial();
2439 for kb in &self.keyboards {
2440 if same_client(kb, &focused_wl) {
2441 kb.key(serial, time, kc, wl_keyboard::KeyState::Pressed);
2442 }
2443 }
2444 let serial = self.next_serial();
2445 for kb in &self.keyboards {
2446 if same_client(kb, &focused_wl) {
2447 kb.key(serial, time, kc, wl_keyboard::KeyState::Released);
2448 }
2449 }
2450 if need_shift {
2451 let serial = self.next_serial();
2452 for kb in &self.keyboards {
2453 if same_client(kb, &focused_wl) {
2454 kb.key(
2455 serial,
2456 time,
2457 KEY_LEFTSHIFT,
2458 wl_keyboard::KeyState::Released,
2459 );
2460 }
2461 }
2462 self.update_and_send_modifiers(KEY_LEFTSHIFT, false);
2463 }
2464 }
2465 }
2468 if self.mods_depressed != saved_mods_depressed {
2472 self.mods_depressed = saved_mods_depressed;
2473 let serial = self.next_serial();
2474 for kb in &self.keyboards {
2475 if same_client(kb, &focused_wl) {
2476 kb.modifiers(serial, self.mods_depressed, 0, self.mods_locked, 0);
2477 }
2478 }
2479 }
2480 let _ = self.display_handle.flush_clients();
2481 }
2482 CompositorCommand::PointerMotion { surface_id, x, y } => {
2483 let time = elapsed_ms();
2484 let (mut x, mut y) =
2489 if let Some(&(cw, ch, lw, lh)) = self.last_reported_size.get(&surface_id) {
2490 let sx = if cw > 0 { lw as f64 / cw as f64 } else { 1.0 };
2491 let sy = if ch > 0 { lh as f64 / ch as f64 } else { 1.0 };
2492 (x * sx, y * sy)
2493 } else {
2494 (x, y)
2495 };
2496 if let Some((gx, gy, _, _)) = self
2500 .toplevel_surface_ids
2501 .get(&surface_id)
2502 .and_then(|rid| self.surfaces.get(rid))
2503 .and_then(|s| s.xdg_geometry)
2504 {
2505 x += gx as f64;
2506 y += gy as f64;
2507 }
2508 let target_wl = self
2511 .toplevel_surface_ids
2512 .get(&surface_id)
2513 .and_then(|root_id| self.hit_test_surface_at(root_id, x, y))
2514 .map(|(wl_surface, lx, ly)| (wl_surface.id(), wl_surface, lx, ly));
2515
2516 static PTR_DBG: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
2517 let pn = PTR_DBG.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2518 if pn < 5 || pn.is_multiple_of(500) {
2519 let root = self.toplevel_surface_ids.get(&surface_id).cloned();
2520 let lrs = self.last_reported_size.get(&surface_id).copied();
2521 eprintln!(
2522 "[pointer #{pn}] sid={surface_id} logical=({x:.1},{y:.1}) lrs={lrs:?} root={root:?} hit={:?}",
2523 target_wl.as_ref().map(|(pid, _, lx, ly)| format!(
2524 "proto={pid:?} local=({lx:.1},{ly:.1})"
2525 ))
2526 );
2527 }
2528 if let Some((proto_id, wl_surface, lx, ly)) = target_wl {
2529 self.pointer_entered_local = (lx, ly);
2533 let matching_ptrs = self
2534 .pointers
2535 .iter()
2536 .filter(|p| same_client(*p, &wl_surface))
2537 .count();
2538 if let Some(change) = focus_transition(
2539 self.pointer_entered_id.as_ref(),
2540 &proto_id,
2541 matching_ptrs > 0,
2542 ) {
2543 let serial = self.next_serial();
2544 if matching_ptrs == 0 {
2545 eprintln!(
2552 "[pointer-enter] proto={proto_id:?} has no pointer yet (total_ptrs={}); deferring",
2553 self.pointers.len()
2554 );
2555 }
2556 if let Some(ref leaving) = change.leave {
2558 let old_wl = self
2559 .surfaces
2560 .values()
2561 .find(|s| s.wl_surface.id() == *leaving)
2562 .map(|s| s.wl_surface.clone());
2563 if let Some(old_wl) = old_wl {
2564 for ptr in &self.pointers {
2565 if same_client(ptr, &old_wl) {
2566 ptr.leave(serial, &old_wl);
2567 ptr.frame();
2568 }
2569 }
2570 }
2571 }
2572 for ptr in &self.pointers {
2573 if same_client(ptr, &wl_surface) {
2574 ptr.enter(serial, &wl_surface, lx, ly);
2575 }
2576 }
2577 self.pointer_entered_id = change.entered;
2583 }
2584 for ptr in &self.pointers {
2585 if same_client(ptr, &wl_surface) {
2586 ptr.motion(time, lx, ly);
2587 ptr.frame();
2588 }
2589 }
2590 }
2591 let _ = self.display_handle.flush_clients();
2594 }
2595 CompositorCommand::PointerButton {
2596 surface_id: _,
2597 button,
2598 pressed,
2599 } => {
2600 let serial = self.next_serial();
2601 let time = elapsed_ms();
2602 let state = if pressed {
2603 wl_pointer::ButtonState::Pressed
2604 } else {
2605 wl_pointer::ButtonState::Released
2606 };
2607
2608 let mut dismissed = false;
2611 if pressed && !self.popup_grab_stack.is_empty() {
2612 let click_on_grabbed = self.pointer_entered_id.as_ref().is_some_and(|eid| {
2613 self.popup_grab_stack.iter().any(|gid| {
2614 self.surfaces
2615 .get(gid)
2616 .is_some_and(|s| s.wl_surface.id() == *eid)
2617 })
2618 });
2619 if !click_on_grabbed {
2620 while let Some(grab_wl_id) = self.popup_grab_stack.pop() {
2622 if let Some(surf) = self.surfaces.get(&grab_wl_id)
2623 && let Some(ref popup) = surf.xdg_popup
2624 {
2625 popup.popup_done();
2626 }
2627 self.unfocus_popup(&grab_wl_id);
2632 }
2633 let _ = self.display_handle.flush_clients();
2634 dismissed = true;
2635 }
2636 }
2637
2638 let (routing, swallow) =
2641 button_routing(pressed, button, dismissed, self.popup_dismiss_button);
2642 self.popup_dismiss_button = swallow;
2643
2644 if routing == ButtonRouting::Deliver {
2645 let focused_wl = self
2646 .surfaces
2647 .values()
2648 .find(|s| Some(s.wl_surface.id()) == self.pointer_entered_id)
2649 .map(|s| s.wl_surface.clone());
2650 for ptr in &self.pointers {
2651 if let Some(ref wl) = focused_wl
2652 && same_client(ptr, wl)
2653 {
2654 ptr.button(serial, time, button, state);
2655 ptr.frame();
2656 }
2657 }
2658 }
2659 let _ = self.display_handle.flush_clients();
2660 }
2661 CompositorCommand::PointerAxis {
2662 surface_id: _,
2663 axis,
2664 value,
2665 } => {
2666 let time = elapsed_ms();
2667 let wl_axis = if axis == 0 {
2668 wl_pointer::Axis::VerticalScroll
2669 } else {
2670 wl_pointer::Axis::HorizontalScroll
2671 };
2672 let focused_wl = self
2673 .surfaces
2674 .values()
2675 .find(|s| Some(s.wl_surface.id()) == self.pointer_entered_id)
2676 .map(|s| s.wl_surface.clone());
2677 for ptr in &self.pointers {
2678 if let Some(ref wl) = focused_wl
2679 && same_client(ptr, wl)
2680 {
2681 ptr.axis(time, wl_axis, value);
2682 ptr.frame();
2683 }
2684 }
2685 let _ = self.display_handle.flush_clients();
2686 }
2687 CompositorCommand::SurfaceResize {
2688 surface_id,
2689 width,
2690 height,
2691 scale_120,
2692 } => {
2693 let s_in = (scale_120 as i32).max(120);
2696 let w = (width as i32) * 120 / s_in;
2697 let h = (height as i32) * 120 / s_in;
2698 self.surface_sizes.insert(surface_id, (w, h));
2699
2700 let mut output_changed = false;
2703
2704 if scale_120 > 0 && scale_120 != self.output_scale_120 {
2706 self.output_scale_120 = scale_120;
2707 output_changed = true;
2708 }
2709
2710 let s120 = self.output_scale_120 as i32;
2711
2712 let (max_w, max_h) = self
2717 .surface_sizes
2718 .values()
2719 .fold((0i32, 0i32), |(mw, mh), &(sw, sh)| (mw.max(sw), mh.max(sh)));
2720 let max_w = max_w.max(1);
2722 let max_h = max_h.max(1);
2723 if max_w != self.output_width || max_h != self.output_height {
2724 self.output_width = max_w;
2725 self.output_height = max_h;
2726 output_changed = true;
2727 }
2728
2729 if output_changed {
2733 let int_scale = ((s120) + 119) / 120;
2734 for output in &self.outputs {
2735 output.geometry(
2736 0,
2737 0,
2738 0,
2739 0,
2740 wl_output::Subpixel::None,
2741 "blit".to_string(),
2742 "virtual".to_string(),
2743 wl_output::Transform::Normal,
2744 );
2745 let mode_w = self.output_width * s120 / 120;
2747 let mode_h = self.output_height * s120 / 120;
2748 output.mode(
2749 wl_output::Mode::Current | wl_output::Mode::Preferred,
2750 mode_w,
2751 mode_h,
2752 self.output_refresh_mhz as i32,
2753 );
2754 if output.version() >= 2 {
2755 output.scale(int_scale);
2756 }
2757 }
2758 for fs in &self.fractional_scales {
2759 fs.preferred_scale(s120 as u32);
2760 }
2761 }
2762
2763 if output_changed {
2766 for output in &self.outputs {
2767 if output.version() >= 2 {
2768 output.done();
2769 }
2770 }
2771 }
2772
2773 let states = xdg_toplevel_states(&[
2774 xdg_toplevel::State::Activated,
2775 xdg_toplevel::State::Maximized,
2776 ]);
2777
2778 if output_changed {
2779 for (&sid, root_id) in &self.toplevel_surface_ids {
2783 let (lw, lh) = self.surface_sizes.get(&sid).copied().unwrap_or((w, h));
2784 if let Some(surf) = self.surfaces.get(root_id) {
2785 if let Some(ref tl) = surf.xdg_toplevel {
2786 tl.configure(lw, lh, states.clone());
2787 }
2788 if let Some(ref xs) = surf.xdg_surface {
2789 let serial = self.serial.wrapping_add(1);
2790 self.serial = serial;
2791 xs.configure(serial);
2792 }
2793 }
2794 }
2795 let all_sids: Vec<u16> = self.toplevel_surface_ids.keys().copied().collect();
2798 for sid in all_sids {
2799 self.fire_frame_callbacks_for_toplevel(sid);
2800 }
2801
2802 self.pointer_entered_id = None;
2805 self.pending_kb_reenter = true;
2806 } else {
2807 if let Some(root_id) = self.toplevel_surface_ids.get(&surface_id)
2812 && let Some(surf) = self.surfaces.get(root_id)
2813 {
2814 if let Some(ref tl) = surf.xdg_toplevel {
2815 tl.configure(w, h, states);
2816 }
2817 if let Some(ref xs) = surf.xdg_surface {
2818 let serial = self.serial.wrapping_add(1);
2819 self.serial = serial;
2820 xs.configure(serial);
2821 }
2822 }
2823 self.fire_frame_callbacks_for_toplevel(surface_id);
2824 }
2825 let _ = self.display_handle.flush_clients();
2826 }
2827 CompositorCommand::SurfaceFocus { surface_id } => {
2828 self.set_keyboard_focus(surface_id);
2829 let _ = self.display_handle.flush_clients();
2830 }
2831 CompositorCommand::SurfaceClose { surface_id } => {
2832 if let Some(root_id) = self.toplevel_surface_ids.get(&surface_id)
2833 && let Some(surf) = self.surfaces.get(root_id)
2834 && let Some(ref tl) = surf.xdg_toplevel
2835 {
2836 tl.close();
2837 }
2838 let _ = self.display_handle.flush_clients();
2839 }
2840 CompositorCommand::ClipboardOffer { mime_type, data } => {
2841 self.external_clipboard = Some(ExternalClipboard { mime_type, data });
2842 if let Some(src) = self.selection_source.take() {
2848 src.cancelled();
2849 }
2850 self.offer_external_clipboard();
2851 }
2852 CompositorCommand::Capture {
2853 surface_id,
2854 scale_120,
2855 reply,
2856 } => {
2857 let cap_s120 = if scale_120 > 0 {
2860 scale_120
2861 } else {
2862 self.output_scale_120
2863 };
2864 let result = if let Some(root_id) = self.toplevel_surface_ids.get(&surface_id) {
2865 if let Some(ref mut vk) = self.vulkan_renderer {
2866 let captured = vk
2873 .render_tree_sized(
2874 root_id,
2875 &self.surfaces,
2876 &self.surface_meta,
2877 cap_s120,
2878 None,
2879 surface_id,
2880 )
2881 .into_iter()
2882 .next()
2883 .map(|(_sid, w, h, pixels)| {
2884 let rgba = pixels.to_rgba(w, h);
2885 (w, h, rgba)
2886 });
2887 let stray = vk.take_encoded_frames();
2891 debug_assert!(stray.is_empty());
2892 captured
2893 } else {
2894 None
2895 }
2896 } else {
2897 None
2898 };
2899 let _ = reply.send(result);
2900 }
2901 CompositorCommand::RequestFrame { surface_id } => {
2902 self.last_request_frame_ms.insert(surface_id, elapsed_ms());
2906 self.fire_frame_callbacks_for_toplevel(surface_id);
2907 }
2908 CompositorCommand::ReleaseKeys { keycodes } => {
2909 let time = elapsed_ms();
2910 let focused_wl = self
2911 .toplevel_surface_ids
2912 .get(&self.focused_surface_id)
2913 .and_then(|root_id| self.surfaces.get(root_id))
2914 .map(|s| s.wl_surface.clone());
2915 for keycode in &keycodes {
2916 let serial = self.next_serial();
2917 for kb in &self.keyboards {
2918 if let Some(ref wl) = focused_wl
2919 && same_client(kb, wl)
2920 {
2921 kb.key(serial, time, *keycode, wl_keyboard::KeyState::Released);
2922 }
2923 }
2924 }
2925 for keycode in &keycodes {
2927 self.update_and_send_modifiers(*keycode, false);
2928 }
2929 let _ = self.display_handle.flush_clients();
2930 }
2931 CompositorCommand::ClipboardListMimes { reply } => {
2932 let mimes = self.collect_clipboard_mime_types();
2933 let _ = reply.send(mimes);
2934 }
2935 CompositorCommand::ClipboardGet { mime_type, reply } => {
2936 let data = self.get_clipboard_content(&mime_type);
2937 let _ = reply.send(data);
2938 }
2939 CompositorCommand::SetExternalOutputBuffers {
2940 surface_id,
2941 target_w,
2942 target_h,
2943 buffers,
2944 } => {
2945 let installed = !buffers.is_empty();
2946 if let Some(ref mut vk) = self.vulkan_renderer {
2947 vk.set_external_output_buffers(surface_id, target_w, target_h, buffers);
2948 }
2949 if installed && self.toplevel_surface_ids.contains_key(&(surface_id as u16)) {
2960 self.pending_recomposite_toplevels
2961 .insert(surface_id as u16, false);
2962 }
2963 }
2964 CompositorCommand::RegisterDownscaleTarget {
2965 surface_id,
2966 target_w,
2967 target_h,
2968 } => {
2969 if let Some(ref mut vk) = self.vulkan_renderer {
2970 vk.register_downscale_target(surface_id, target_w, target_h);
2971 }
2972 if self.toplevel_surface_ids.contains_key(&(surface_id as u16)) {
2975 self.pending_recomposite_toplevels
2976 .insert(surface_id as u16, false);
2977 }
2978 }
2979 CompositorCommand::ClearDownscaleTarget {
2980 surface_id,
2981 target_w,
2982 target_h,
2983 } => {
2984 if let Some(ref mut vk) = self.vulkan_renderer {
2985 vk.clear_downscale_target(surface_id, target_w, target_h);
2986 }
2987 }
2988 CompositorCommand::SetRefreshRate { mhz } => {
2989 let diff = (mhz as i64 - self.output_refresh_mhz as i64).unsigned_abs();
2993 if diff > 2000 && mhz > 0 {
2994 self.output_refresh_mhz = mhz;
2995 let s120 = self.output_scale_120 as i32;
2996 let mode_w = self.output_width * s120 / 120;
2997 let mode_h = self.output_height * s120 / 120;
2998 for output in &self.outputs {
2999 output.mode(
3000 wl_output::Mode::Current | wl_output::Mode::Preferred,
3001 mode_w,
3002 mode_h,
3003 mhz as i32,
3004 );
3005 if output.version() >= 2 {
3006 output.done();
3007 }
3008 }
3009 let _ = self.display_handle.flush_clients();
3010 }
3011 }
3012 CompositorCommand::SetVulkanEncoder {
3013 surface_id,
3014 client_id,
3015 codec,
3016 qp,
3017 width,
3018 height,
3019 } => {
3020 let created = self.vulkan_renderer.as_mut().is_some_and(|vk| {
3021 vk.create_vulkan_encoder(surface_id, client_id, codec, qp, width, height)
3022 });
3023 if !created {
3024 let _ = self
3025 .event_tx
3026 .send(CompositorEvent::VulkanEncoderUnavailable {
3027 surface_id: surface_id as u16,
3028 client_id,
3029 });
3030 (self.event_notify)();
3031 }
3032 }
3033 CompositorCommand::SetVulkanEncoderQp {
3034 surface_id,
3035 client_id,
3036 qp,
3037 } => {
3038 if let Some(ref mut vk) = self.vulkan_renderer {
3039 vk.set_vulkan_encoder_qp(surface_id, client_id, qp);
3040 }
3041 }
3042 CompositorCommand::RequestVulkanKeyframe {
3043 surface_id,
3044 client_id,
3045 } => {
3046 if let Some(ref mut vk) = self.vulkan_renderer {
3047 vk.request_encoder_keyframe(surface_id, client_id);
3048 }
3049 if self.toplevel_surface_ids.contains_key(&(surface_id as u16)) {
3058 self.pending_recomposite_toplevels
3059 .entry(surface_id as u16)
3060 .or_insert(true);
3061 }
3062 }
3063 CompositorCommand::DestroyVulkanEncoder {
3064 surface_id,
3065 client_id,
3066 } => {
3067 if let Some(ref mut vk) = self.vulkan_renderer {
3068 vk.destroy_vulkan_encoder(surface_id, client_id);
3069 }
3070 }
3071 CompositorCommand::Shutdown => {
3072 self.shutdown.store(true, Ordering::Relaxed);
3073 self.loop_signal.stop();
3074 }
3075 }
3076 }
3077
3078 fn send_dmabuf_feedback(&self, fb: &ZwpLinuxDmabufFeedbackV1) {
3082 use std::os::unix::fs::MetadataExt;
3083
3084 let modifiers: &[(u32, u64)] = self
3086 .vulkan_renderer
3087 .as_ref()
3088 .map(|vk| vk.supported_dmabuf_modifiers.as_slice())
3089 .unwrap_or(&[]);
3090
3091 let entry_size = 16usize;
3093 let table_size = modifiers.len() * entry_size;
3094 let mut table_data = vec![0u8; table_size];
3095 for (i, &(fmt, modifier)) in modifiers.iter().enumerate() {
3096 let off = i * entry_size;
3097 table_data[off..off + 4].copy_from_slice(&fmt.to_ne_bytes());
3098 table_data[off + 8..off + 16].copy_from_slice(&modifier.to_ne_bytes());
3100 }
3101
3102 let name = c"dmabuf-feedback-table";
3104 let raw_fd = unsafe { libc::memfd_create(name.as_ptr(), libc::MFD_CLOEXEC) };
3105 if raw_fd < 0 {
3106 eprintln!("[compositor] memfd_create for dmabuf feedback failed");
3107 fb.done();
3108 return;
3109 }
3110 let table_fd = unsafe { OwnedFd::from_raw_fd(raw_fd) };
3111 if !table_data.is_empty() {
3112 use std::io::Write;
3113 let mut file = std::fs::File::from(table_fd.try_clone().unwrap());
3114 if file.write_all(&table_data).is_err() {
3115 eprintln!("[compositor] failed to write dmabuf feedback table");
3116 fb.done();
3117 return;
3118 }
3119 }
3120 fb.format_table(table_fd.as_fd(), table_size as u32);
3121
3122 let dev = std::fs::metadata(&self.gpu_device)
3124 .map(|m| m.rdev())
3125 .unwrap_or(0);
3126 let dev_bytes = dev.to_ne_bytes().to_vec();
3127 fb.main_device(dev_bytes.clone());
3128
3129 fb.tranche_target_device(dev_bytes);
3131
3132 let indices: Vec<u8> = (0..modifiers.len() as u16)
3134 .flat_map(|i| i.to_ne_bytes())
3135 .collect();
3136 fb.tranche_formats(indices);
3137
3138 fb.tranche_flags(
3139 wayland_protocols::wp::linux_dmabuf::zv1::server::zwp_linux_dmabuf_feedback_v1::TrancheFlags::empty(),
3140 );
3141 fb.tranche_done();
3142 fb.done();
3143 }
3144}
3145
3146impl Compositor {
3147 fn collect_clipboard_mime_types(&self) -> Vec<String> {
3149 if let Some(ref src) = self.selection_source {
3151 let data = src.data::<DataSourceData>().unwrap();
3152 return data.mime_types.lock().unwrap().clone();
3153 }
3154 if let Some(ref cb) = self.external_clipboard
3156 && !cb.mime_type.is_empty()
3157 {
3158 let mut mimes = vec![cb.mime_type.clone()];
3159 if cb.mime_type.starts_with("text/plain") {
3161 if cb.mime_type != "text/plain" {
3162 mimes.push("text/plain".to_string());
3163 }
3164 if cb.mime_type != "text/plain;charset=utf-8" {
3165 mimes.push("text/plain;charset=utf-8".to_string());
3166 }
3167 mimes.push("UTF8_STRING".to_string());
3168 }
3169 return mimes;
3170 }
3171 Vec::new()
3172 }
3173
3174 fn get_clipboard_content(&mut self, mime_type: &str) -> Option<Vec<u8>> {
3176 if let Some(ref cb) = self.external_clipboard
3178 && self.selection_source.is_none()
3179 {
3180 let matches = cb.mime_type == mime_type
3182 || (cb.mime_type.starts_with("text/plain")
3183 && (mime_type == "text/plain"
3184 || mime_type == "text/plain;charset=utf-8"
3185 || mime_type == "UTF8_STRING"));
3186 if matches {
3187 return Some(cb.data.clone());
3188 }
3189 return None;
3190 }
3191 if let Some(src) = self.selection_source.clone() {
3193 return self.read_data_source_sync(&src, mime_type);
3194 }
3195 None
3196 }
3197
3198 fn read_data_source_sync(&mut self, source: &WlDataSource, mime_type: &str) -> Option<Vec<u8>> {
3200 let mut fds = [0i32; 2];
3201 if unsafe { libc::pipe(fds.as_mut_ptr()) } != 0 {
3202 return None;
3203 }
3204 let read_fd = unsafe { OwnedFd::from_raw_fd(fds[0]) };
3205 let write_fd = unsafe { OwnedFd::from_raw_fd(fds[1]) };
3206 source.send(mime_type.to_string(), write_fd.as_fd());
3207 let _ = self.display_handle.flush_clients();
3208 drop(write_fd); unsafe {
3211 libc::fcntl(read_fd.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
3212 }
3213 std::thread::sleep(std::time::Duration::from_millis(5));
3214 let mut buf = Vec::new();
3215 let mut tmp = [0u8; 8192];
3216 loop {
3217 let n = unsafe {
3218 libc::read(
3219 read_fd.as_raw_fd(),
3220 tmp.as_mut_ptr() as *mut libc::c_void,
3221 tmp.len(),
3222 )
3223 };
3224 if n <= 0 {
3225 break;
3226 }
3227 buf.extend_from_slice(&tmp[..n as usize]);
3228 if buf.len() > 1024 * 1024 {
3229 break; }
3231 }
3232 if buf.is_empty() { None } else { Some(buf) }
3233 }
3234}
3235
3236fn monotonic_timespec() -> (i64, i64) {
3242 let mut ts = libc::timespec {
3243 tv_sec: 0,
3244 tv_nsec: 0,
3245 };
3246 unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts) };
3248 (ts.tv_sec, ts.tv_nsec)
3249}
3250
3251fn elapsed_ms() -> u32 {
3252 let (sec, nsec) = monotonic_timespec();
3257 (sec as u32)
3258 .wrapping_mul(1000)
3259 .wrapping_add(nsec as u32 / 1_000_000)
3260}
3261
3262fn same_client<R1: Resource, R2: Resource>(a: &R1, b: &R2) -> bool {
3264 match (a.client(), b.client()) {
3265 (Some(ca), Some(cb)) => ca.id() == cb.id(),
3266 _ => false,
3267 }
3268}
3269
3270fn yuv420_to_rgb(y: u8, u: u8, v: u8) -> [u8; 3] {
3271 let y = (y as i32 - 16).max(0);
3272 let u = u as i32 - 128;
3273 let v = v as i32 - 128;
3274 let r = ((298 * y + 409 * v + 128) >> 8).clamp(0, 255) as u8;
3275 let g = ((298 * y - 100 * u - 208 * v + 128) >> 8).clamp(0, 255) as u8;
3276 let b = ((298 * y + 516 * u + 128) >> 8).clamp(0, 255) as u8;
3277 [r, g, b]
3278}
3279
3280fn xdg_toplevel_states(states: &[xdg_toplevel::State]) -> Vec<u8> {
3282 let mut bytes = Vec::with_capacity(states.len() * 4);
3283 for state in states {
3284 bytes.extend_from_slice(&(*state as u32).to_ne_bytes());
3285 }
3286 bytes
3287}
3288
3289fn create_keymap_fd(keymap_data: &[u8]) -> Option<OwnedFd> {
3290 use std::io::Write;
3291 let name = c"blit-keymap";
3292 let raw_fd = unsafe { libc::memfd_create(name.as_ptr(), libc::MFD_CLOEXEC) };
3293 if raw_fd < 0 {
3294 return None;
3295 }
3296 let fd = unsafe { OwnedFd::from_raw_fd(raw_fd) };
3297 let mut file = std::fs::File::from(fd);
3298 file.write_all(keymap_data).ok()?;
3299 Some(file.into())
3300}
3301
3302impl GlobalDispatch<WlCompositor, ()> for Compositor {
3309 fn bind(
3310 _state: &mut Self,
3311 _handle: &DisplayHandle,
3312 _client: &Client,
3313 resource: New<WlCompositor>,
3314 _data: &(),
3315 data_init: &mut DataInit<'_, Self>,
3316 ) {
3317 data_init.init(resource, ());
3318 }
3319}
3320
3321impl Dispatch<WlCompositor, ()> for Compositor {
3322 fn request(
3323 state: &mut Self,
3324 _client: &Client,
3325 _resource: &WlCompositor,
3326 request: <WlCompositor as Resource>::Request,
3327 _data: &(),
3328 _dh: &DisplayHandle,
3329 data_init: &mut DataInit<'_, Self>,
3330 ) {
3331 use wayland_server::protocol::wl_compositor::Request;
3332 match request {
3333 Request::CreateSurface { id } => {
3334 let surface = data_init.init(id, ());
3335 let proto_id = surface.id();
3336 state.surfaces.insert(
3337 proto_id,
3338 Surface {
3339 surface_id: 0,
3340 wl_surface: surface,
3341 pending_buffer: None,
3342 pending_buffer_scale: 1,
3343 pending_damage: false,
3344 pending_frame_callbacks: Vec::new(),
3345 pending_presentation_feedbacks: Vec::new(),
3346 pending_opaque: false,
3347 pending_input_region: None,
3348 input_region: None,
3349 buffer_scale: 1,
3350 is_opaque: false,
3351 parent_surface_id: None,
3352 pending_subsurface_position: None,
3353 subsurface_position: (0, 0),
3354 children: Vec::new(),
3355 xdg_surface: None,
3356 xdg_toplevel: None,
3357 xdg_popup: None,
3358 xdg_geometry: None,
3359 title: String::new(),
3360 app_id: String::new(),
3361 pending_viewport_destination: None,
3362 viewport_destination: None,
3363 is_cursor: false,
3364 cursor_hotspot: (0, 0),
3365 },
3366 );
3367 }
3368 Request::CreateRegion { id } => {
3369 data_init.init(id, ());
3370 }
3371 _ => {}
3372 }
3373 }
3374}
3375
3376impl Dispatch<WlSurface, ()> for Compositor {
3379 fn request(
3380 state: &mut Self,
3381 _client: &Client,
3382 resource: &WlSurface,
3383 request: <WlSurface as Resource>::Request,
3384 _data: &(),
3385 _dh: &DisplayHandle,
3386 data_init: &mut DataInit<'_, Self>,
3387 ) {
3388 use wayland_server::protocol::wl_surface::Request;
3389 let sid = resource.id();
3390 match request {
3391 Request::Attach { buffer, x: _, y: _ } => {
3392 if let Some(surf) = state.surfaces.get_mut(&sid) {
3393 surf.pending_buffer = buffer;
3394 }
3395 }
3396 Request::Damage { .. } | Request::DamageBuffer { .. } => {
3397 if let Some(surf) = state.surfaces.get_mut(&sid) {
3398 surf.pending_damage = true;
3399 }
3400 }
3401 Request::Frame { callback } => {
3402 let cb = data_init.init(callback, ());
3403 if let Some(surf) = state.surfaces.get_mut(&sid) {
3404 surf.pending_frame_callbacks.push(cb);
3405 }
3406 }
3407 Request::SetBufferScale { scale } => {
3408 if let Some(surf) = state.surfaces.get_mut(&sid) {
3409 surf.pending_buffer_scale = scale;
3410 }
3411 }
3412 Request::SetOpaqueRegion { region: _ } => {
3413 if let Some(surf) = state.surfaces.get_mut(&sid) {
3414 surf.pending_opaque = true;
3415 }
3416 }
3417 Request::SetInputRegion { region } => {
3418 let ops = region.map(|r| {
3423 state
3424 .regions
3425 .get(&r.id())
3426 .map(|(_, ops)| ops.clone())
3427 .unwrap_or_default()
3428 });
3429 if let Some(surf) = state.surfaces.get_mut(&sid) {
3430 surf.pending_input_region = Some(ops);
3431 }
3432 }
3433 Request::Commit => {
3434 let is_cursor = state.surfaces.get(&sid).is_some_and(|s| s.is_cursor);
3435 if is_cursor {
3436 state.handle_cursor_commit(&sid);
3437 } else {
3438 state.handle_surface_commit(&sid);
3439 }
3440 }
3441 Request::SetBufferTransform { .. } => {}
3442 Request::Offset { .. } => {}
3443 Request::Destroy => {
3444 state.surface_meta.remove(&sid);
3445 state.cursor_rgba.remove(&sid);
3446 if let Some(ref mut vk) = state.vulkan_renderer {
3447 vk.remove_surface(&sid);
3448 }
3449 if let Some(held) = state.held_buffers.remove(&sid) {
3450 held.release();
3451 }
3452 state.forget_pointer_focus(&sid);
3453 if let Some(parent_id) = state
3454 .surfaces
3455 .get(&sid)
3456 .and_then(|s| s.parent_surface_id.clone())
3457 && let Some(parent) = state.surfaces.get_mut(&parent_id)
3458 {
3459 parent.children.retain(|c| *c != sid);
3460 }
3461 if let Some(surf) = state.surfaces.remove(&sid) {
3462 for fb in surf.pending_presentation_feedbacks {
3463 fb.discarded();
3464 }
3465 if surf.surface_id > 0 {
3466 state.toplevel_surface_ids.remove(&surf.surface_id);
3467 state.last_reported_size.remove(&surf.surface_id);
3468 state.surface_sizes.remove(&surf.surface_id);
3469 if let Some(ref mut vk) = state.vulkan_renderer {
3470 vk.destroy_external_outputs_for_surface(surf.surface_id as u32);
3471 }
3472 let _ = state.event_tx.send(CompositorEvent::SurfaceDestroyed {
3473 surface_id: surf.surface_id,
3474 });
3475 (state.event_notify)();
3476 }
3477 }
3478 }
3479 _ => {}
3480 }
3481 }
3482}
3483
3484impl Dispatch<WlCallback, ()> for Compositor {
3486 fn request(
3487 _: &mut Self,
3488 _: &Client,
3489 _: &WlCallback,
3490 _: <WlCallback as Resource>::Request,
3491 _: &(),
3492 _: &DisplayHandle,
3493 _: &mut DataInit<'_, Self>,
3494 ) {
3495 }
3496}
3497
3498impl GlobalDispatch<WpPresentation, ()> for Compositor {
3500 fn bind(
3501 _: &mut Self,
3502 _: &DisplayHandle,
3503 _: &Client,
3504 resource: New<WpPresentation>,
3505 _: &(),
3506 data_init: &mut DataInit<'_, Self>,
3507 ) {
3508 let pres = data_init.init(resource, ());
3509 pres.clock_id(libc::CLOCK_MONOTONIC as u32);
3511 }
3512}
3513
3514impl Dispatch<WpPresentation, ()> for Compositor {
3515 fn request(
3516 state: &mut Self,
3517 _: &Client,
3518 _: &WpPresentation,
3519 request: <WpPresentation as Resource>::Request,
3520 _: &(),
3521 _: &DisplayHandle,
3522 data_init: &mut DataInit<'_, Self>,
3523 ) {
3524 use wp_presentation::Request;
3525 match request {
3526 Request::Feedback { surface, callback } => {
3527 let fb = data_init.init(callback, ());
3528 let sid = surface.id();
3529 if let Some(surf) = state.surfaces.get_mut(&sid) {
3530 surf.pending_presentation_feedbacks.push(fb);
3531 }
3532 }
3533 Request::Destroy => {}
3534 _ => {}
3535 }
3536 }
3537}
3538
3539impl Dispatch<WpPresentationFeedback, ()> for Compositor {
3541 fn request(
3542 _: &mut Self,
3543 _: &Client,
3544 _: &WpPresentationFeedback,
3545 _: <WpPresentationFeedback as Resource>::Request,
3546 _: &(),
3547 _: &DisplayHandle,
3548 _: &mut DataInit<'_, Self>,
3549 ) {
3550 }
3551}
3552
3553impl Dispatch<WlRegion, ()> for Compositor {
3555 fn request(
3556 state: &mut Self,
3557 _: &Client,
3558 resource: &WlRegion,
3559 request: <WlRegion as Resource>::Request,
3560 _: &(),
3561 _: &DisplayHandle,
3562 _: &mut DataInit<'_, Self>,
3563 ) {
3564 use wayland_server::protocol::wl_region::Request;
3565 let rid = resource.id();
3566 match request {
3567 Request::Add {
3568 x,
3569 y,
3570 width,
3571 height,
3572 } => {
3573 state
3574 .regions
3575 .entry(rid)
3576 .or_insert_with(|| (resource.clone(), Vec::new()))
3577 .1
3578 .push(RegionOp {
3579 add: true,
3580 x,
3581 y,
3582 w: width,
3583 h: height,
3584 });
3585 }
3586 Request::Subtract {
3587 x,
3588 y,
3589 width,
3590 height,
3591 } => {
3592 state
3593 .regions
3594 .entry(rid)
3595 .or_insert_with(|| (resource.clone(), Vec::new()))
3596 .1
3597 .push(RegionOp {
3598 add: false,
3599 x,
3600 y,
3601 w: width,
3602 h: height,
3603 });
3604 }
3605 Request::Destroy => {
3606 state.regions.remove(&rid);
3610 }
3611 _ => {}
3612 }
3613 }
3614}
3615
3616impl GlobalDispatch<WlSubcompositor, ()> for Compositor {
3618 fn bind(
3619 _: &mut Self,
3620 _: &DisplayHandle,
3621 _: &Client,
3622 resource: New<WlSubcompositor>,
3623 _: &(),
3624 data_init: &mut DataInit<'_, Self>,
3625 ) {
3626 data_init.init(resource, ());
3627 }
3628}
3629
3630impl Dispatch<WlSubcompositor, ()> for Compositor {
3631 fn request(
3632 state: &mut Self,
3633 _: &Client,
3634 _: &WlSubcompositor,
3635 request: <WlSubcompositor as Resource>::Request,
3636 _: &(),
3637 _: &DisplayHandle,
3638 data_init: &mut DataInit<'_, Self>,
3639 ) {
3640 use wayland_server::protocol::wl_subcompositor::Request;
3641 match request {
3642 Request::GetSubsurface {
3643 id,
3644 surface,
3645 parent,
3646 } => {
3647 let child_id = surface.id();
3648 let parent_id = parent.id();
3649 data_init.init(
3650 id,
3651 SubsurfaceData {
3652 wl_surface_id: child_id.clone(),
3653 parent_surface_id: parent_id.clone(),
3654 },
3655 );
3656 if let Some(surf) = state.surfaces.get_mut(&child_id) {
3657 surf.parent_surface_id = Some(parent_id.clone());
3658 }
3659 if let Some(parent_surf) = state.surfaces.get_mut(&parent_id)
3660 && !parent_surf.children.contains(&child_id)
3661 {
3662 parent_surf.children.push(child_id);
3663 }
3664 }
3665 Request::Destroy => {}
3666 _ => {}
3667 }
3668 }
3669}
3670
3671impl Dispatch<WlSubsurface, SubsurfaceData> for Compositor {
3673 fn request(
3674 state: &mut Self,
3675 _: &Client,
3676 _: &WlSubsurface,
3677 request: <WlSubsurface as Resource>::Request,
3678 data: &SubsurfaceData,
3679 _: &DisplayHandle,
3680 _: &mut DataInit<'_, Self>,
3681 ) {
3682 use wayland_server::protocol::wl_subsurface::Request;
3683 match request {
3684 Request::SetPosition { x, y } => {
3685 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3686 surf.pending_subsurface_position = Some((x, y));
3687 }
3688 }
3689 Request::PlaceAbove { sibling } => {
3690 let sibling_id = sibling.id();
3691 if let Some(parent) = state.surfaces.get_mut(&data.parent_surface_id) {
3692 let child_id = &data.wl_surface_id;
3693 parent.children.retain(|c| c != child_id);
3694 let pos = parent
3695 .children
3696 .iter()
3697 .position(|c| *c == sibling_id)
3698 .map(|p| p + 1)
3699 .unwrap_or(parent.children.len());
3700 parent.children.insert(pos, child_id.clone());
3701 }
3702 }
3703 Request::PlaceBelow { sibling } => {
3704 let sibling_id = sibling.id();
3705 if let Some(parent) = state.surfaces.get_mut(&data.parent_surface_id) {
3706 let child_id = &data.wl_surface_id;
3707 parent.children.retain(|c| c != child_id);
3708 let pos = parent
3709 .children
3710 .iter()
3711 .position(|c| *c == sibling_id)
3712 .unwrap_or(0);
3713 parent.children.insert(pos, child_id.clone());
3714 }
3715 }
3716 Request::SetSync | Request::SetDesync => {}
3717 Request::Destroy => {
3718 let child_id = &data.wl_surface_id;
3719 if let Some(parent) = state.surfaces.get_mut(&data.parent_surface_id) {
3720 parent.children.retain(|c| c != child_id);
3721 }
3722 if let Some(surf) = state.surfaces.get_mut(child_id) {
3723 surf.parent_surface_id = None;
3724 }
3725 }
3726 _ => {}
3727 }
3728 }
3729}
3730
3731impl GlobalDispatch<XdgWmBase, ()> for Compositor {
3733 fn bind(
3734 _: &mut Self,
3735 _: &DisplayHandle,
3736 _: &Client,
3737 resource: New<XdgWmBase>,
3738 _: &(),
3739 data_init: &mut DataInit<'_, Self>,
3740 ) {
3741 data_init.init(resource, ());
3742 }
3743}
3744
3745impl Dispatch<XdgWmBase, ()> for Compositor {
3746 fn request(
3747 state: &mut Self,
3748 _: &Client,
3749 _: &XdgWmBase,
3750 request: <XdgWmBase as Resource>::Request,
3751 _: &(),
3752 _: &DisplayHandle,
3753 data_init: &mut DataInit<'_, Self>,
3754 ) {
3755 use xdg_wm_base::Request;
3756 match request {
3757 Request::GetXdgSurface { id, surface } => {
3758 let wl_surface_id = surface.id();
3759 let xdg_surface = data_init.init(
3760 id,
3761 XdgSurfaceData {
3762 wl_surface_id: wl_surface_id.clone(),
3763 },
3764 );
3765 if let Some(surf) = state.surfaces.get_mut(&wl_surface_id) {
3766 surf.xdg_surface = Some(xdg_surface);
3767 }
3768 }
3769 Request::CreatePositioner { id } => {
3770 let positioner = data_init.init(id, ());
3771 let pos_id = positioner.id();
3772 state.positioners.insert(
3773 pos_id,
3774 PositionerState {
3775 resource: positioner,
3776 geometry: PositionerGeometry {
3777 size: (0, 0),
3778 anchor_rect: (0, 0, 0, 0),
3779 anchor: 0,
3780 gravity: 0,
3781 constraint_adjustment: 0,
3782 offset: (0, 0),
3783 },
3784 },
3785 );
3786 }
3787 Request::Pong { .. } => {}
3788 Request::Destroy => {}
3789 _ => {}
3790 }
3791 }
3792}
3793
3794impl Dispatch<XdgSurface, XdgSurfaceData> for Compositor {
3796 fn request(
3797 state: &mut Self,
3798 _: &Client,
3799 resource: &XdgSurface,
3800 request: <XdgSurface as Resource>::Request,
3801 data: &XdgSurfaceData,
3802 _: &DisplayHandle,
3803 data_init: &mut DataInit<'_, Self>,
3804 ) {
3805 use xdg_surface::Request;
3806 match request {
3807 Request::GetToplevel { id } => {
3808 let toplevel = data_init.init(
3809 id,
3810 XdgToplevelData {
3811 wl_surface_id: data.wl_surface_id.clone(),
3812 },
3813 );
3814 let surface_id = state.allocate_surface_id();
3815 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3816 surf.xdg_toplevel = Some(toplevel.clone());
3817 surf.surface_id = surface_id;
3818 }
3819 state
3820 .toplevel_surface_ids
3821 .insert(surface_id, data.wl_surface_id.clone());
3822
3823 let (cw, ch) = state
3828 .surface_sizes
3829 .get(&surface_id)
3830 .copied()
3831 .unwrap_or((state.output_width, state.output_height));
3832 let states = xdg_toplevel_states(&[
3833 xdg_toplevel::State::Activated,
3834 xdg_toplevel::State::Maximized,
3835 ]);
3836 toplevel.configure(cw, ch, states);
3837 let serial = state.next_serial();
3838 resource.configure(serial);
3839
3840 state.set_keyboard_focus(surface_id);
3843 if let Some(surf) = state.surfaces.get(&data.wl_surface_id) {
3846 for output in &state.outputs {
3847 if same_client(output, &surf.wl_surface) {
3848 surf.wl_surface.enter(output);
3849 }
3850 }
3851 }
3852 let _ = state.display_handle.flush_clients();
3853
3854 let _ = state.event_tx.send(CompositorEvent::SurfaceCreated {
3855 surface_id,
3856 title: String::new(),
3857 app_id: String::new(),
3858 parent_id: 0,
3859 width: 0,
3860 height: 0,
3861 });
3862 (state.event_notify)();
3863 if state.verbose {
3864 eprintln!("[compositor] new_toplevel sid={surface_id}");
3865 }
3866 }
3867 Request::GetPopup {
3868 id,
3869 parent,
3870 positioner,
3871 } => {
3872 let popup = data_init.init(
3873 id,
3874 XdgPopupData {
3875 wl_surface_id: data.wl_surface_id.clone(),
3876 },
3877 );
3878
3879 let parent_wl_id: Option<ObjectId> = parent
3882 .as_ref()
3883 .and_then(|p| p.data::<XdgSurfaceData>())
3884 .map(|d| d.wl_surface_id.clone());
3885
3886 let parent_geom_offset = parent_wl_id
3891 .as_ref()
3892 .and_then(|pid| state.surfaces.get(pid))
3893 .and_then(|s| s.xdg_geometry)
3894 .map(|(gx, gy, _, _)| (gx, gy))
3895 .unwrap_or((0, 0));
3896
3897 let parent_abs = parent_wl_id
3902 .as_ref()
3903 .map(|pid| {
3904 let abs = state.surface_absolute_position(pid);
3905 (abs.0 + parent_geom_offset.0, abs.1 + parent_geom_offset.1)
3906 })
3907 .unwrap_or((0, 0));
3908 let (_, toplevel_root) = parent_wl_id
3911 .as_ref()
3912 .map(|pid| state.find_toplevel_root(pid))
3913 .unwrap_or_else(|| {
3914 (data.wl_surface_id.clone(), None)
3916 });
3917 let bounds = toplevel_root
3918 .and_then(|_| {
3919 let root_wl_id = parent_wl_id.as_ref().map(|pid| {
3920 let (rid, _) = state.find_toplevel_root(pid);
3921 rid
3922 })?;
3923 let surf = state.surfaces.get(&root_wl_id)?;
3924 if let Some((gx, gy, gw, gh)) = surf.xdg_geometry
3925 && gw > 0
3926 && gh > 0
3927 {
3928 return Some((gx, gy, gw, gh));
3929 }
3930
3931 let sm = state.surface_meta.get(&root_wl_id)?;
3934 let s = (sm.scale).max(1);
3935 let (lw, lh) = surf
3936 .viewport_destination
3937 .filter(|&(dw, dh)| dw > 0 && dh > 0)
3938 .unwrap_or((sm.width as i32 / s, sm.height as i32 / s));
3939 Some((0, 0, lw, lh))
3940 })
3941 .unwrap_or((0, 0, state.output_width, state.output_height));
3942
3943 eprintln!(
3944 "[popup] parent_abs={parent_abs:?} bounds={bounds:?} parent_wl={parent_wl_id:?} geom_off={parent_geom_offset:?}"
3945 );
3946 let pos_id = positioner.id();
3948 let (px, py, pw, ph) = state
3949 .positioners
3950 .get(&pos_id)
3951 .map(|p| p.geometry.compute_position(parent_abs, bounds))
3952 .unwrap_or((0, 0, 200, 200));
3953 eprintln!("[popup] result=({px},{py},{pw},{ph})");
3954
3955 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3956 surf.xdg_popup = Some(popup.clone());
3957 surf.parent_surface_id = parent_wl_id.clone();
3958 surf.subsurface_position =
3963 (parent_geom_offset.0 + px, parent_geom_offset.1 + py);
3964 }
3965 if let Some(ref parent_id) = parent_wl_id
3966 && let Some(parent_surf) = state.surfaces.get_mut(parent_id)
3967 && !parent_surf.children.contains(&data.wl_surface_id)
3968 {
3969 parent_surf.children.push(data.wl_surface_id.clone());
3970 }
3971
3972 popup.configure(px, py, pw, ph);
3973 let serial = state.next_serial();
3974 resource.configure(serial);
3975 let _ = state.display_handle.flush_clients();
3976 }
3977 Request::SetWindowGeometry {
3978 x,
3979 y,
3980 width,
3981 height,
3982 } => {
3983 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3984 if surf.xdg_popup.is_some() {
3992 let (old_gx, old_gy) = surf
3993 .xdg_geometry
3994 .map(|(gx, gy, _, _)| (gx, gy))
3995 .unwrap_or((0, 0));
3996 surf.subsurface_position.0 += old_gx - x;
3997 surf.subsurface_position.1 += old_gy - y;
3998 }
3999 surf.xdg_geometry = Some((x, y, width, height));
4000 }
4001 }
4002 Request::AckConfigure { .. } => {}
4003 Request::Destroy => {}
4004 _ => {}
4005 }
4006 }
4007}
4008
4009impl Dispatch<XdgToplevel, XdgToplevelData> for Compositor {
4011 fn request(
4012 state: &mut Self,
4013 _: &Client,
4014 _: &XdgToplevel,
4015 request: <XdgToplevel as Resource>::Request,
4016 data: &XdgToplevelData,
4017 _: &DisplayHandle,
4018 _: &mut DataInit<'_, Self>,
4019 ) {
4020 use xdg_toplevel::Request;
4021 match request {
4022 Request::SetTitle { title } => {
4023 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id)
4024 && surf.title != title
4025 {
4026 surf.title = title.clone();
4027 if surf.surface_id > 0 {
4028 let _ = state.event_tx.send(CompositorEvent::SurfaceTitle {
4029 surface_id: surf.surface_id,
4030 title,
4031 });
4032 (state.event_notify)();
4033 }
4034 }
4035 }
4036 Request::SetAppId { app_id } => {
4037 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id)
4038 && surf.app_id != app_id
4039 {
4040 surf.app_id = app_id.clone();
4041 if surf.surface_id > 0 {
4042 let _ = state.event_tx.send(CompositorEvent::SurfaceAppId {
4043 surface_id: surf.surface_id,
4044 app_id,
4045 });
4046 (state.event_notify)();
4047 }
4048 }
4049 }
4050 Request::Destroy => {
4051 let wl_surface_id = &data.wl_surface_id;
4052 state.surface_meta.remove(wl_surface_id);
4053 state.cursor_rgba.remove(wl_surface_id);
4054 if let Some(ref mut vk) = state.vulkan_renderer {
4055 vk.remove_surface(wl_surface_id);
4056 }
4057 if let Some(held) = state.held_buffers.remove(wl_surface_id) {
4058 held.release();
4059 }
4060 if let Some(surf) = state.surfaces.get_mut(wl_surface_id) {
4061 let sid = surf.surface_id;
4062 surf.xdg_toplevel = None;
4063 if sid > 0 {
4064 state.toplevel_surface_ids.remove(&sid);
4065 state.last_reported_size.remove(&sid);
4066 state.surface_sizes.remove(&sid);
4067 if let Some(ref mut vk) = state.vulkan_renderer {
4068 vk.destroy_external_outputs_for_surface(sid as u32);
4069 }
4070 let _ = state
4071 .event_tx
4072 .send(CompositorEvent::SurfaceDestroyed { surface_id: sid });
4073 (state.event_notify)();
4074 surf.surface_id = 0;
4075 }
4076 }
4077 }
4078 _ => {}
4079 }
4080 }
4081}
4082
4083impl Dispatch<XdgPopup, XdgPopupData> for Compositor {
4085 fn request(
4086 state: &mut Self,
4087 _: &Client,
4088 _: &XdgPopup,
4089 request: <XdgPopup as Resource>::Request,
4090 data: &XdgPopupData,
4091 _: &DisplayHandle,
4092 _: &mut DataInit<'_, Self>,
4093 ) {
4094 use xdg_popup::Request;
4095 match request {
4096 Request::Grab { seat: _, serial: _ } => {
4097 state
4100 .popup_grab_stack
4101 .retain(|id| *id != data.wl_surface_id);
4102 state.popup_grab_stack.push(data.wl_surface_id.clone());
4103 state.focus_popup(&data.wl_surface_id);
4107 }
4108 Request::Reposition { positioner, token } => {
4109 let pos_id = positioner.id();
4111 if let Some(surf) = state.surfaces.get(&data.wl_surface_id)
4112 && let Some(parent_id) = surf.parent_surface_id.clone()
4113 {
4114 let parent_geom_offset = state
4115 .surfaces
4116 .get(&parent_id)
4117 .and_then(|s| s.xdg_geometry)
4118 .map(|(gx, gy, _, _)| (gx, gy))
4119 .unwrap_or((0, 0));
4120 let parent_abs = {
4121 let abs = state.surface_absolute_position(&parent_id);
4122 (abs.0 + parent_geom_offset.0, abs.1 + parent_geom_offset.1)
4123 };
4124 let (root_id, toplevel_root) = state.find_toplevel_root(&parent_id);
4125 let bounds = toplevel_root
4126 .and_then(|_| {
4127 let surf = state.surfaces.get(&root_id)?;
4128 if let Some((gx, gy, gw, gh)) = surf.xdg_geometry
4129 && gw > 0
4130 && gh > 0
4131 {
4132 return Some((gx, gy, gw, gh));
4133 }
4134 let sm = state.surface_meta.get(&root_id)?;
4135 let s = (sm.scale).max(1);
4136 let (lw, lh) = surf
4137 .viewport_destination
4138 .filter(|&(dw, dh)| dw > 0 && dh > 0)
4139 .unwrap_or((sm.width as i32 / s, sm.height as i32 / s));
4140 Some((0, 0, lw, lh))
4141 })
4142 .unwrap_or((0, 0, state.output_width, state.output_height));
4143 let (px, py, pw, ph) = state
4144 .positioners
4145 .get(&pos_id)
4146 .map(|p| p.geometry.compute_position(parent_abs, bounds))
4147 .unwrap_or((0, 0, 200, 200));
4148 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
4149 let old_gx = surf.xdg_geometry.map(|(gx, _, _, _)| gx).unwrap_or(0);
4152 let old_gy = surf.xdg_geometry.map(|(_, gy, _, _)| gy).unwrap_or(0);
4153 surf.subsurface_position = (
4154 parent_geom_offset.0 + px - old_gx,
4155 parent_geom_offset.1 + py - old_gy,
4156 );
4157 if let Some(ref popup) = surf.xdg_popup {
4158 popup.configure(px, py, pw, ph);
4159 popup.repositioned(token);
4160 }
4161 if let Some(ref xs) = surf.xdg_surface {
4162 let serial = state.serial.wrapping_add(1);
4163 state.serial = serial;
4164 xs.configure(serial);
4165 }
4166 }
4167 }
4168 }
4169 Request::Destroy => {
4170 state
4172 .popup_grab_stack
4173 .retain(|id| *id != data.wl_surface_id);
4174 state.unfocus_popup(&data.wl_surface_id);
4180 if let Some(parent_id) = state
4182 .surfaces
4183 .get(&data.wl_surface_id)
4184 .and_then(|s| s.parent_surface_id.clone())
4185 && let Some(parent) = state.surfaces.get_mut(&parent_id)
4186 {
4187 parent.children.retain(|c| *c != data.wl_surface_id);
4188 }
4189 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
4190 surf.xdg_popup = None;
4191 surf.parent_surface_id = None;
4192 }
4193 }
4194 _ => {}
4195 }
4196 }
4197}
4198
4199use wayland_protocols::xdg::shell::server::xdg_positioner;
4201impl Dispatch<XdgPositioner, ()> for Compositor {
4202 fn request(
4203 state: &mut Self,
4204 _: &Client,
4205 resource: &XdgPositioner,
4206 request: <XdgPositioner as Resource>::Request,
4207 _: &(),
4208 _: &DisplayHandle,
4209 _: &mut DataInit<'_, Self>,
4210 ) {
4211 use xdg_positioner::Request;
4212 let pos_id = resource.id();
4213 let Some(pos) = state.positioners.get_mut(&pos_id) else {
4214 return;
4215 };
4216 match request {
4217 Request::SetSize { width, height } => {
4218 pos.geometry.size = (width, height);
4219 }
4220 Request::SetAnchorRect {
4221 x,
4222 y,
4223 width,
4224 height,
4225 } => {
4226 pos.geometry.anchor_rect = (x, y, width, height);
4227 }
4228 Request::SetAnchor {
4229 anchor: wayland_server::WEnum::Value(v),
4230 } => {
4231 pos.geometry.anchor = v as u32;
4232 }
4233 Request::SetGravity {
4234 gravity: wayland_server::WEnum::Value(v),
4235 } => {
4236 pos.geometry.gravity = v as u32;
4237 }
4238 Request::SetOffset { x, y } => {
4239 pos.geometry.offset = (x, y);
4240 }
4241 Request::SetConstraintAdjustment {
4242 constraint_adjustment,
4243 } => {
4244 pos.geometry.constraint_adjustment = constraint_adjustment.into();
4245 }
4246 Request::Destroy => {
4247 state.positioners.remove(&pos_id);
4248 }
4249 _ => {}
4250 }
4251 }
4252}
4253
4254impl GlobalDispatch<ZxdgDecorationManagerV1, ()> for Compositor {
4256 fn bind(
4257 _: &mut Self,
4258 _: &DisplayHandle,
4259 _: &Client,
4260 resource: New<ZxdgDecorationManagerV1>,
4261 _: &(),
4262 data_init: &mut DataInit<'_, Self>,
4263 ) {
4264 data_init.init(resource, ());
4265 }
4266}
4267
4268impl Dispatch<ZxdgDecorationManagerV1, ()> for Compositor {
4269 fn request(
4270 _: &mut Self,
4271 _: &Client,
4272 _: &ZxdgDecorationManagerV1,
4273 request: <ZxdgDecorationManagerV1 as Resource>::Request,
4274 _: &(),
4275 _: &DisplayHandle,
4276 data_init: &mut DataInit<'_, Self>,
4277 ) {
4278 use zxdg_decoration_manager_v1::Request;
4279 match request {
4280 Request::GetToplevelDecoration { id, toplevel: _ } => {
4281 let decoration = data_init.init(id, ());
4282 decoration.configure(zxdg_toplevel_decoration_v1::Mode::ServerSide);
4284 }
4285 Request::Destroy => {}
4286 _ => {}
4287 }
4288 }
4289}
4290
4291impl Dispatch<ZxdgToplevelDecorationV1, ()> for Compositor {
4292 fn request(
4293 _: &mut Self,
4294 _: &Client,
4295 resource: &ZxdgToplevelDecorationV1,
4296 request: <ZxdgToplevelDecorationV1 as Resource>::Request,
4297 _: &(),
4298 _: &DisplayHandle,
4299 _: &mut DataInit<'_, Self>,
4300 ) {
4301 use zxdg_toplevel_decoration_v1::Request;
4302 match request {
4303 Request::SetMode { .. } | Request::UnsetMode => {
4304 resource.configure(zxdg_toplevel_decoration_v1::Mode::ServerSide);
4305 }
4306 Request::Destroy => {}
4307 _ => {}
4308 }
4309 }
4310}
4311
4312impl GlobalDispatch<WlShm, ()> for Compositor {
4314 fn bind(
4315 _: &mut Self,
4316 _: &DisplayHandle,
4317 _: &Client,
4318 resource: New<WlShm>,
4319 _: &(),
4320 data_init: &mut DataInit<'_, Self>,
4321 ) {
4322 let shm = data_init.init(resource, ());
4323 shm.format(wl_shm::Format::Argb8888);
4324 shm.format(wl_shm::Format::Xrgb8888);
4325 shm.format(wl_shm::Format::Abgr8888);
4326 shm.format(wl_shm::Format::Xbgr8888);
4327 }
4328}
4329
4330impl Dispatch<WlShm, ()> for Compositor {
4331 fn request(
4332 state: &mut Self,
4333 _: &Client,
4334 _: &WlShm,
4335 request: <WlShm as Resource>::Request,
4336 _: &(),
4337 _: &DisplayHandle,
4338 data_init: &mut DataInit<'_, Self>,
4339 ) {
4340 use wayland_server::protocol::wl_shm::Request;
4341 if let Request::CreatePool { id, fd, size } = request {
4342 let pool = data_init.init(id, ());
4343 let pool_id = pool.id();
4344 state
4345 .shm_pools
4346 .insert(pool_id, Arc::new(ShmPool::new(pool, fd, size)));
4347 }
4348 }
4349}
4350
4351impl Dispatch<WlShmPool, ()> for Compositor {
4353 fn request(
4354 state: &mut Self,
4355 _: &Client,
4356 resource: &WlShmPool,
4357 request: <WlShmPool as Resource>::Request,
4358 _: &(),
4359 _: &DisplayHandle,
4360 data_init: &mut DataInit<'_, Self>,
4361 ) {
4362 use wayland_server::protocol::wl_shm_pool::Request;
4363 let pool_id = resource.id();
4364 match request {
4365 Request::CreateBuffer {
4366 id,
4367 offset,
4368 width,
4369 height,
4370 stride,
4371 format,
4372 } => {
4373 let fmt = match format {
4375 wayland_server::WEnum::Value(f) => f,
4376 _ => wl_shm::Format::Argb8888, };
4378 let Some(pool) = state.shm_pools.get(&pool_id).cloned() else {
4379 return;
4380 };
4381 data_init.init(
4382 id,
4383 ShmBufferData {
4384 pool,
4385 offset,
4386 width,
4387 height,
4388 stride,
4389 format: fmt,
4390 },
4391 );
4392 }
4393 Request::Resize { size } => {
4394 if let Some(pool) = state.shm_pools.get(&pool_id) {
4395 pool.resize(size);
4396 }
4397 }
4398 Request::Destroy => {
4399 state.shm_pools.remove(&pool_id);
4402 }
4403 _ => {}
4404 }
4405 }
4406}
4407
4408impl Dispatch<WlBuffer, ShmBufferData> for Compositor {
4410 fn request(
4411 _: &mut Self,
4412 _: &Client,
4413 _: &WlBuffer,
4414 _: <WlBuffer as Resource>::Request,
4415 _: &ShmBufferData,
4416 _: &DisplayHandle,
4417 _: &mut DataInit<'_, Self>,
4418 ) {
4419 }
4420}
4421
4422impl Dispatch<WlBuffer, DmaBufBufferData> for Compositor {
4424 fn request(
4425 _: &mut Self,
4426 _: &Client,
4427 _: &WlBuffer,
4428 _: <WlBuffer as Resource>::Request,
4429 _: &DmaBufBufferData,
4430 _: &DisplayHandle,
4431 _: &mut DataInit<'_, Self>,
4432 ) {
4433 }
4434}
4435
4436impl GlobalDispatch<WlOutput, ()> for Compositor {
4438 fn bind(
4439 state: &mut Self,
4440 _: &DisplayHandle,
4441 _: &Client,
4442 resource: New<WlOutput>,
4443 _: &(),
4444 data_init: &mut DataInit<'_, Self>,
4445 ) {
4446 let output = data_init.init(resource, ());
4447 output.geometry(
4448 0,
4449 0,
4450 0,
4451 0,
4452 wl_output::Subpixel::Unknown,
4453 "Virtual".to_string(),
4454 "Headless".to_string(),
4455 wl_output::Transform::Normal,
4456 );
4457 let s120 = state.output_scale_120 as i32;
4458 let mode_w = state.output_width * s120 / 120;
4459 let mode_h = state.output_height * s120 / 120;
4460 output.mode(
4461 wl_output::Mode::Current | wl_output::Mode::Preferred,
4462 mode_w,
4463 mode_h,
4464 state.output_refresh_mhz as i32,
4465 );
4466 if output.version() >= 2 {
4467 output.scale(((state.output_scale_120 as i32) + 119) / 120);
4468 }
4469 if output.version() >= 2 {
4470 output.done();
4471 }
4472 state.outputs.push(output);
4473 }
4474}
4475
4476impl Dispatch<WlOutput, ()> for Compositor {
4477 fn request(
4478 state: &mut Self,
4479 _: &Client,
4480 resource: &WlOutput,
4481 request: <WlOutput as Resource>::Request,
4482 _: &(),
4483 _: &DisplayHandle,
4484 _: &mut DataInit<'_, Self>,
4485 ) {
4486 use wayland_server::protocol::wl_output::Request;
4487 if let Request::Release = request {
4488 state.outputs.retain(|o| o.id() != resource.id());
4489 }
4490 }
4491}
4492
4493impl GlobalDispatch<WlSeat, ()> for Compositor {
4495 fn bind(
4496 _: &mut Self,
4497 _: &DisplayHandle,
4498 _: &Client,
4499 resource: New<WlSeat>,
4500 _: &(),
4501 data_init: &mut DataInit<'_, Self>,
4502 ) {
4503 let seat = data_init.init(resource, ());
4504 seat.capabilities(wl_seat::Capability::Keyboard | wl_seat::Capability::Pointer);
4505 if seat.version() >= 2 {
4506 seat.name("headless".to_string());
4507 }
4508 }
4509}
4510
4511impl Dispatch<WlSeat, ()> for Compositor {
4512 fn request(
4513 state: &mut Self,
4514 _: &Client,
4515 _: &WlSeat,
4516 request: <WlSeat as Resource>::Request,
4517 _: &(),
4518 _: &DisplayHandle,
4519 data_init: &mut DataInit<'_, Self>,
4520 ) {
4521 use wayland_server::protocol::wl_seat::Request;
4522 match request {
4523 Request::GetKeyboard { id } => {
4524 let kb = data_init.init(id, ());
4525 if let Some(fd) = create_keymap_fd(&state.keyboard_keymap_data) {
4526 kb.keymap(
4527 wl_keyboard::KeymapFormat::XkbV1,
4528 fd.as_fd(),
4529 state.keyboard_keymap_data.len() as u32,
4530 );
4531 }
4532 if kb.version() >= 4 {
4533 kb.repeat_info(25, 200);
4534 }
4535 state.keyboards.push(kb);
4536 }
4537 Request::GetPointer { id } => {
4538 let ptr = data_init.init(id, ());
4539 let entered = state.pointer_entered_id.as_ref().and_then(|eid| {
4546 state
4547 .surfaces
4548 .values()
4549 .find(|s| s.wl_surface.id() == *eid)
4550 .map(|s| s.wl_surface.clone())
4551 });
4552 if let Some(wl) = entered
4553 && same_client(&ptr, &wl)
4554 {
4555 let serial = state.next_serial();
4556 let (lx, ly) = state.pointer_entered_local;
4557 ptr.enter(serial, &wl, lx, ly);
4558 ptr.frame();
4561 let _ = state.display_handle.flush_clients();
4562 }
4563 state.pointers.push(ptr);
4564 }
4565 Request::GetTouch { id } => {
4566 data_init.init(id, ());
4567 }
4568 Request::Release => {}
4569 _ => {}
4570 }
4571 }
4572}
4573
4574impl Dispatch<WlKeyboard, ()> for Compositor {
4576 fn request(
4577 state: &mut Self,
4578 _: &Client,
4579 resource: &WlKeyboard,
4580 request: <WlKeyboard as Resource>::Request,
4581 _: &(),
4582 _: &DisplayHandle,
4583 _: &mut DataInit<'_, Self>,
4584 ) {
4585 if let wl_keyboard::Request::Release = request {
4586 state.keyboards.retain(|k| k.id() != resource.id());
4587 }
4588 }
4589}
4590
4591impl Dispatch<WlPointer, ()> for Compositor {
4593 fn request(
4594 state: &mut Self,
4595 _: &Client,
4596 resource: &WlPointer,
4597 request: <WlPointer as Resource>::Request,
4598 _: &(),
4599 _: &DisplayHandle,
4600 _: &mut DataInit<'_, Self>,
4601 ) {
4602 use wl_pointer::Request;
4603 match request {
4604 Request::SetCursor {
4605 serial: _,
4606 surface,
4607 hotspot_x,
4608 hotspot_y,
4609 } => {
4610 if let Some(surface) = surface {
4611 let sid = surface.id();
4612 if let Some(surf) = state.surfaces.get_mut(&sid) {
4613 surf.is_cursor = true;
4614 surf.cursor_hotspot = (hotspot_x, hotspot_y);
4615 }
4616 } else {
4617 let _ = state.event_tx.send(CompositorEvent::SurfaceCursor {
4618 surface_id: state.focused_surface_id,
4619 cursor: CursorImage::Hidden,
4620 });
4621 }
4622 }
4623 Request::Release => {
4624 state.pointers.retain(|p| p.id() != resource.id());
4625 }
4626 _ => {}
4627 }
4628 }
4629}
4630
4631impl Dispatch<wayland_server::protocol::wl_touch::WlTouch, ()> for Compositor {
4633 fn request(
4634 _: &mut Self,
4635 _: &Client,
4636 _: &wayland_server::protocol::wl_touch::WlTouch,
4637 _: <wayland_server::protocol::wl_touch::WlTouch as Resource>::Request,
4638 _: &(),
4639 _: &DisplayHandle,
4640 _: &mut DataInit<'_, Self>,
4641 ) {
4642 }
4643}
4644
4645impl GlobalDispatch<ZwpLinuxDmabufV1, ()> for Compositor {
4647 fn bind(
4648 state: &mut Self,
4649 _: &DisplayHandle,
4650 _: &Client,
4651 resource: New<ZwpLinuxDmabufV1>,
4652 _: &(),
4653 data_init: &mut DataInit<'_, Self>,
4654 ) {
4655 let dmabuf = data_init.init(resource, ());
4656 if dmabuf.version() >= 4 {
4659 return;
4660 }
4661 if dmabuf.version() >= 3 {
4662 if let Some(ref vk) = state.vulkan_renderer
4668 && !vk.supported_dmabuf_modifiers.is_empty()
4669 {
4670 for &(drm_fmt, modifier) in &vk.supported_dmabuf_modifiers {
4671 let mod_hi = (modifier >> 32) as u32;
4672 let mod_lo = (modifier & 0xFFFFFFFF) as u32;
4673 dmabuf.modifier(drm_fmt, mod_hi, mod_lo);
4674 }
4675 }
4676 } else if state
4680 .vulkan_renderer
4681 .as_ref()
4682 .is_some_and(|vk| vk.has_dmabuf())
4683 {
4684 dmabuf.format(drm_fourcc::ARGB8888);
4685 dmabuf.format(drm_fourcc::XRGB8888);
4686 dmabuf.format(drm_fourcc::ABGR8888);
4687 dmabuf.format(drm_fourcc::XBGR8888);
4688 }
4689 }
4690}
4691
4692impl Dispatch<ZwpLinuxDmabufV1, ()> for Compositor {
4693 fn request(
4694 state: &mut Self,
4695 _: &Client,
4696 _: &ZwpLinuxDmabufV1,
4697 request: <ZwpLinuxDmabufV1 as Resource>::Request,
4698 _: &(),
4699 _: &DisplayHandle,
4700 data_init: &mut DataInit<'_, Self>,
4701 ) {
4702 use zwp_linux_dmabuf_v1::Request;
4703 match request {
4704 Request::CreateParams { params_id } => {
4705 data_init.init(params_id, ());
4706 }
4707 Request::GetDefaultFeedback { id } => {
4708 let fb = data_init.init(id, ());
4709 state.send_dmabuf_feedback(&fb);
4710 }
4711 Request::GetSurfaceFeedback { id, .. } => {
4712 let fb = data_init.init(id, ());
4713 state.send_dmabuf_feedback(&fb);
4714 }
4715 Request::Destroy => {}
4716 _ => {}
4717 }
4718 }
4719}
4720
4721impl Dispatch<ZwpLinuxDmabufFeedbackV1, ()> for Compositor {
4722 fn request(
4723 _: &mut Self,
4724 _: &Client,
4725 _: &ZwpLinuxDmabufFeedbackV1,
4726 _request: <ZwpLinuxDmabufFeedbackV1 as Resource>::Request,
4727 _: &(),
4728 _: &DisplayHandle,
4729 _data_init: &mut DataInit<'_, Self>,
4730 ) {
4731 }
4733}
4734
4735impl Dispatch<ZwpLinuxBufferParamsV1, ()> for Compositor {
4737 fn request(
4738 state: &mut Self,
4739 client: &Client,
4740 resource: &ZwpLinuxBufferParamsV1,
4741 request: <ZwpLinuxBufferParamsV1 as Resource>::Request,
4742 _: &(),
4743 dh: &DisplayHandle,
4744 data_init: &mut DataInit<'_, Self>,
4745 ) {
4746 use zwp_linux_buffer_params_v1::Request;
4747 let params_id = resource.id();
4748 match request {
4749 Request::Add {
4750 fd,
4751 plane_idx: _,
4752 offset,
4753 stride,
4754 modifier_hi,
4755 modifier_lo,
4756 } => {
4757 let modifier = ((modifier_hi as u64) << 32) | (modifier_lo as u64);
4758 let entry = state
4759 .dmabuf_params
4760 .entry(params_id.clone())
4761 .or_insert_with(|| DmaBufParamsPending {
4762 resource: resource.clone(),
4763 planes: Vec::new(),
4764 modifier,
4765 });
4766 entry.modifier = modifier;
4767 entry.planes.push(DmaBufPlane { fd, offset, stride });
4768 }
4769 Request::Create {
4770 width,
4771 height,
4772 format,
4773 flags,
4774 } => {
4775 let pending = state.dmabuf_params.remove(¶ms_id);
4776 let (planes, modifier) = match pending {
4777 Some(p) => (p.planes, p.modifier),
4778 None => {
4779 resource.failed();
4780 return;
4781 }
4782 };
4783 let y_invert = flags
4784 .into_result()
4785 .ok()
4786 .is_some_and(|f| f.contains(zwp_linux_buffer_params_v1::Flags::YInvert));
4787 match client.create_resource::<WlBuffer, DmaBufBufferData, Compositor>(
4788 dh,
4789 1,
4790 DmaBufBufferData {
4791 width,
4792 height,
4793 fourcc: format,
4794 modifier,
4795 planes,
4796 y_invert,
4797 },
4798 ) {
4799 Ok(buffer) => resource.created(&buffer),
4800 Err(_) => resource.failed(),
4801 }
4802 }
4803 Request::CreateImmed {
4804 buffer_id,
4805 width,
4806 height,
4807 format,
4808 flags,
4809 } => {
4810 let (planes, modifier) = state
4811 .dmabuf_params
4812 .remove(¶ms_id)
4813 .map(|p| (p.planes, p.modifier))
4814 .unwrap_or_default();
4815 let y_invert = flags
4816 .into_result()
4817 .ok()
4818 .is_some_and(|f| f.contains(zwp_linux_buffer_params_v1::Flags::YInvert));
4819 data_init.init(
4820 buffer_id,
4821 DmaBufBufferData {
4822 width,
4823 height,
4824 fourcc: format,
4825 modifier,
4826 planes,
4827 y_invert,
4828 },
4829 );
4830 }
4831 Request::Destroy => {
4832 state.dmabuf_params.remove(¶ms_id);
4833 }
4834 _ => {}
4835 }
4836 }
4837}
4838
4839impl GlobalDispatch<WpFractionalScaleManagerV1, ()> for Compositor {
4841 fn bind(
4842 _: &mut Self,
4843 _: &DisplayHandle,
4844 _: &Client,
4845 resource: New<WpFractionalScaleManagerV1>,
4846 _: &(),
4847 data_init: &mut DataInit<'_, Self>,
4848 ) {
4849 data_init.init(resource, ());
4850 }
4851}
4852
4853impl Dispatch<WpFractionalScaleManagerV1, ()> for Compositor {
4854 fn request(
4855 state: &mut Self,
4856 _: &Client,
4857 _: &WpFractionalScaleManagerV1,
4858 request: <WpFractionalScaleManagerV1 as Resource>::Request,
4859 _: &(),
4860 _: &DisplayHandle,
4861 data_init: &mut DataInit<'_, Self>,
4862 ) {
4863 use wp_fractional_scale_manager_v1::Request;
4864 match request {
4865 Request::GetFractionalScale { id, surface: _ } => {
4866 let fs = data_init.init(id, ());
4867 fs.preferred_scale(state.output_scale_120 as u32);
4869 state.fractional_scales.push(fs);
4870 }
4871 Request::Destroy => {}
4872 _ => {}
4873 }
4874 }
4875}
4876
4877impl Dispatch<WpFractionalScaleV1, ()> for Compositor {
4879 fn request(
4880 state: &mut Self,
4881 _: &Client,
4882 resource: &WpFractionalScaleV1,
4883 _: <WpFractionalScaleV1 as Resource>::Request,
4884 _: &(),
4885 _: &DisplayHandle,
4886 _: &mut DataInit<'_, Self>,
4887 ) {
4888 state
4890 .fractional_scales
4891 .retain(|fs| fs.id() != resource.id());
4892 }
4893}
4894
4895impl GlobalDispatch<WpViewporter, ()> for Compositor {
4897 fn bind(
4898 _: &mut Self,
4899 _: &DisplayHandle,
4900 _: &Client,
4901 resource: New<WpViewporter>,
4902 _: &(),
4903 data_init: &mut DataInit<'_, Self>,
4904 ) {
4905 data_init.init(resource, ());
4906 }
4907}
4908
4909impl Dispatch<WpViewporter, ()> for Compositor {
4910 fn request(
4911 _: &mut Self,
4912 _: &Client,
4913 _: &WpViewporter,
4914 request: <WpViewporter as Resource>::Request,
4915 _: &(),
4916 _: &DisplayHandle,
4917 data_init: &mut DataInit<'_, Self>,
4918 ) {
4919 use wp_viewporter::Request;
4920 match request {
4921 Request::GetViewport { id, surface } => {
4922 let obj_id = surface.id();
4925 data_init.init(id, obj_id);
4926 }
4927 Request::Destroy => {}
4928 _ => {}
4929 }
4930 }
4931}
4932
4933impl Dispatch<WpViewport, ObjectId> for Compositor {
4935 fn request(
4936 state: &mut Self,
4937 _: &Client,
4938 _: &WpViewport,
4939 request: <WpViewport as Resource>::Request,
4940 surface_obj_id: &ObjectId,
4941 _: &DisplayHandle,
4942 _: &mut DataInit<'_, Self>,
4943 ) {
4944 use wayland_protocols::wp::viewporter::server::wp_viewport::Request;
4945 match request {
4946 Request::SetDestination { width, height } => {
4947 if let Some(surf) = state.surfaces.get_mut(surface_obj_id) {
4948 if width > 0 && height > 0 {
4950 surf.pending_viewport_destination = Some((width, height));
4951 } else {
4952 surf.pending_viewport_destination = None;
4953 }
4954 }
4955 }
4956 Request::SetSource { .. } => {
4957 }
4959 Request::Destroy => {}
4960 _ => {}
4961 }
4962 }
4963}
4964
4965impl GlobalDispatch<WlDataDeviceManager, ()> for Compositor {
4972 fn bind(
4973 _: &mut Self,
4974 _: &DisplayHandle,
4975 _: &Client,
4976 resource: New<WlDataDeviceManager>,
4977 _: &(),
4978 data_init: &mut DataInit<'_, Self>,
4979 ) {
4980 data_init.init(resource, ());
4981 }
4982}
4983
4984impl Dispatch<WlDataDeviceManager, ()> for Compositor {
4985 fn request(
4986 state: &mut Self,
4987 _: &Client,
4988 _: &WlDataDeviceManager,
4989 request: <WlDataDeviceManager as Resource>::Request,
4990 _: &(),
4991 _: &DisplayHandle,
4992 data_init: &mut DataInit<'_, Self>,
4993 ) {
4994 use wl_data_device_manager::Request;
4995 match request {
4996 Request::CreateDataSource { id } => {
4997 data_init.init(
4998 id,
4999 DataSourceData {
5000 mime_types: std::sync::Mutex::new(Vec::new()),
5001 },
5002 );
5003 }
5004 Request::GetDataDevice { id, seat: _ } => {
5005 let dd = data_init.init(id, ());
5006 state.data_devices.push(dd);
5007 }
5008 _ => {}
5009 }
5010 }
5011}
5012
5013impl Dispatch<WlDataSource, DataSourceData> for Compositor {
5014 fn request(
5015 _: &mut Self,
5016 _: &Client,
5017 _: &WlDataSource,
5018 request: <WlDataSource as Resource>::Request,
5019 data: &DataSourceData,
5020 _: &DisplayHandle,
5021 _: &mut DataInit<'_, Self>,
5022 ) {
5023 use wl_data_source::Request;
5024 match request {
5025 Request::Offer { mime_type } => {
5026 data.mime_types.lock().unwrap().push(mime_type);
5027 }
5028 Request::Destroy => {}
5029 _ => {} }
5031 }
5032
5033 fn destroyed(
5034 state: &mut Self,
5035 _: wayland_server::backend::ClientId,
5036 resource: &WlDataSource,
5037 _: &DataSourceData,
5038 ) {
5039 if state
5040 .selection_source
5041 .as_ref()
5042 .is_some_and(|s| s.id() == resource.id())
5043 {
5044 state.selection_source = None;
5045 }
5046 }
5047}
5048
5049impl Dispatch<WlDataDevice, ()> for Compositor {
5050 fn request(
5051 state: &mut Self,
5052 _: &Client,
5053 _: &WlDataDevice,
5054 request: <WlDataDevice as Resource>::Request,
5055 _: &(),
5056 _: &DisplayHandle,
5057 _: &mut DataInit<'_, Self>,
5058 ) {
5059 use wl_data_device::Request;
5060 match request {
5061 Request::SetSelection { source, serial: _ } => {
5062 state.selection_source = source.clone();
5063 if let Some(ref src) = source {
5065 let data = src.data::<DataSourceData>().unwrap();
5066 let mimes = data.mime_types.lock().unwrap();
5067 let text_mime = mimes
5068 .iter()
5069 .find(|m| {
5070 m.as_str() == "text/plain;charset=utf-8"
5071 || m.as_str() == "text/plain"
5072 || m.as_str() == "UTF8_STRING"
5073 })
5074 .cloned();
5075 drop(mimes);
5076 if let Some(mime) = text_mime {
5077 state.read_data_source_and_emit(src, &mime);
5078 }
5079 }
5080 }
5081 Request::Release => {}
5082 _ => {} }
5084 }
5085
5086 fn destroyed(
5087 state: &mut Self,
5088 _: wayland_server::backend::ClientId,
5089 resource: &WlDataDevice,
5090 _: &(),
5091 ) {
5092 state.data_devices.retain(|d| d.id() != resource.id());
5093 }
5094}
5095
5096impl Dispatch<WlDataOffer, DataOfferData> for Compositor {
5097 fn request(
5098 state: &mut Self,
5099 _: &Client,
5100 _: &WlDataOffer,
5101 request: <WlDataOffer as Resource>::Request,
5102 data: &DataOfferData,
5103 _: &DisplayHandle,
5104 _: &mut DataInit<'_, Self>,
5105 ) {
5106 use wl_data_offer::Request;
5107 match request {
5108 Request::Receive { mime_type, fd } => {
5109 if data.external {
5110 if let Some(ref cb) = state.external_clipboard
5112 && (cb.mime_type == mime_type
5113 || mime_type == "text/plain"
5114 || mime_type == "text/plain;charset=utf-8"
5115 || mime_type == "UTF8_STRING")
5116 {
5117 use std::io::Write;
5118 let mut f = std::fs::File::from(fd);
5119 let _ = f.write_all(&cb.data);
5120 }
5121 } else if let Some(ref src) = state.selection_source {
5122 src.send(mime_type, fd.as_fd());
5124 }
5125 }
5126 Request::Destroy => {}
5127 _ => {} }
5129 }
5130}
5131
5132impl Compositor {
5133 fn read_data_source_and_emit(&mut self, source: &WlDataSource, mime_type: &str) {
5136 let mut fds = [0i32; 2];
5137 if unsafe { libc::pipe(fds.as_mut_ptr()) } != 0 {
5138 return;
5139 }
5140 let read_fd = unsafe { OwnedFd::from_raw_fd(fds[0]) };
5141 let write_fd = unsafe { OwnedFd::from_raw_fd(fds[1]) };
5142 source.send(mime_type.to_string(), write_fd.as_fd());
5143 let _ = self.display_handle.flush_clients();
5144 unsafe {
5146 libc::fcntl(read_fd.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
5147 }
5148 std::thread::sleep(std::time::Duration::from_millis(5));
5150 let mut buf = Vec::new();
5151 let mut tmp = [0u8; 8192];
5152 loop {
5153 let n = unsafe {
5154 libc::read(
5155 read_fd.as_raw_fd(),
5156 tmp.as_mut_ptr() as *mut libc::c_void,
5157 tmp.len(),
5158 )
5159 };
5160 if n <= 0 {
5161 break;
5162 }
5163 buf.extend_from_slice(&tmp[..n as usize]);
5164 if buf.len() > 1024 * 1024 {
5165 break; }
5167 }
5168 if !buf.is_empty() {
5169 let _ = self.event_tx.send(CompositorEvent::ClipboardContent {
5170 mime_type: mime_type.to_string(),
5171 data: buf,
5172 });
5173 (self.event_notify)();
5174 }
5175 }
5176
5177 fn offer_external_clipboard(&mut self) {
5179 let Some(ref cb) = self.external_clipboard else {
5180 return;
5181 };
5182 let mime = cb.mime_type.clone();
5183 for dd in &self.data_devices {
5184 if let Some(client) = dd.client() {
5185 let offer = client
5186 .create_resource::<WlDataOffer, DataOfferData, Compositor>(
5187 &self.display_handle,
5188 dd.version(),
5189 DataOfferData { external: true },
5190 )
5191 .unwrap();
5192 dd.data_offer(&offer);
5193 offer.offer(mime.clone());
5194 if mime.starts_with("text/plain") {
5196 if mime != "text/plain" {
5197 offer.offer("text/plain".to_string());
5198 }
5199 if mime != "text/plain;charset=utf-8" {
5200 offer.offer("text/plain;charset=utf-8".to_string());
5201 }
5202 offer.offer("UTF8_STRING".to_string());
5203 }
5204 dd.selection(Some(&offer));
5205 }
5206 }
5207 let _ = self.display_handle.flush_clients();
5208 }
5209}
5210
5211impl GlobalDispatch<ZwpPrimarySelectionDeviceManagerV1, ()> for Compositor {
5214 fn bind(
5215 _: &mut Self,
5216 _: &DisplayHandle,
5217 _: &Client,
5218 resource: New<ZwpPrimarySelectionDeviceManagerV1>,
5219 _: &(),
5220 data_init: &mut DataInit<'_, Self>,
5221 ) {
5222 data_init.init(resource, ());
5223 }
5224}
5225
5226impl Dispatch<ZwpPrimarySelectionDeviceManagerV1, ()> for Compositor {
5227 fn request(
5228 state: &mut Self,
5229 _: &Client,
5230 _: &ZwpPrimarySelectionDeviceManagerV1,
5231 request: <ZwpPrimarySelectionDeviceManagerV1 as Resource>::Request,
5232 _: &(),
5233 _: &DisplayHandle,
5234 data_init: &mut DataInit<'_, Self>,
5235 ) {
5236 use zwp_primary_selection_device_manager_v1::Request;
5237 match request {
5238 Request::CreateSource { id } => {
5239 data_init.init(
5240 id,
5241 PrimarySourceData {
5242 mime_types: std::sync::Mutex::new(Vec::new()),
5243 },
5244 );
5245 }
5246 Request::GetDevice { id, seat: _ } => {
5247 let pd = data_init.init(id, ());
5248 state.primary_devices.push(pd);
5249 }
5250 Request::Destroy => {}
5251 _ => {}
5252 }
5253 }
5254}
5255
5256impl Dispatch<ZwpPrimarySelectionSourceV1, PrimarySourceData> for Compositor {
5257 fn request(
5258 _: &mut Self,
5259 _: &Client,
5260 _: &ZwpPrimarySelectionSourceV1,
5261 request: <ZwpPrimarySelectionSourceV1 as Resource>::Request,
5262 data: &PrimarySourceData,
5263 _: &DisplayHandle,
5264 _: &mut DataInit<'_, Self>,
5265 ) {
5266 use zwp_primary_selection_source_v1::Request;
5267 match request {
5268 Request::Offer { mime_type } => {
5269 data.mime_types.lock().unwrap().push(mime_type);
5270 }
5271 Request::Destroy => {}
5272 _ => {}
5273 }
5274 }
5275
5276 fn destroyed(
5277 state: &mut Self,
5278 _: wayland_server::backend::ClientId,
5279 resource: &ZwpPrimarySelectionSourceV1,
5280 _: &PrimarySourceData,
5281 ) {
5282 if state
5283 .primary_source
5284 .as_ref()
5285 .is_some_and(|s| s.id() == resource.id())
5286 {
5287 state.primary_source = None;
5288 }
5289 }
5290}
5291
5292impl Dispatch<ZwpPrimarySelectionDeviceV1, ()> for Compositor {
5293 fn request(
5294 state: &mut Self,
5295 _: &Client,
5296 _: &ZwpPrimarySelectionDeviceV1,
5297 request: <ZwpPrimarySelectionDeviceV1 as Resource>::Request,
5298 _: &(),
5299 _: &DisplayHandle,
5300 _: &mut DataInit<'_, Self>,
5301 ) {
5302 use zwp_primary_selection_device_v1::Request;
5303 match request {
5304 Request::SetSelection { source, serial: _ } => {
5305 state.primary_source = source;
5306 }
5307 Request::Destroy => {}
5308 _ => {}
5309 }
5310 }
5311
5312 fn destroyed(
5313 state: &mut Self,
5314 _: wayland_server::backend::ClientId,
5315 resource: &ZwpPrimarySelectionDeviceV1,
5316 _: &(),
5317 ) {
5318 state.primary_devices.retain(|d| d.id() != resource.id());
5319 }
5320}
5321
5322impl Dispatch<ZwpPrimarySelectionOfferV1, PrimaryOfferData> for Compositor {
5323 fn request(
5324 state: &mut Self,
5325 _: &Client,
5326 _: &ZwpPrimarySelectionOfferV1,
5327 request: <ZwpPrimarySelectionOfferV1 as Resource>::Request,
5328 data: &PrimaryOfferData,
5329 _: &DisplayHandle,
5330 _: &mut DataInit<'_, Self>,
5331 ) {
5332 use zwp_primary_selection_offer_v1::Request;
5333 match request {
5334 Request::Receive { mime_type, fd } => {
5335 if data.external {
5336 if let Some(ref cb) = state.external_primary {
5337 use std::io::Write;
5338 let mut f = std::fs::File::from(fd);
5339 let _ = f.write_all(&cb.data);
5340 let _ = mime_type; }
5342 } else if let Some(ref src) = state.primary_source {
5343 src.send(mime_type, fd.as_fd());
5344 }
5345 }
5346 Request::Destroy => {}
5347 _ => {}
5348 }
5349 }
5350}
5351
5352impl GlobalDispatch<ZwpPointerConstraintsV1, ()> for Compositor {
5355 fn bind(
5356 _: &mut Self,
5357 _: &DisplayHandle,
5358 _: &Client,
5359 resource: New<ZwpPointerConstraintsV1>,
5360 _: &(),
5361 data_init: &mut DataInit<'_, Self>,
5362 ) {
5363 data_init.init(resource, ());
5364 }
5365}
5366
5367impl Dispatch<ZwpPointerConstraintsV1, ()> for Compositor {
5368 fn request(
5369 _: &mut Self,
5370 _: &Client,
5371 _: &ZwpPointerConstraintsV1,
5372 request: <ZwpPointerConstraintsV1 as Resource>::Request,
5373 _: &(),
5374 _: &DisplayHandle,
5375 data_init: &mut DataInit<'_, Self>,
5376 ) {
5377 use zwp_pointer_constraints_v1::Request;
5378 match request {
5379 Request::LockPointer {
5380 id,
5381 surface: _,
5382 pointer: _,
5383 region: _,
5384 lifetime: _,
5385 } => {
5386 let lp = data_init.init(id, ());
5387 lp.locked();
5389 }
5390 Request::ConfinePointer {
5391 id,
5392 surface: _,
5393 pointer: _,
5394 region: _,
5395 lifetime: _,
5396 } => {
5397 let cp = data_init.init(id, ());
5398 cp.confined();
5399 }
5400 Request::Destroy => {}
5401 _ => {}
5402 }
5403 }
5404}
5405
5406impl Dispatch<ZwpLockedPointerV1, ()> for Compositor {
5407 fn request(
5408 _: &mut Self,
5409 _: &Client,
5410 _: &ZwpLockedPointerV1,
5411 _: <ZwpLockedPointerV1 as Resource>::Request,
5412 _: &(),
5413 _: &DisplayHandle,
5414 _: &mut DataInit<'_, Self>,
5415 ) {
5416 }
5418}
5419
5420impl Dispatch<ZwpConfinedPointerV1, ()> for Compositor {
5421 fn request(
5422 _: &mut Self,
5423 _: &Client,
5424 _: &ZwpConfinedPointerV1,
5425 _: <ZwpConfinedPointerV1 as Resource>::Request,
5426 _: &(),
5427 _: &DisplayHandle,
5428 _: &mut DataInit<'_, Self>,
5429 ) {
5430 }
5432}
5433
5434impl GlobalDispatch<ZwpRelativePointerManagerV1, ()> for Compositor {
5437 fn bind(
5438 _: &mut Self,
5439 _: &DisplayHandle,
5440 _: &Client,
5441 resource: New<ZwpRelativePointerManagerV1>,
5442 _: &(),
5443 data_init: &mut DataInit<'_, Self>,
5444 ) {
5445 data_init.init(resource, ());
5446 }
5447}
5448
5449impl Dispatch<ZwpRelativePointerManagerV1, ()> for Compositor {
5450 fn request(
5451 state: &mut Self,
5452 _: &Client,
5453 _: &ZwpRelativePointerManagerV1,
5454 request: <ZwpRelativePointerManagerV1 as Resource>::Request,
5455 _: &(),
5456 _: &DisplayHandle,
5457 data_init: &mut DataInit<'_, Self>,
5458 ) {
5459 use zwp_relative_pointer_manager_v1::Request;
5460 match request {
5461 Request::GetRelativePointer { id, pointer: _ } => {
5462 let rp = data_init.init(id, ());
5463 state.relative_pointers.push(rp);
5464 }
5465 Request::Destroy => {}
5466 _ => {}
5467 }
5468 }
5469}
5470
5471impl Dispatch<ZwpRelativePointerV1, ()> for Compositor {
5472 fn request(
5473 state: &mut Self,
5474 _: &Client,
5475 resource: &ZwpRelativePointerV1,
5476 _: <ZwpRelativePointerV1 as Resource>::Request,
5477 _: &(),
5478 _: &DisplayHandle,
5479 _: &mut DataInit<'_, Self>,
5480 ) {
5481 state
5483 .relative_pointers
5484 .retain(|rp| rp.id() != resource.id());
5485 }
5486}
5487
5488impl GlobalDispatch<ZwpTextInputManagerV3, ()> for Compositor {
5491 fn bind(
5492 _: &mut Self,
5493 _: &DisplayHandle,
5494 _: &Client,
5495 resource: New<ZwpTextInputManagerV3>,
5496 _: &(),
5497 data_init: &mut DataInit<'_, Self>,
5498 ) {
5499 data_init.init(resource, ());
5500 }
5501}
5502
5503impl Dispatch<ZwpTextInputManagerV3, ()> for Compositor {
5504 fn request(
5505 state: &mut Self,
5506 _: &Client,
5507 _: &ZwpTextInputManagerV3,
5508 request: <ZwpTextInputManagerV3 as Resource>::Request,
5509 _: &(),
5510 _: &DisplayHandle,
5511 data_init: &mut DataInit<'_, Self>,
5512 ) {
5513 use zwp_text_input_manager_v3::Request;
5514 match request {
5515 Request::GetTextInput { id, seat: _ } => {
5516 let ti = data_init.init(id, ());
5517 state.text_inputs.push(TextInputState {
5518 resource: ti,
5519 enabled: false,
5520 });
5521 }
5522 Request::Destroy => {}
5523 _ => {}
5524 }
5525 }
5526}
5527
5528impl Dispatch<ZwpTextInputV3, ()> for Compositor {
5529 fn request(
5530 state: &mut Self,
5531 _: &Client,
5532 resource: &ZwpTextInputV3,
5533 request: <ZwpTextInputV3 as Resource>::Request,
5534 _: &(),
5535 _: &DisplayHandle,
5536 _: &mut DataInit<'_, Self>,
5537 ) {
5538 use zwp_text_input_v3::Request;
5539 match request {
5540 Request::Enable => {
5541 if let Some(ti) = state
5542 .text_inputs
5543 .iter_mut()
5544 .find(|t| t.resource.id() == resource.id())
5545 {
5546 ti.enabled = true;
5547 }
5548 }
5549 Request::Disable => {
5550 if let Some(ti) = state
5551 .text_inputs
5552 .iter_mut()
5553 .find(|t| t.resource.id() == resource.id())
5554 {
5555 ti.enabled = false;
5556 }
5557 }
5558 Request::Commit => {
5559 }
5561 Request::Destroy => {
5562 state
5563 .text_inputs
5564 .retain(|t| t.resource.id() != resource.id());
5565 }
5566 _ => {}
5569 }
5570 }
5571}
5572
5573impl GlobalDispatch<XdgActivationV1, ()> for Compositor {
5576 fn bind(
5577 _: &mut Self,
5578 _: &DisplayHandle,
5579 _: &Client,
5580 resource: New<XdgActivationV1>,
5581 _: &(),
5582 data_init: &mut DataInit<'_, Self>,
5583 ) {
5584 data_init.init(resource, ());
5585 }
5586}
5587
5588impl Dispatch<XdgActivationV1, ()> for Compositor {
5589 fn request(
5590 state: &mut Self,
5591 _: &Client,
5592 _: &XdgActivationV1,
5593 request: <XdgActivationV1 as Resource>::Request,
5594 _: &(),
5595 _: &DisplayHandle,
5596 data_init: &mut DataInit<'_, Self>,
5597 ) {
5598 use xdg_activation_v1::Request;
5599 match request {
5600 Request::GetActivationToken { id } => {
5601 let serial = state.next_activation_token;
5602 state.next_activation_token = serial.wrapping_add(1);
5603 data_init.init(id, ActivationTokenData { serial });
5604 }
5605 Request::Activate {
5606 token: _,
5607 surface: _,
5608 } => {
5609 }
5612 Request::Destroy => {}
5613 _ => {}
5614 }
5615 }
5616}
5617
5618impl Dispatch<XdgActivationTokenV1, ActivationTokenData> for Compositor {
5619 fn request(
5620 _: &mut Self,
5621 _: &Client,
5622 resource: &XdgActivationTokenV1,
5623 request: <XdgActivationTokenV1 as Resource>::Request,
5624 data: &ActivationTokenData,
5625 _: &DisplayHandle,
5626 _: &mut DataInit<'_, Self>,
5627 ) {
5628 use xdg_activation_token_v1::Request;
5629 match request {
5630 Request::Commit => {
5631 resource.done(format!("blit-token-{}", data.serial));
5634 }
5635 Request::SetSerial { .. } | Request::SetAppId { .. } | Request::SetSurface { .. } => {}
5636 Request::Destroy => {}
5637 _ => {}
5638 }
5639 }
5640}
5641
5642impl GlobalDispatch<WpCursorShapeManagerV1, ()> for Compositor {
5645 fn bind(
5646 _: &mut Self,
5647 _: &DisplayHandle,
5648 _: &Client,
5649 resource: New<WpCursorShapeManagerV1>,
5650 _: &(),
5651 data_init: &mut DataInit<'_, Self>,
5652 ) {
5653 data_init.init(resource, ());
5654 }
5655}
5656
5657impl Dispatch<WpCursorShapeManagerV1, ()> for Compositor {
5658 fn request(
5659 _: &mut Self,
5660 _: &Client,
5661 _: &WpCursorShapeManagerV1,
5662 request: <WpCursorShapeManagerV1 as Resource>::Request,
5663 _: &(),
5664 _: &DisplayHandle,
5665 data_init: &mut DataInit<'_, Self>,
5666 ) {
5667 use wp_cursor_shape_manager_v1::Request;
5668 match request {
5669 Request::GetPointer {
5670 cursor_shape_device,
5671 pointer: _,
5672 } => {
5673 data_init.init(cursor_shape_device, ());
5674 }
5675 Request::GetTabletToolV2 {
5676 cursor_shape_device,
5677 tablet_tool: _,
5678 } => {
5679 data_init.init(cursor_shape_device, ());
5680 }
5681 Request::Destroy => {}
5682 _ => {}
5683 }
5684 }
5685}
5686
5687impl Dispatch<WpCursorShapeDeviceV1, ()> for Compositor {
5688 fn request(
5689 state: &mut Self,
5690 _: &Client,
5691 _: &WpCursorShapeDeviceV1,
5692 request: <WpCursorShapeDeviceV1 as Resource>::Request,
5693 _: &(),
5694 _: &DisplayHandle,
5695 _: &mut DataInit<'_, Self>,
5696 ) {
5697 use wp_cursor_shape_device_v1::Request;
5698 match request {
5699 Request::SetShape { serial: _, shape } => {
5700 use wayland_server::WEnum;
5701 use wp_cursor_shape_device_v1::Shape;
5702 let name = match shape {
5703 WEnum::Value(Shape::Default) => "default",
5704 WEnum::Value(Shape::ContextMenu) => "context-menu",
5705 WEnum::Value(Shape::Help) => "help",
5706 WEnum::Value(Shape::Pointer) => "pointer",
5707 WEnum::Value(Shape::Progress) => "progress",
5708 WEnum::Value(Shape::Wait) => "wait",
5709 WEnum::Value(Shape::Cell) => "cell",
5710 WEnum::Value(Shape::Crosshair) => "crosshair",
5711 WEnum::Value(Shape::Text) => "text",
5712 WEnum::Value(Shape::VerticalText) => "vertical-text",
5713 WEnum::Value(Shape::Alias) => "alias",
5714 WEnum::Value(Shape::Copy) => "copy",
5715 WEnum::Value(Shape::Move) => "move",
5716 WEnum::Value(Shape::NoDrop) => "no-drop",
5717 WEnum::Value(Shape::NotAllowed) => "not-allowed",
5718 WEnum::Value(Shape::Grab) => "grab",
5719 WEnum::Value(Shape::Grabbing) => "grabbing",
5720 WEnum::Value(Shape::EResize) => "e-resize",
5721 WEnum::Value(Shape::NResize) => "n-resize",
5722 WEnum::Value(Shape::NeResize) => "ne-resize",
5723 WEnum::Value(Shape::NwResize) => "nw-resize",
5724 WEnum::Value(Shape::SResize) => "s-resize",
5725 WEnum::Value(Shape::SeResize) => "se-resize",
5726 WEnum::Value(Shape::SwResize) => "sw-resize",
5727 WEnum::Value(Shape::WResize) => "w-resize",
5728 WEnum::Value(Shape::EwResize) => "ew-resize",
5729 WEnum::Value(Shape::NsResize) => "ns-resize",
5730 WEnum::Value(Shape::NeswResize) => "nesw-resize",
5731 WEnum::Value(Shape::NwseResize) => "nwse-resize",
5732 WEnum::Value(Shape::ColResize) => "col-resize",
5733 WEnum::Value(Shape::RowResize) => "row-resize",
5734 WEnum::Value(Shape::AllScroll) => "all-scroll",
5735 WEnum::Value(Shape::ZoomIn) => "zoom-in",
5736 WEnum::Value(Shape::ZoomOut) => "zoom-out",
5737 _ => "default",
5738 };
5739 let _ = state.event_tx.send(CompositorEvent::SurfaceCursor {
5740 surface_id: state.focused_surface_id,
5741 cursor: CursorImage::Named(name.to_string()),
5742 });
5743 (state.event_notify)();
5744 }
5745 Request::Destroy => {}
5746 _ => {}
5747 }
5748 }
5749}
5750
5751impl wayland_server::backend::ClientData for ClientState {
5753 fn initialized(&self, _: wayland_server::backend::ClientId) {}
5754 fn disconnected(
5755 &self,
5756 _: wayland_server::backend::ClientId,
5757 _: wayland_server::backend::DisconnectReason,
5758 ) {
5759 }
5760}
5761
5762pub struct CompositorHandle {
5767 pub event_rx: mpsc::Receiver<CompositorEvent>,
5768 pub command_tx: mpsc::Sender<CompositorCommand>,
5769 pub socket_name: String,
5770 pub thread: std::thread::JoinHandle<()>,
5771 pub shutdown: Arc<AtomicBool>,
5772 pub vulkan_video_encode: bool,
5774 pub vulkan_video_encode_av1: bool,
5776 loop_signal: LoopSignal,
5777}
5778
5779impl CompositorHandle {
5780 pub fn wake(&self) {
5781 self.loop_signal.wakeup();
5782 }
5783}
5784
5785pub fn spawn_compositor(
5786 verbose: bool,
5787 event_notify: Arc<dyn Fn() + Send + Sync>,
5788 gpu_device: &str,
5789) -> CompositorHandle {
5790 let _gpu_device = gpu_device.to_string();
5791 let (event_tx, event_rx) = mpsc::channel();
5792 let (command_tx, command_rx) = mpsc::channel();
5793 let (socket_tx, socket_rx) = mpsc::sync_channel(1);
5794 let (signal_tx, signal_rx) = mpsc::sync_channel::<LoopSignal>(1);
5795 let (caps_tx, caps_rx) = mpsc::sync_channel::<(bool, bool)>(1);
5796 let shutdown = Arc::new(AtomicBool::new(false));
5797 let shutdown_clone = shutdown.clone();
5798
5799 let runtime_dir = std::env::var_os("XDG_RUNTIME_DIR")
5800 .map(std::path::PathBuf::from)
5801 .filter(|p| {
5802 let probe = p.join(".blit-probe");
5803 if std::fs::write(&probe, b"").is_ok() {
5804 let _ = std::fs::remove_file(&probe);
5805 true
5806 } else {
5807 false
5808 }
5809 })
5810 .unwrap_or_else(std::env::temp_dir);
5811
5812 let runtime_dir_clone = runtime_dir.clone();
5813 let thread = std::thread::Builder::new()
5814 .name("compositor".into())
5815 .spawn(move || {
5816 unsafe { std::env::set_var("XDG_RUNTIME_DIR", &runtime_dir_clone) };
5817 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
5818 run_compositor(
5819 event_tx,
5820 command_rx,
5821 socket_tx,
5822 signal_tx,
5823 caps_tx,
5824 event_notify,
5825 shutdown_clone,
5826 verbose,
5827 _gpu_device,
5828 );
5829 }));
5830 if let Err(e) = result {
5831 let msg = if let Some(s) = e.downcast_ref::<&str>() {
5832 s.to_string()
5833 } else if let Some(s) = e.downcast_ref::<String>() {
5834 s.clone()
5835 } else {
5836 "unknown panic".to_string()
5837 };
5838 eprintln!("[compositor] PANIC: {msg}");
5839 }
5840 })
5841 .expect("failed to spawn compositor thread");
5842
5843 let socket_name = socket_rx.recv().expect("compositor failed to start");
5844 let socket_name = runtime_dir
5845 .join(&socket_name)
5846 .to_string_lossy()
5847 .into_owned();
5848 let loop_signal = signal_rx
5849 .recv()
5850 .expect("compositor failed to send loop signal");
5851 let (vulkan_video_encode, vulkan_video_encode_av1) = caps_rx.recv().unwrap_or((false, false));
5852
5853 CompositorHandle {
5854 event_rx,
5855 command_tx,
5856 socket_name,
5857 thread,
5858 shutdown,
5859 vulkan_video_encode,
5860 vulkan_video_encode_av1,
5861 loop_signal,
5862 }
5863}
5864
5865#[allow(clippy::too_many_arguments)]
5866fn run_compositor(
5867 event_tx: mpsc::Sender<CompositorEvent>,
5868 command_rx: mpsc::Receiver<CompositorCommand>,
5869 socket_tx: mpsc::SyncSender<String>,
5870 signal_tx: mpsc::SyncSender<LoopSignal>,
5871 caps_tx: mpsc::SyncSender<(bool, bool)>,
5872 event_notify: Arc<dyn Fn() + Send + Sync>,
5873 shutdown: Arc<AtomicBool>,
5874 verbose: bool,
5875 gpu_device: String,
5876) {
5877 let mut event_loop: EventLoop<Compositor> =
5878 EventLoop::try_new().expect("failed to create event loop");
5879 let loop_signal = event_loop.get_signal();
5880
5881 let display: Display<Compositor> = Display::new().expect("failed to create display");
5882 let dh = display.handle();
5883
5884 eprintln!("[compositor] trying Vulkan renderer for {gpu_device}");
5887 let vulkan_renderer = super::vulkan_render::VulkanRenderer::try_new(&gpu_device);
5888 let has_dmabuf = vulkan_renderer.as_ref().is_some_and(|vk| vk.has_dmabuf());
5889 eprintln!(
5890 "[compositor] Vulkan renderer: {} (dmabuf={})",
5891 vulkan_renderer.is_some(),
5892 has_dmabuf,
5893 );
5894 if vulkan_renderer.is_none() {
5895 eprintln!(
5896 "[compositor] WARNING: no Vulkan renderer — clients can connect but NO frames will be composited (windows will never appear)."
5897 );
5898 eprintln!(
5899 "[compositor] WARNING: install a Vulkan driver; on GPU-less hosts a software driver works (e.g. `apt install mesa-vulkan-drivers` for lavapipe)."
5900 );
5901 }
5902
5903 dh.create_global::<Compositor, WlCompositor, ()>(6, ());
5905 dh.create_global::<Compositor, WlSubcompositor, ()>(1, ());
5906 dh.create_global::<Compositor, XdgWmBase, ()>(6, ());
5907 dh.create_global::<Compositor, WlShm, ()>(1, ());
5908 dh.create_global::<Compositor, WlOutput, ()>(4, ());
5909 dh.create_global::<Compositor, WlSeat, ()>(9, ());
5910 if has_dmabuf {
5915 dh.create_global::<Compositor, ZwpLinuxDmabufV1, ()>(4, ());
5916 }
5917 dh.create_global::<Compositor, WpViewporter, ()>(1, ());
5918 dh.create_global::<Compositor, WpFractionalScaleManagerV1, ()>(1, ());
5919 dh.create_global::<Compositor, ZxdgDecorationManagerV1, ()>(1, ());
5920 dh.create_global::<Compositor, WlDataDeviceManager, ()>(3, ());
5921 dh.create_global::<Compositor, ZwpPointerConstraintsV1, ()>(1, ());
5922 dh.create_global::<Compositor, ZwpRelativePointerManagerV1, ()>(1, ());
5923 dh.create_global::<Compositor, XdgActivationV1, ()>(1, ());
5924 dh.create_global::<Compositor, WpCursorShapeManagerV1, ()>(1, ());
5925 dh.create_global::<Compositor, ZwpPrimarySelectionDeviceManagerV1, ()>(1, ());
5926 dh.create_global::<Compositor, WpPresentation, ()>(1, ());
5927 dh.create_global::<Compositor, ZwpTextInputManagerV3, ()>(1, ());
5928
5929 let keymap_string = include_str!("../data/us-qwerty.xkb");
5931 let mut keymap_data = keymap_string.as_bytes().to_vec();
5932 keymap_data.push(0); let listening_socket = wayland_server::ListeningSocket::bind_auto("wayland", 0..33)
5936 .unwrap_or_else(|e| {
5937 let dir = std::env::var("XDG_RUNTIME_DIR").unwrap_or_else(|_| "(unset)".into());
5938 panic!("failed to create wayland socket in XDG_RUNTIME_DIR={dir}: {e}\nhint: ensure the directory exists and is writable by the current user");
5939 });
5940 let socket_name = listening_socket
5941 .socket_name()
5942 .unwrap()
5943 .to_string_lossy()
5944 .into_owned();
5945 socket_tx.send(socket_name).unwrap();
5946 let _ = signal_tx.send(loop_signal.clone());
5947
5948 let mut compositor = Compositor {
5949 display_handle: dh,
5950 surfaces: HashMap::new(),
5951 regions: HashMap::new(),
5952 toplevel_surface_ids: HashMap::new(),
5953 last_request_frame_ms: HashMap::new(),
5954 next_surface_id: 1,
5955 shm_pools: HashMap::new(),
5956 surface_meta: HashMap::new(),
5957 dmabuf_params: HashMap::new(),
5958 vulkan_renderer,
5959 output_width: 1920,
5960 output_height: 1080,
5961 output_refresh_mhz: 60_000,
5962 output_scale_120: 120,
5963 outputs: Vec::new(),
5964 keyboards: Vec::new(),
5965 pointers: Vec::new(),
5966 keyboard_keymap_data: keymap_data,
5967 mods_depressed: 0,
5968 mods_locked: 0,
5969 serial: 0,
5970 event_tx,
5971 event_notify,
5972 loop_signal: loop_signal.clone(),
5973 pending_commits: HashMap::new(),
5974 pending_encoded: Vec::new(),
5975 pending_native_sizes: HashMap::new(),
5976 pending_recomposite_toplevels: HashMap::new(),
5977 focused_surface_id: 0,
5978 pointer_entered_id: None,
5979 pointer_entered_local: (0.0, 0.0),
5980 pending_kb_reenter: false,
5981 gpu_device,
5982 verbose,
5983 shutdown: shutdown.clone(),
5984 last_reported_size: HashMap::new(),
5985 surface_sizes: HashMap::new(),
5986 positioners: HashMap::new(),
5987 fractional_scales: Vec::new(),
5988 data_devices: Vec::new(),
5989 selection_source: None,
5990 external_clipboard: None,
5991 primary_devices: Vec::new(),
5992 primary_source: None,
5993 external_primary: None,
5994 relative_pointers: Vec::new(),
5995 text_inputs: Vec::new(),
5996 text_input_serial: 0,
5997 next_activation_token: 1,
5998 popup_grab_stack: Vec::new(),
5999 kb_focus_popup: None,
6000 popup_dismiss_button: None,
6001 held_buffers: HashMap::new(),
6002 cursor_rgba: HashMap::new(),
6003 };
6004
6005 {
6007 let (vve, vve_av1) = compositor
6008 .vulkan_renderer
6009 .as_ref()
6010 .map(|vk| (vk.has_video_encode(), vk.has_video_encode_av1()))
6011 .unwrap_or((false, false));
6012 let _ = caps_tx.send((vve, vve_av1));
6013 }
6014
6015 let handle = event_loop.handle();
6016
6017 let display_source = Generic::new(display, Interest::READ, calloop::Mode::Level);
6019 handle
6020 .insert_source(display_source, |_, display, state| {
6021 let d = unsafe { display.get_mut() };
6022 if let Err(e) = d.dispatch_clients(state)
6023 && state.verbose
6024 {
6025 eprintln!("[compositor] dispatch_clients error: {e}");
6026 }
6027 state.cleanup_dead_surfaces();
6028 if let Err(e) = d.flush_clients()
6029 && state.verbose
6030 {
6031 eprintln!("[compositor] flush_clients error: {e}");
6032 }
6033 Ok(PostAction::Continue)
6034 })
6035 .expect("failed to insert display source");
6036
6037 let socket_source = Generic::new(listening_socket, Interest::READ, calloop::Mode::Level);
6039 handle
6040 .insert_source(socket_source, |_, socket, state| {
6041 let ls = unsafe { socket.get_mut() };
6042 if let Some(client_stream) = ls.accept().ok().flatten()
6043 && let Err(e) = state
6044 .display_handle
6045 .insert_client(client_stream, Arc::new(ClientState))
6046 && state.verbose
6047 {
6048 eprintln!("[compositor] insert_client error: {e}");
6049 }
6050 Ok(PostAction::Continue)
6051 })
6052 .expect("failed to insert listening socket");
6053
6054 if verbose {
6055 eprintln!("[compositor] entering event loop");
6056 }
6057
6058 while !shutdown.load(Ordering::Relaxed) {
6059 while let Ok(cmd) = command_rx.try_recv() {
6061 match cmd {
6062 CompositorCommand::Shutdown => {
6063 shutdown.store(true, Ordering::Relaxed);
6064 return;
6065 }
6066 other => compositor.handle_command(other),
6067 }
6068 }
6069
6070 let poll_timeout = if compositor
6073 .vulkan_renderer
6074 .as_ref()
6075 .is_some_and(|vk| vk.has_pending())
6076 {
6077 std::time::Duration::from_millis(1)
6078 } else {
6079 std::time::Duration::from_secs(1)
6080 };
6081
6082 if let Err(e) = event_loop.dispatch(Some(poll_timeout), &mut compositor)
6083 && verbose
6084 {
6085 eprintln!("[compositor] event loop error: {e}");
6086 }
6087
6088 if let Some(ref mut vk) = compositor.vulkan_renderer {
6094 let retired = vk.try_retire_pending();
6095 if !retired.is_empty() {
6096 let s120_u32 = (compositor.output_scale_120 as u32).max(120);
6097 if let Some(&(sid, nw, nh, _)) = retired
6101 .iter()
6102 .max_by_key(|&&(_, w, h, _)| (w as u64) * (h as u64))
6103 {
6104 let log_w = (nw * 120).div_ceil(s120_u32);
6105 let log_h = (nh * 120).div_ceil(s120_u32);
6106 compositor
6107 .pending_native_sizes
6108 .entry(sid)
6109 .or_insert((nw, nh, log_w, log_h));
6110 }
6111 for (sid, w, h, pixels) in retired {
6112 let log_w = (w * 120).div_ceil(s120_u32);
6113 let log_h = (h * 120).div_ceil(s120_u32);
6114 compositor
6115 .pending_commits
6116 .insert((sid, w, h), (log_w, log_h, pixels));
6117 }
6118 }
6119 }
6120
6121 let can_recomposite = compositor
6130 .vulkan_renderer
6131 .as_ref()
6132 .is_some_and(|vk| !vk.has_pending());
6133 if can_recomposite
6134 && let Some((&sid, &encoder_only)) =
6135 compositor.pending_recomposite_toplevels.iter().next()
6136 {
6137 compositor.pending_recomposite_toplevels.remove(&sid);
6138 if let Some(root_id) = compositor.toplevel_surface_ids.get(&sid).cloned() {
6139 compositor.composite_toplevel_into_pending(&root_id, sid, encoder_only);
6140 loop_signal.wakeup();
6145 }
6146 }
6147
6148 if !compositor.pending_commits.is_empty()
6149 || !compositor.pending_encoded.is_empty()
6150 || !compositor.pending_native_sizes.is_empty()
6151 {
6152 compositor.flush_pending_commits();
6153 }
6154
6155 if let Err(e) = compositor.display_handle.flush_clients()
6156 && verbose
6157 {
6158 eprintln!("[compositor] flush error: {e}");
6159 }
6160 }
6161
6162 if verbose {
6163 eprintln!("[compositor] event loop exited");
6164 }
6165}