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, HashSet};
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 Encoded {
160 data: Arc<Vec<u8>>,
161 is_keyframe: bool,
162 codec_flag: u8,
164 },
165}
166
167#[derive(Clone, Copy, Default)]
172pub struct ExternalOutputPlane {
173 pub offset: u32,
174 pub pitch: u32,
175}
176
177pub struct ExternalOutputBuffer {
178 pub fd: Arc<OwnedFd>,
179 pub fourcc: u32,
180 pub modifier: u64,
181 pub stride: u32,
182 pub offset: u32,
183 pub width: u32,
184 pub height: u32,
185 pub va_surface_id: u32,
186 pub va_display: usize,
187 pub planes: Vec<ExternalOutputPlane>,
189 pub nv12_fd: Option<Arc<OwnedFd>>,
193 pub nv12_stride: u32,
194 pub nv12_uv_offset: u32,
195 pub nv12_modifier: u64,
197 pub nv12_width: u32,
200 pub nv12_height: u32,
201}
202
203pub mod drm_fourcc {
204 pub const ARGB8888: u32 = u32::from_le_bytes(*b"AR24");
205 pub const XRGB8888: u32 = u32::from_le_bytes(*b"XR24");
206 pub const ABGR8888: u32 = u32::from_le_bytes(*b"AB24");
207 pub const XBGR8888: u32 = u32::from_le_bytes(*b"XB24");
208 pub const NV12: u32 = u32::from_le_bytes(*b"NV12");
209}
210
211impl PixelData {
212 pub fn to_rgba(&self, width: u32, height: u32) -> Vec<u8> {
213 let w = width as usize;
214 let h = height as usize;
215 match self {
216 PixelData::Rgba(data) => data.as_ref().clone(),
217 PixelData::Bgra(data) => {
218 let mut rgba = Vec::with_capacity(w * h * 4);
219 for px in data.chunks_exact(4) {
220 rgba.extend_from_slice(&[px[2], px[1], px[0], px[3]]);
221 }
222 rgba
223 }
224 PixelData::Nv12 {
225 data,
226 y_stride,
227 uv_stride,
228 } => {
229 let y_plane_size = *y_stride * h;
230 let uv_h = h.div_ceil(2);
231 let uv_plane_size = *uv_stride * uv_h;
232 if data.len() < y_plane_size + uv_plane_size {
233 return Vec::new();
234 }
235 let y_plane = &data[..y_plane_size];
236 let uv_plane = &data[y_plane_size..];
237 let mut rgba = Vec::with_capacity(w * h * 4);
238 for row in 0..h {
239 for col in 0..w {
240 let y = y_plane[row * y_stride + col];
241 let uv_idx = (row / 2) * uv_stride + (col / 2) * 2;
242 if uv_idx + 1 >= uv_plane.len() {
243 rgba.extend_from_slice(&[0, 0, 0, 255]);
244 continue;
245 }
246 let u = uv_plane[uv_idx];
247 let v = uv_plane[uv_idx + 1];
248 let [r, g, b] = yuv420_to_rgb(y, u, v);
249 rgba.extend_from_slice(&[r, g, b, 255]);
250 }
251 }
252 rgba
253 }
254 PixelData::DmaBuf {
255 fd,
256 fourcc,
257 stride,
258 offset,
259 ..
260 } => {
261 let raw = fd.as_raw_fd();
262 let stride_usize = *stride as usize;
263 let plane_offset = *offset as usize;
264 let map_size = plane_offset + stride_usize * h;
265 if map_size == 0 {
266 return Vec::new();
267 }
268 const DMA_BUF_SYNC_READ: u64 = 1;
275 const DMA_BUF_SYNC_START: u64 = 0;
276 const DMA_BUF_SYNC_END: u64 = 4;
277 const DMA_BUF_IOCTL_SYNC: libc::c_ulong = 0x40086200;
278 let did_sync = {
279 let mut pfd = libc::pollfd {
280 fd: raw,
281 events: libc::POLLIN,
282 revents: 0,
283 };
284 let ready = unsafe { libc::poll(&mut pfd, 1, 0) };
285 if ready > 0 {
286 let s: u64 = DMA_BUF_SYNC_START | DMA_BUF_SYNC_READ;
287 unsafe { libc::ioctl(raw, DMA_BUF_IOCTL_SYNC as _, &s) };
288 true
289 } else {
290 false
291 }
292 };
293 let ptr = unsafe {
294 libc::mmap(
295 std::ptr::null_mut(),
296 map_size,
297 libc::PROT_READ,
298 libc::MAP_SHARED,
299 raw,
300 0,
301 )
302 };
303 if ptr == libc::MAP_FAILED {
304 if did_sync {
305 let s: u64 = DMA_BUF_SYNC_END | DMA_BUF_SYNC_READ;
306 unsafe { libc::ioctl(raw, DMA_BUF_IOCTL_SYNC as _, &s) };
307 }
308 return Vec::new();
309 }
310 let slice = unsafe { std::slice::from_raw_parts(ptr as *const u8, map_size) };
311 let row_bytes = w * 4;
312 let mut pixels = Vec::with_capacity(w * h * 4);
313 for row in 0..h {
314 let start = plane_offset + row * stride_usize;
315 if start + row_bytes <= slice.len() {
316 pixels.extend_from_slice(&slice[start..start + row_bytes]);
317 }
318 }
319 let is_bgr_mem = matches!(*fourcc, drm_fourcc::ARGB8888 | drm_fourcc::XRGB8888);
320 let force_alpha = matches!(*fourcc, drm_fourcc::XRGB8888 | drm_fourcc::XBGR8888);
321 for px in pixels.chunks_exact_mut(4) {
322 if is_bgr_mem {
323 px.swap(0, 2);
324 }
325 if force_alpha {
326 px[3] = 255;
327 }
328 }
329 unsafe { libc::munmap(ptr, map_size) };
330 if did_sync {
331 let s: u64 = DMA_BUF_SYNC_END | DMA_BUF_SYNC_READ;
332 unsafe { libc::ioctl(raw, DMA_BUF_IOCTL_SYNC as _, &s) };
333 }
334 pixels
335 }
336 PixelData::Nv12DmaBuf {
337 fd,
338 stride,
339 uv_offset,
340 width: nv12_w,
341 height: nv12_h,
342 sync_fd,
343 } => {
344 if let Some(sync) = sync_fd {
350 let mut pfd = libc::pollfd {
351 fd: sync.as_raw_fd(),
352 events: libc::POLLIN,
353 revents: 0,
354 };
355 unsafe {
359 libc::poll(&mut pfd, 1, 10);
360 }
361 }
362 let nw = *nv12_w as usize;
363 let nh = *nv12_h as usize;
364 let stride_usize = *stride as usize;
365 let uv_off = *uv_offset as usize;
366 let y_plane_size = stride_usize * nh;
367 let uv_h = nh.div_ceil(2);
368 let uv_plane_size = stride_usize * uv_h;
369 let map_size = uv_off + uv_plane_size;
370 if map_size == 0 || nw == 0 || nh == 0 {
371 return Vec::new();
372 }
373 let raw = fd.as_raw_fd();
374 const DMA_BUF_SYNC_READ: u64 = 1;
375 const DMA_BUF_SYNC_START: u64 = 0;
376 const DMA_BUF_SYNC_END: u64 = 4;
377 const DMA_BUF_IOCTL_SYNC: libc::c_ulong = 0x40086200;
378 let s_start: u64 = DMA_BUF_SYNC_START | DMA_BUF_SYNC_READ;
379 let did_sync = unsafe { libc::ioctl(raw, DMA_BUF_IOCTL_SYNC as _, &s_start) == 0 };
380 let ptr = unsafe {
381 libc::mmap(
382 std::ptr::null_mut(),
383 map_size,
384 libc::PROT_READ,
385 libc::MAP_SHARED,
386 raw,
387 0,
388 )
389 };
390 if ptr == libc::MAP_FAILED {
391 if did_sync {
392 let s_end: u64 = DMA_BUF_SYNC_END | DMA_BUF_SYNC_READ;
393 unsafe { libc::ioctl(raw, DMA_BUF_IOCTL_SYNC as _, &s_end) };
394 }
395 return Vec::new();
396 }
397 let slice = unsafe { std::slice::from_raw_parts(ptr as *const u8, map_size) };
398 let y_plane = &slice[..y_plane_size.min(slice.len())];
399 let uv_plane = &slice[uv_off.min(slice.len())..];
400 let out_w = w.min(nw);
403 let out_h = h.min(nh);
404 let mut rgba = Vec::with_capacity(w * h * 4);
405 for row in 0..out_h {
406 for col in 0..out_w {
407 let y_idx = row * stride_usize + col;
408 let uv_idx = (row / 2) * stride_usize + (col / 2) * 2;
409 if y_idx >= y_plane.len() || uv_idx + 1 >= uv_plane.len() {
410 rgba.extend_from_slice(&[0, 0, 0, 255]);
411 continue;
412 }
413 let y = y_plane[y_idx];
414 let u = uv_plane[uv_idx];
415 let v = uv_plane[uv_idx + 1];
416 let [r, g, b] = yuv420_to_rgb(y, u, v);
417 rgba.extend_from_slice(&[r, g, b, 255]);
418 }
419 for _ in out_w..w {
421 rgba.extend_from_slice(&[0, 0, 0, 255]);
422 }
423 }
424 for _ in out_h..h {
425 for _ in 0..w {
426 rgba.extend_from_slice(&[0, 0, 0, 255]);
427 }
428 }
429 unsafe { libc::munmap(ptr, map_size) };
430 if did_sync {
431 let s_end: u64 = DMA_BUF_SYNC_END | DMA_BUF_SYNC_READ;
432 unsafe { libc::ioctl(raw, DMA_BUF_IOCTL_SYNC as _, &s_end) };
433 }
434 rgba
435 }
436 PixelData::VaSurface { .. } | PixelData::Encoded { .. } => Vec::new(),
437 }
438 }
439
440 pub fn is_empty(&self) -> bool {
441 match self {
442 PixelData::Bgra(v) | PixelData::Rgba(v) => v.is_empty(),
443 PixelData::Encoded { data, .. } => data.is_empty(),
444 PixelData::Nv12 { data, .. } => data.is_empty(),
445 PixelData::DmaBuf { .. }
446 | PixelData::VaSurface { .. }
447 | PixelData::Nv12DmaBuf { .. } => false,
448 }
449 }
450
451 pub fn is_dmabuf(&self) -> bool {
452 matches!(self, PixelData::DmaBuf { .. })
453 }
454
455 pub fn is_va_surface(&self) -> bool {
456 matches!(self, PixelData::VaSurface { .. })
457 }
458}
459
460#[derive(Clone)]
461pub enum CursorImage {
462 Named(String),
463 Custom {
464 hotspot_x: u16,
465 hotspot_y: u16,
466 width: u16,
467 height: u16,
468 rgba: Vec<u8>,
469 },
470 Hidden,
471}
472
473pub enum CompositorEvent {
474 SurfaceCreated {
475 surface_id: u16,
476 title: String,
477 app_id: String,
478 parent_id: u16,
479 width: u16,
480 height: u16,
481 },
482 SurfaceDestroyed {
483 surface_id: u16,
484 },
485 SurfaceCommit {
486 surface_id: u16,
487 width: u32,
488 height: u32,
489 pixels: PixelData,
490 timestamp_ms: u32,
494 },
495 SurfaceTitle {
496 surface_id: u16,
497 title: String,
498 },
499 SurfaceAppId {
500 surface_id: u16,
501 app_id: String,
502 },
503 SurfaceResized {
504 surface_id: u16,
505 width: u16,
506 height: u16,
507 },
508 ClipboardContent {
509 mime_type: String,
510 data: Vec<u8>,
511 },
512 SurfaceCursor {
513 surface_id: u16,
514 cursor: CursorImage,
515 },
516}
517
518pub enum CompositorCommand {
519 KeyInput {
520 surface_id: u16,
521 keycode: u32,
522 pressed: bool,
523 },
524 PointerMotion {
525 surface_id: u16,
526 x: f64,
527 y: f64,
528 },
529 PointerButton {
530 surface_id: u16,
531 button: u32,
532 pressed: bool,
533 },
534 PointerAxis {
535 surface_id: u16,
536 axis: u8,
537 value: f64,
538 },
539 SurfaceResize {
540 surface_id: u16,
541 width: u16,
542 height: u16,
543 scale_120: u16,
544 },
545 SurfaceFocus {
546 surface_id: u16,
547 },
548 SurfaceClose {
549 surface_id: u16,
550 },
551 ClipboardOffer {
552 mime_type: String,
553 data: Vec<u8>,
554 },
555 Capture {
556 surface_id: u16,
557 scale_120: u16,
558 reply: mpsc::SyncSender<Option<(u32, u32, Vec<u8>)>>,
559 },
560 RequestFrame {
561 surface_id: u16,
562 },
563 ReleaseKeys {
564 keycodes: Vec<u32>,
565 },
566 ClipboardListMimes {
568 reply: mpsc::SyncSender<Vec<String>>,
569 },
570 ClipboardGet {
572 mime_type: String,
573 reply: mpsc::SyncSender<Option<Vec<u8>>>,
574 },
575 SetExternalOutputBuffers {
583 surface_id: u32,
584 target_w: u32,
585 target_h: u32,
586 buffers: Vec<ExternalOutputBuffer>,
587 },
588 RegisterDownscaleTarget {
598 surface_id: u32,
599 target_w: u32,
600 target_h: u32,
601 },
602 ClearDownscaleTarget {
605 surface_id: u32,
606 target_w: u32,
607 target_h: u32,
608 },
609 TextInput {
611 text: String,
612 },
613 SetRefreshRate {
615 mhz: u32,
616 },
617 SetVulkanEncoder {
619 surface_id: u32,
620 codec: u8,
621 qp: u8,
622 width: u32,
623 height: u32,
624 },
625 RequestVulkanKeyframe {
627 surface_id: u32,
628 },
629 DestroyVulkanEncoder {
631 surface_id: u32,
632 },
633 Shutdown,
634}
635
636pub(crate) struct Surface {
642 pub surface_id: u16,
643 pub wl_surface: WlSurface,
644
645 pending_buffer: Option<WlBuffer>,
647 pending_buffer_scale: i32,
648 pending_damage: bool,
649 pending_frame_callbacks: Vec<WlCallback>,
650 pending_presentation_feedbacks: Vec<WpPresentationFeedback>,
651 pending_opaque: bool,
652 pending_input_region: Option<Option<Vec<RegionOp>>>,
655
656 pub buffer_scale: i32,
658 pub is_opaque: bool,
659 input_region: Option<Vec<RegionOp>>,
662
663 pub parent_surface_id: Option<ObjectId>,
665 pending_subsurface_position: Option<(i32, i32)>,
666 pub subsurface_position: (i32, i32),
667 pub children: Vec<ObjectId>,
668
669 xdg_surface: Option<XdgSurface>,
671 xdg_toplevel: Option<XdgToplevel>,
672 xdg_popup: Option<XdgPopup>,
673 pub xdg_geometry: Option<(i32, i32, i32, i32)>,
674
675 title: String,
676 app_id: String,
677
678 pending_viewport_destination: Option<(i32, i32)>,
680 pub viewport_destination: Option<(i32, i32)>,
685
686 is_cursor: bool,
687 cursor_hotspot: (i32, i32),
688}
689
690struct ShmPool {
691 resource: WlShmPool,
692 fd: OwnedFd,
693 inner: std::sync::Mutex<ShmPoolInner>,
694}
695
696struct ShmPoolInner {
697 size: usize,
698 mmap_ptr: *mut u8,
699}
700
701unsafe impl Send for ShmPoolInner {}
704
705impl ShmPool {
706 fn new(resource: WlShmPool, fd: OwnedFd, size: i32) -> Self {
707 let sz = size.max(0) as usize;
708 let ptr = if sz > 0 {
709 unsafe {
710 libc::mmap(
711 std::ptr::null_mut(),
712 sz,
713 libc::PROT_READ,
714 libc::MAP_SHARED,
715 fd.as_raw_fd(),
716 0,
717 )
718 }
719 } else {
720 libc::MAP_FAILED
721 };
722 ShmPool {
723 resource,
724 fd,
725 inner: std::sync::Mutex::new(ShmPoolInner {
726 size: sz,
727 mmap_ptr: if ptr == libc::MAP_FAILED {
728 std::ptr::null_mut()
729 } else {
730 ptr as *mut u8
731 },
732 }),
733 }
734 }
735
736 fn resize(&self, new_size: i32) {
737 let new_sz = new_size.max(0) as usize;
738 let mut inner = self.inner.lock().unwrap();
739 if new_sz <= inner.size {
740 return;
741 }
742 if !inner.mmap_ptr.is_null() {
743 unsafe {
744 libc::munmap(inner.mmap_ptr as *mut _, inner.size);
745 }
746 }
747 let ptr = unsafe {
748 libc::mmap(
749 std::ptr::null_mut(),
750 new_sz,
751 libc::PROT_READ,
752 libc::MAP_SHARED,
753 self.fd.as_raw_fd(),
754 0,
755 )
756 };
757 inner.mmap_ptr = if ptr == libc::MAP_FAILED {
758 std::ptr::null_mut()
759 } else {
760 ptr as *mut u8
761 };
762 inner.size = new_sz;
763 }
764
765 fn with_mmap<F, R>(&self, f: F) -> Option<R>
771 where
772 F: FnOnce(&[u8]) -> R,
773 {
774 let inner = self.inner.lock().unwrap();
775 if inner.mmap_ptr.is_null() {
776 return None;
777 }
778 let slice = unsafe { std::slice::from_raw_parts(inner.mmap_ptr, inner.size) };
779 Some(f(slice))
780 }
781
782 fn read_buffer(
783 &self,
784 offset: i32,
785 width: i32,
786 height: i32,
787 stride: i32,
788 format: wl_shm::Format,
789 ) -> Option<(u32, u32, PixelData)> {
790 let inner = self.inner.lock().unwrap();
791 if inner.mmap_ptr.is_null() {
792 return None;
793 }
794 if offset < 0 || width <= 0 || height <= 0 || stride < 0 {
799 return None;
800 }
801 let w = width as u32;
802 let h = height as u32;
803 let s = stride as usize;
804 let off = offset as usize;
805 let row_bytes = (w as usize).checked_mul(4)?;
806 let needed = s
807 .checked_mul(h as usize - 1)
808 .and_then(|body| body.checked_add(off))
809 .and_then(|n| n.checked_add(row_bytes))?;
810 if needed > inner.size {
811 return None;
812 }
813 let mut bgra = if s == row_bytes && off == 0 {
814 let total = row_bytes * h as usize;
815 unsafe { std::slice::from_raw_parts(inner.mmap_ptr, total) }.to_vec()
816 } else {
817 let mut packed = Vec::with_capacity(row_bytes * h as usize);
818 for row in 0..h as usize {
819 let src = unsafe {
820 std::slice::from_raw_parts(inner.mmap_ptr.add(off + row * s), row_bytes)
821 };
822 packed.extend_from_slice(src);
823 }
824 packed
825 };
826 if matches!(format, wl_shm::Format::Xrgb8888 | wl_shm::Format::Xbgr8888) {
827 for px in bgra.chunks_exact_mut(4) {
828 px[3] = 255;
829 }
830 }
831 if matches!(format, wl_shm::Format::Abgr8888 | wl_shm::Format::Xbgr8888) {
832 Some((w, h, PixelData::Rgba(Arc::new(bgra))))
833 } else {
834 Some((w, h, PixelData::Bgra(Arc::new(bgra))))
835 }
836 }
837}
838
839impl Drop for ShmPool {
840 fn drop(&mut self) {
841 let inner = self.inner.get_mut().unwrap();
842 if !inner.mmap_ptr.is_null() {
843 unsafe {
844 libc::munmap(inner.mmap_ptr as *mut _, inner.size);
845 }
846 }
847 }
848}
849
850unsafe impl Send for ShmPool {}
851
852struct ShmBufferData {
853 pool: Arc<ShmPool>,
859 offset: i32,
860 width: i32,
861 height: i32,
862 stride: i32,
863 format: wl_shm::Format,
864}
865
866struct DmaBufBufferData {
867 width: i32,
868 height: i32,
869 fourcc: u32,
870 modifier: u64,
871 planes: Vec<DmaBufPlane>,
872 y_invert: bool,
873}
874
875struct DmaBufPlane {
876 fd: OwnedFd,
877 offset: u32,
878 stride: u32,
879}
880
881struct DmaBufParamsPending {
882 resource: ZwpLinuxBufferParamsV1,
883 planes: Vec<DmaBufPlane>,
884 modifier: u64,
885}
886
887struct ClientState;
888struct XdgSurfaceData {
889 wl_surface_id: ObjectId,
890}
891struct XdgToplevelData {
892 wl_surface_id: ObjectId,
893}
894struct XdgPopupData {
895 wl_surface_id: ObjectId,
896}
897struct SubsurfaceData {
898 wl_surface_id: ObjectId,
899 parent_surface_id: ObjectId,
900}
901
902struct DataSourceData {
905 mime_types: std::sync::Mutex<Vec<String>>,
906}
907
908struct DataOfferData {
909 external: bool,
913}
914
915struct ExternalClipboard {
917 mime_type: String,
918 data: Vec<u8>,
919}
920
921struct PrimarySourceData {
922 mime_types: std::sync::Mutex<Vec<String>>,
923}
924struct PrimaryOfferData {
925 external: bool,
926}
927
928struct ActivationTokenData {
930 serial: u32,
931}
932
933struct PositionerState {
934 resource: XdgPositioner,
935 geometry: PositionerGeometry,
936}
937
938fn char_to_keycode(ch: char) -> Option<(u32, bool)> {
946 const KEY_1: u32 = 2;
947 const KEY_2: u32 = 3;
948 const KEY_3: u32 = 4;
949 const KEY_4: u32 = 5;
950 const KEY_5: u32 = 6;
951 const KEY_6: u32 = 7;
952 const KEY_7: u32 = 8;
953 const KEY_8: u32 = 9;
954 const KEY_9: u32 = 10;
955 const KEY_0: u32 = 11;
956 const KEY_MINUS: u32 = 12;
957 const KEY_EQUAL: u32 = 13;
958 const KEY_TAB: u32 = 15;
959 const KEY_Q: u32 = 16;
960 const KEY_W: u32 = 17;
961 const KEY_E: u32 = 18;
962 const KEY_R: u32 = 19;
963 const KEY_T: u32 = 20;
964 const KEY_Y: u32 = 21;
965 const KEY_U: u32 = 22;
966 const KEY_I: u32 = 23;
967 const KEY_O: u32 = 24;
968 const KEY_P: u32 = 25;
969 const KEY_LEFTBRACE: u32 = 26;
970 const KEY_RIGHTBRACE: u32 = 27;
971 const KEY_ENTER: u32 = 28;
972 const KEY_A: u32 = 30;
973 const KEY_S: u32 = 31;
974 const KEY_D: u32 = 32;
975 const KEY_F: u32 = 33;
976 const KEY_G: u32 = 34;
977 const KEY_H: u32 = 35;
978 const KEY_J: u32 = 36;
979 const KEY_K: u32 = 37;
980 const KEY_L: u32 = 38;
981 const KEY_SEMICOLON: u32 = 39;
982 const KEY_APOSTROPHE: u32 = 40;
983 const KEY_GRAVE: u32 = 41;
984 const KEY_BACKSLASH: u32 = 43;
985 const KEY_Z: u32 = 44;
986 const KEY_X: u32 = 45;
987 const KEY_C: u32 = 46;
988 const KEY_V: u32 = 47;
989 const KEY_B: u32 = 48;
990 const KEY_N: u32 = 49;
991 const KEY_M: u32 = 50;
992 const KEY_COMMA: u32 = 51;
993 const KEY_DOT: u32 = 52;
994 const KEY_SLASH: u32 = 53;
995 const KEY_SPACE: u32 = 57;
996
997 fn letter_kc(ch: char) -> u32 {
998 match ch {
999 'a' => KEY_A,
1000 'b' => KEY_B,
1001 'c' => KEY_C,
1002 'd' => KEY_D,
1003 'e' => KEY_E,
1004 'f' => KEY_F,
1005 'g' => KEY_G,
1006 'h' => KEY_H,
1007 'i' => KEY_I,
1008 'j' => KEY_J,
1009 'k' => KEY_K,
1010 'l' => KEY_L,
1011 'm' => KEY_M,
1012 'n' => KEY_N,
1013 'o' => KEY_O,
1014 'p' => KEY_P,
1015 'q' => KEY_Q,
1016 'r' => KEY_R,
1017 's' => KEY_S,
1018 't' => KEY_T,
1019 'u' => KEY_U,
1020 'v' => KEY_V,
1021 'w' => KEY_W,
1022 'x' => KEY_X,
1023 'y' => KEY_Y,
1024 'z' => KEY_Z,
1025 _ => KEY_SPACE,
1026 }
1027 }
1028
1029 let (kc, shift) = match ch {
1030 'a'..='z' => (letter_kc(ch), false),
1031 'A'..='Z' => (letter_kc(ch.to_ascii_lowercase()), true),
1032 '0' => (KEY_0, false),
1033 '1'..='9' => (KEY_1 + (ch as u32 - '1' as u32), false),
1034 ' ' => (KEY_SPACE, false),
1035 '-' => (KEY_MINUS, false),
1036 '=' => (KEY_EQUAL, false),
1037 '[' => (KEY_LEFTBRACE, false),
1038 ']' => (KEY_RIGHTBRACE, false),
1039 ';' => (KEY_SEMICOLON, false),
1040 '\'' => (KEY_APOSTROPHE, false),
1041 ',' => (KEY_COMMA, false),
1042 '.' => (KEY_DOT, false),
1043 '/' => (KEY_SLASH, false),
1044 '\\' => (KEY_BACKSLASH, false),
1045 '`' => (KEY_GRAVE, false),
1046 '\t' => (KEY_TAB, false),
1047 '\n' => (KEY_ENTER, false),
1048 '!' => (KEY_1, true),
1049 '@' => (KEY_2, true),
1050 '#' => (KEY_3, true),
1051 '$' => (KEY_4, true),
1052 '%' => (KEY_5, true),
1053 '^' => (KEY_6, true),
1054 '&' => (KEY_7, true),
1055 '*' => (KEY_8, true),
1056 '(' => (KEY_9, true),
1057 ')' => (KEY_0, true),
1058 '_' => (KEY_MINUS, true),
1059 '+' => (KEY_EQUAL, true),
1060 '{' => (KEY_LEFTBRACE, true),
1061 '}' => (KEY_RIGHTBRACE, true),
1062 ':' => (KEY_SEMICOLON, true),
1063 '"' => (KEY_APOSTROPHE, true),
1064 '<' => (KEY_COMMA, true),
1065 '>' => (KEY_DOT, true),
1066 '?' => (KEY_SLASH, true),
1067 '|' => (KEY_BACKSLASH, true),
1068 '~' => (KEY_GRAVE, true),
1069 _ => return None,
1070 };
1071 Some((kc, shift))
1072}
1073
1074const MOD_SHIFT: u32 = 1 << 0;
1080const MOD_LOCK: u32 = 1 << 1;
1081const MOD_CONTROL: u32 = 1 << 2;
1082const MOD_MOD1: u32 = 1 << 3; const MOD_MOD4: u32 = 1 << 6; fn keycode_to_mod(keycode: u32) -> u32 {
1088 match keycode {
1089 42 | 54 => MOD_SHIFT, 58 => MOD_LOCK, 29 | 97 => MOD_CONTROL, 56 | 100 => MOD_MOD1, 125 | 126 => MOD_MOD4, _ => 0,
1095 }
1096}
1097
1098struct TextInputState {
1100 resource: ZwpTextInputV3,
1101 enabled: bool,
1103}
1104
1105struct Compositor {
1107 display_handle: DisplayHandle,
1108 surfaces: HashMap<ObjectId, Surface>,
1109 regions: HashMap<ObjectId, (WlRegion, Vec<RegionOp>)>,
1114 toplevel_surface_ids: HashMap<u16, ObjectId>,
1115 last_request_frame_ms: HashMap<u16, u32>,
1123 next_surface_id: u16,
1124 shm_pools: HashMap<ObjectId, Arc<ShmPool>>,
1125 surface_meta: HashMap<ObjectId, super::render::SurfaceMeta>,
1129 dmabuf_params: HashMap<ObjectId, DmaBufParamsPending>,
1130 vulkan_renderer: Option<super::vulkan_render::VulkanRenderer>,
1131 output_width: i32,
1132 output_height: i32,
1133 output_refresh_mhz: u32,
1136 output_scale_120: u16,
1140 outputs: Vec<WlOutput>,
1141 keyboards: Vec<WlKeyboard>,
1142 pointers: Vec<WlPointer>,
1143 keyboard_keymap_data: Vec<u8>,
1144 mods_depressed: u32,
1146 mods_locked: u32,
1148 serial: u32,
1149 event_tx: mpsc::Sender<CompositorEvent>,
1150 event_notify: Arc<dyn Fn() + Send + Sync>,
1151 loop_signal: LoopSignal,
1152 pending_commits: HashMap<(u16, u32, u32), (u32, u32, PixelData)>,
1159 pending_native_sizes: HashMap<u16, (u32, u32, u32, u32)>,
1166 pending_recomposite_toplevels: HashSet<u16>,
1177 focused_surface_id: u16,
1178 pointer_entered_id: Option<ObjectId>,
1180 pointer_entered_local: (f64, f64),
1183 pending_kb_reenter: bool,
1187
1188 gpu_device: String,
1189 verbose: bool,
1190 shutdown: Arc<AtomicBool>,
1191 last_reported_size: HashMap<u16, (u32, u32, u32, u32)>,
1195 surface_sizes: HashMap<u16, (i32, i32)>,
1199 positioners: HashMap<ObjectId, PositionerState>,
1201 fractional_scales: Vec<WpFractionalScaleV1>,
1204
1205 data_devices: Vec<WlDataDevice>,
1208 selection_source: Option<WlDataSource>,
1211 external_clipboard: Option<ExternalClipboard>,
1213
1214 primary_devices: Vec<ZwpPrimarySelectionDeviceV1>,
1216 primary_source: Option<ZwpPrimarySelectionSourceV1>,
1217 external_primary: Option<ExternalClipboard>,
1218
1219 relative_pointers: Vec<ZwpRelativePointerV1>,
1221
1222 text_inputs: Vec<TextInputState>,
1227 #[expect(dead_code)]
1230 text_input_serial: u32,
1231
1232 next_activation_token: u32,
1234
1235 popup_grab_stack: Vec<ObjectId>,
1240 popup_dismiss_button: Option<u32>,
1244 kb_focus_popup: Option<ObjectId>,
1254
1255 held_buffers: HashMap<ObjectId, WlBuffer>,
1262
1263 cursor_rgba: HashMap<ObjectId, (u32, u32, Vec<u8>)>,
1268}
1269
1270impl Compositor {
1271 fn next_serial(&mut self) -> u32 {
1272 self.serial = self.serial.wrapping_add(1);
1273 self.serial
1274 }
1275
1276 fn update_and_send_modifiers(&mut self, keycode: u32, pressed: bool) {
1281 let m = keycode_to_mod(keycode);
1282 if m == 0 {
1283 return;
1284 }
1285 if keycode == 58 {
1286 if pressed {
1288 self.mods_locked ^= MOD_LOCK;
1289 }
1290 } else if pressed {
1291 self.mods_depressed |= m;
1292 } else {
1293 self.mods_depressed &= !m;
1294 }
1295 let serial = self.next_serial();
1296 let focused_wl = self
1297 .toplevel_surface_ids
1298 .get(&self.focused_surface_id)
1299 .and_then(|root_id| self.surfaces.get(root_id))
1300 .map(|s| s.wl_surface.clone());
1301 for kb in &self.keyboards {
1302 if let Some(ref wl) = focused_wl
1303 && same_client(kb, wl)
1304 {
1305 kb.modifiers(serial, self.mods_depressed, 0, self.mods_locked, 0);
1306 }
1307 }
1308 }
1309
1310 fn keyboard_focus_wl(&self) -> Option<WlSurface> {
1315 if let Some(ref popup_id) = self.kb_focus_popup
1316 && let Some(surf) = self.surfaces.get(popup_id)
1317 {
1318 return Some(surf.wl_surface.clone());
1319 }
1320 self.toplevel_surface_ids
1321 .get(&self.focused_surface_id)
1322 .and_then(|root| self.surfaces.get(root))
1323 .map(|s| s.wl_surface.clone())
1324 }
1325
1326 fn send_keyboard_leave(&mut self, wl: &WlSurface) {
1328 let serial = self.next_serial();
1329 for kb in &self.keyboards {
1330 if same_client(kb, wl) {
1331 kb.leave(serial, wl);
1332 }
1333 }
1334 for ti in &self.text_inputs {
1335 if same_client(&ti.resource, wl) {
1336 ti.resource.leave(wl);
1337 }
1338 }
1339 }
1340
1341 fn send_keyboard_enter(&mut self, wl: &WlSurface) {
1343 let serial = self.next_serial();
1344 for kb in &self.keyboards {
1345 if same_client(kb, wl) {
1346 kb.enter(serial, wl, vec![]);
1347 }
1348 }
1349 for ti in &self.text_inputs {
1350 if same_client(&ti.resource, wl) {
1351 ti.resource.enter(wl);
1352 }
1353 }
1354 }
1355
1356 fn focus_popup(&mut self, popup_id: &ObjectId) {
1363 if self.kb_focus_popup.as_ref() == Some(popup_id) {
1364 return;
1365 }
1366 let Some(popup_wl) = self.surfaces.get(popup_id).map(|s| s.wl_surface.clone()) else {
1367 return;
1368 };
1369 if let Some(previous) = self.keyboard_focus_wl() {
1370 self.send_keyboard_leave(&previous);
1371 }
1372 self.kb_focus_popup = Some(popup_id.clone());
1373 self.send_keyboard_enter(&popup_wl);
1374 let _ = self.display_handle.flush_clients();
1375 }
1376
1377 fn unfocus_popup(&mut self, popup_id: &ObjectId) {
1384 let Some(next_holder) = keyboard_focus_after_popup_close(
1387 popup_id,
1388 self.kb_focus_popup.as_ref(),
1389 &self.popup_grab_stack,
1390 ) else {
1391 return;
1392 };
1393 if let Some(going) = self.surfaces.get(popup_id).map(|s| s.wl_surface.clone()) {
1394 self.send_keyboard_leave(&going);
1395 }
1396 self.kb_focus_popup = next_holder;
1397 if let Some(next) = self.keyboard_focus_wl() {
1398 self.send_keyboard_enter(&next);
1399 }
1400 let _ = self.display_handle.flush_clients();
1401 }
1402
1403 fn set_keyboard_focus(&mut self, new_surface_id: u16) {
1408 let old_id = self.focused_surface_id;
1409 if old_id == new_surface_id {
1410 return;
1427 }
1428
1429 if let Some(old_wl) = self.keyboard_focus_wl()
1434 && (old_id != 0 || self.kb_focus_popup.is_some())
1435 {
1436 self.send_keyboard_leave(&old_wl);
1437 }
1438 self.kb_focus_popup = None;
1440
1441 self.focused_surface_id = new_surface_id;
1442
1443 if let Some(root_id) = self.toplevel_surface_ids.get(&new_surface_id)
1445 && let Some(wl_surface) = self.surfaces.get(root_id).map(|s| s.wl_surface.clone())
1446 {
1447 self.send_keyboard_enter(&wl_surface);
1448 }
1449 }
1450
1451 fn allocate_surface_id(&mut self) -> u16 {
1452 let mut id = self.next_surface_id;
1453 let start = id;
1454 loop {
1455 if !self.toplevel_surface_ids.contains_key(&id) {
1456 break;
1457 }
1458 id = id.wrapping_add(1);
1459 if id == 0 {
1460 id = 1;
1461 }
1462 if id == start {
1463 break;
1464 }
1465 }
1466 self.next_surface_id = id.wrapping_add(1);
1467 if self.next_surface_id == 0 {
1468 self.next_surface_id = 1;
1469 }
1470 id
1471 }
1472
1473 fn flush_pending_commits(&mut self) {
1474 for (surface_id, (width, height, log_w, log_h)) in self.pending_native_sizes.drain() {
1481 let prev = self.last_reported_size.get(&surface_id).copied();
1482 self.last_reported_size
1493 .insert(surface_id, (width, height, log_w, log_h));
1494 if prev.map(|(pw, ph, _, _)| (pw, ph)) != Some((width, height)) {
1498 let _ = self.event_tx.send(CompositorEvent::SurfaceResized {
1499 surface_id,
1500 width: width as u16,
1501 height: height as u16,
1502 });
1503 }
1504 }
1505 let now_ms = elapsed_ms();
1508 #[allow(clippy::type_complexity)]
1509 let mut entries: Vec<((u16, u32, u32), (u32, u32, PixelData))> =
1510 self.pending_commits.drain().collect();
1511 entries.sort_by_key(|((sid, w, h), _)| (*sid, *w, *h));
1512 for ((surface_id, width, height), (_log_w, _log_h, pixels)) in entries {
1513 let _ = self.event_tx.send(CompositorEvent::SurfaceCommit {
1514 surface_id,
1515 width,
1516 height,
1517 pixels,
1518 timestamp_ms: now_ms,
1519 });
1520 }
1521 (self.event_notify)();
1522 }
1523
1524 fn read_shm_buffer(&self, buffer: &WlBuffer) -> Option<(u32, u32, PixelData)> {
1525 let data = buffer.data::<ShmBufferData>()?;
1526 let r = data.pool.read_buffer(
1527 data.offset,
1528 data.width,
1529 data.height,
1530 data.stride,
1531 data.format,
1532 );
1533 if r.is_none() {
1534 static N: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1535 let n = N.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1536 if n < 10 || n.is_multiple_of(100) {
1537 eprintln!(
1538 "[read_shm_buffer #{n}] pool.read_buffer=None off={} {}x{} stride={} fmt={:?}",
1539 data.offset, data.width, data.height, data.stride, data.format,
1540 );
1541 }
1542 }
1543 r
1544 }
1545
1546 fn read_dmabuf_buffer(&self, buffer: &WlBuffer) -> Option<(u32, u32, PixelData)> {
1547 let data = buffer.data::<DmaBufBufferData>()?;
1548 let width = data.width as u32;
1549 let height = data.height as u32;
1550 if width == 0 || height == 0 || data.planes.is_empty() {
1551 static N: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1552 let n = N.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1553 if n < 10 || n.is_multiple_of(100) {
1554 eprintln!(
1555 "[read_dmabuf_buffer #{n}] empty: {}x{} planes={}",
1556 width,
1557 height,
1558 data.planes.len()
1559 );
1560 }
1561 return None;
1562 }
1563 let plane = &data.planes[0];
1564 if matches!(
1565 data.fourcc,
1566 drm_fourcc::ARGB8888
1567 | drm_fourcc::XRGB8888
1568 | drm_fourcc::ABGR8888
1569 | drm_fourcc::XBGR8888
1570 ) {
1571 use std::os::fd::AsRawFd;
1574 let raw_fd = plane.fd.as_raw_fd();
1575 let _is_drm = {
1576 let mut link_buf = [0u8; 256];
1577 let path = format!("/proc/self/fd/{raw_fd}\0");
1578 let n = unsafe {
1579 libc::readlink(
1580 path.as_ptr() as *const _,
1581 link_buf.as_mut_ptr() as *mut _,
1582 255,
1583 )
1584 };
1585 n > 0 && link_buf[..n as usize].starts_with(b"/dev/dri/")
1586 };
1587
1588 let owned = plane.fd.try_clone().ok()?;
1592 return Some((
1593 width,
1594 height,
1595 PixelData::DmaBuf {
1596 fd: Arc::new(owned),
1597 fourcc: data.fourcc,
1598 modifier: data.modifier,
1599 stride: plane.stride,
1600 offset: plane.offset,
1601 y_invert: data.y_invert,
1602 },
1603 ));
1604 }
1605 static N: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1606 let n = N.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1607 if n < 10 || n.is_multiple_of(100) {
1608 eprintln!(
1609 "[read_dmabuf_buffer #{n}] unsupported fourcc=0x{:08x} ({}x{}) modifier=0x{:x}",
1610 data.fourcc, width, height, data.modifier,
1611 );
1612 }
1613 None
1614 }
1615
1616 fn read_buffer(&self, buffer: &WlBuffer) -> Option<(u32, u32, PixelData)> {
1617 if buffer.data::<ShmBufferData>().is_some() {
1621 return self.read_shm_buffer(buffer);
1622 }
1623 if buffer.data::<DmaBufBufferData>().is_some() {
1624 return self.read_dmabuf_buffer(buffer);
1625 }
1626 static N: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1627 let n = N.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1628 if n < 10 || n.is_multiple_of(100) {
1629 eprintln!(
1630 "[read_buffer #{n}] buffer has unknown role (neither Shm nor DmaBuf data attached)",
1631 );
1632 }
1633 None
1634 }
1635
1636 fn handle_surface_commit(&mut self, surface_id: &ObjectId) {
1637 let (root_id, toplevel_sid) = self.find_toplevel_root(surface_id);
1638
1639 let had_buffer = self
1644 .surfaces
1645 .get(surface_id)
1646 .is_some_and(|s| s.pending_buffer.is_some());
1647 {
1648 static N: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1649 let n = N.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1650 if n < 40 || n.is_multiple_of(200) {
1651 let children = self
1652 .surfaces
1653 .get(surface_id)
1654 .map(|s| s.children.len())
1655 .unwrap_or(0);
1656 eprintln!(
1657 "[commit-in #{n}] sid={surface_id:?} toplevel={toplevel_sid:?} root={root_id:?} had_buffer={had_buffer} children={children}",
1658 );
1659 }
1660 }
1661 self.apply_pending_state(surface_id);
1662
1663 let toplevel_sid = match toplevel_sid {
1664 Some(sid) => sid,
1665 None => {
1666 if let Some(held) = self.held_buffers.remove(surface_id) {
1669 held.release();
1670 }
1671 self.fire_surface_frame_callbacks(surface_id);
1674 let _ = self.display_handle.flush_clients();
1675 return;
1676 }
1677 };
1678
1679 self.composite_toplevel_into_pending(&root_id, toplevel_sid);
1680
1681 if let Some(held) = self.held_buffers.remove(surface_id) {
1686 held.release();
1687 }
1688
1689 const PACING_GRACE_MS: u32 = 500;
1704 let paced = self
1705 .last_request_frame_ms
1706 .get(&toplevel_sid)
1707 .is_some_and(|&t| elapsed_ms().wrapping_sub(t) < PACING_GRACE_MS);
1708 if !paced {
1709 self.fire_frame_callbacks_for_toplevel(toplevel_sid);
1710 }
1711
1712 if self.pending_kb_reenter {
1717 self.pending_kb_reenter = false;
1718 if let Some(wl) = self.keyboard_focus_wl() {
1730 self.send_keyboard_leave(&wl);
1731 self.send_keyboard_enter(&wl);
1732 }
1733 let _ = self.display_handle.flush_clients();
1734 }
1735
1736 if self.verbose {
1737 let cache_entries = self.surface_meta.len();
1738 let has_pending = self
1739 .pending_commits
1740 .keys()
1741 .any(|(sid, _, _)| *sid == toplevel_sid);
1742 static COMMIT_COUNT: std::sync::atomic::AtomicU64 =
1743 std::sync::atomic::AtomicU64::new(0);
1744 let n = COMMIT_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1745 if n < 5 || n.is_multiple_of(1000) {
1746 eprintln!(
1747 "[commit #{n}] sid={surface_id:?} root={root_id:?} cache={cache_entries} pending={has_pending} buf={had_buffer}",
1748 );
1749 }
1750 }
1751 }
1752
1753 fn composite_toplevel_into_pending(&mut self, root_id: &ObjectId, toplevel_sid: u16) {
1763 let s120 = self.output_scale_120;
1767 let target_phys = self.surface_sizes.get(&toplevel_sid).map(|&(lw, lh)| {
1768 let pw = super::render::to_physical(lw as u32, s120 as u32);
1769 let ph = super::render::to_physical(lh as u32, s120 as u32);
1770 (pw, ph)
1771 });
1772 let composited = if let Some(ref mut vk) = self.vulkan_renderer {
1773 vk.render_tree_sized(
1774 root_id,
1775 &self.surfaces,
1776 &self.surface_meta,
1777 s120,
1778 target_phys,
1779 toplevel_sid,
1780 )
1781 } else {
1782 Vec::new()
1783 };
1784
1785 let s120_u32 = (s120 as u32).max(120);
1792 if let Some((nw, nh)) = target_phys {
1793 let nlog_w = (nw * 120).div_ceil(s120_u32);
1794 let nlog_h = (nh * 120).div_ceil(s120_u32);
1795 self.pending_native_sizes
1796 .insert(toplevel_sid, (nw, nh, nlog_w, nlog_h));
1797 } else if let Some((sid, nw, nh, _)) = composited
1798 .iter()
1799 .max_by_key(|(_, w, h, _)| (*w as u64) * (*h as u64))
1800 {
1801 let nlog_w = (nw * 120).div_ceil(s120_u32);
1802 let nlog_h = (nh * 120).div_ceil(s120_u32);
1803 self.pending_native_sizes
1804 .insert(*sid, (*nw, *nh, nlog_w, nlog_h));
1805 }
1806
1807 for (result_sid, w, h, pixels) in composited {
1808 if pixels.is_empty() {
1809 continue;
1810 }
1811 let kind = match &pixels {
1812 PixelData::Bgra(_) => "bgra",
1813 PixelData::Rgba(_) => "rgba",
1814 PixelData::Nv12 { .. } => "nv12",
1815 PixelData::VaSurface { .. } => "va-surface",
1816 PixelData::Nv12DmaBuf { .. } => "nv12-dmabuf",
1817 PixelData::Encoded { .. } => "vulkan-encoded",
1818 PixelData::DmaBuf { fd, .. } => {
1819 use std::os::fd::AsRawFd;
1820 let raw = fd.as_raw_fd();
1821 let mut lb = [0u8; 128];
1822 let p = format!("/proc/self/fd/{raw}\0");
1823 let n = unsafe {
1824 libc::readlink(p.as_ptr() as *const _, lb.as_mut_ptr() as *mut _, 127)
1825 };
1826 if n > 0 && lb[..n as usize].starts_with(b"/dev/dri/") {
1827 "dmabuf-drm"
1828 } else {
1829 "dmabuf-anon"
1830 }
1831 }
1832 };
1833 if self.verbose {
1834 static LC: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1835 let lc = LC.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1836 if lc < 3 || lc.is_multiple_of(1000) {
1837 eprintln!("[pending #{lc}] {w}x{h} kind={kind}");
1838 }
1839 }
1840 let log_w = (w * 120).div_ceil(s120_u32);
1845 let log_h = (h * 120).div_ceil(s120_u32);
1846 self.pending_commits
1847 .insert((result_sid, w, h), (log_w, log_h, pixels));
1848 }
1849 }
1850
1851 fn surface_absolute_position(&self, surface_id: &ObjectId) -> (i32, i32) {
1855 let mut x = 0i32;
1856 let mut y = 0i32;
1857 let mut current = surface_id.clone();
1858 while let Some(surf) = self.surfaces.get(¤t) {
1859 x += surf.subsurface_position.0;
1860 y += surf.subsurface_position.1;
1861 match surf.parent_surface_id {
1862 Some(ref parent) => current = parent.clone(),
1863 None => break,
1864 }
1865 }
1866 (x, y)
1867 }
1868
1869 fn find_toplevel_root(&self, surface_id: &ObjectId) -> (ObjectId, Option<u16>) {
1870 let mut current = surface_id.clone();
1871 loop {
1872 match self.surfaces.get(¤t) {
1873 Some(surf) => {
1874 if let Some(ref parent) = surf.parent_surface_id {
1875 current = parent.clone();
1876 } else {
1877 return (
1878 current,
1879 if surf.surface_id > 0 {
1880 Some(surf.surface_id)
1881 } else {
1882 None
1883 },
1884 );
1885 }
1886 }
1887 None => return (current, None),
1888 }
1889 }
1890 }
1891
1892 fn collect_surface_tree(&self, root_id: &ObjectId) -> Vec<ObjectId> {
1893 let mut result = Vec::new();
1894 self.collect_tree_recursive(root_id, &mut result);
1895 result
1896 }
1897
1898 fn collect_tree_recursive(&self, surface_id: &ObjectId, result: &mut Vec<ObjectId>) {
1899 result.push(surface_id.clone());
1900 if let Some(surf) = self.surfaces.get(surface_id) {
1901 for child_id in &surf.children {
1902 self.collect_tree_recursive(child_id, result);
1903 }
1904 }
1905 }
1906
1907 fn hit_test_surface_at(
1912 &self,
1913 root_id: &ObjectId,
1914 x: f64,
1915 y: f64,
1916 ) -> Option<(WlSurface, f64, f64)> {
1917 self.hit_test_recursive(root_id, x, y, 0, 0).or_else(|| {
1918 self.surfaces
1920 .get(root_id)
1921 .map(|s| (s.wl_surface.clone(), x, y))
1922 })
1923 }
1924
1925 fn hit_test_recursive(
1926 &self,
1927 surface_id: &ObjectId,
1928 x: f64,
1929 y: f64,
1930 offset_x: i32,
1931 offset_y: i32,
1932 ) -> Option<(WlSurface, f64, f64)> {
1933 let surf = self.surfaces.get(surface_id)?;
1934 let sx = offset_x + surf.subsurface_position.0;
1935 let sy = offset_y + surf.subsurface_position.1;
1936
1937 for child_id in surf.children.iter().rev() {
1939 if let Some(hit) = self.hit_test_recursive(child_id, x, y, sx, sy) {
1940 return Some(hit);
1941 }
1942 }
1943
1944 if let Some(sm) = self.surface_meta.get(surface_id) {
1946 let s = sm.scale.max(1) as f64;
1947 let (w, h) = (sm.width, sm.height);
1948 let (lw, lh) = surf
1951 .viewport_destination
1952 .filter(|&(dw, dh)| dw > 0 && dh > 0)
1953 .map(|(dw, dh)| (dw as f64, dh as f64))
1954 .unwrap_or((w as f64 / s, h as f64 / s));
1955 let lx = x - sx as f64;
1956 let ly = y - sy as f64;
1957 if lx >= 0.0 && ly >= 0.0 && lx < lw && ly < lh {
1958 match surf.input_region {
1965 Some(ref ops) if !input_region::contains(ops, lx, ly) => {}
1966 _ => return Some((surf.wl_surface.clone(), lx, ly)),
1967 }
1968 }
1969 }
1970 None
1971 }
1972
1973 fn apply_pending_state(&mut self, surface_id: &ObjectId) {
1986 let (buffer, scale, is_cursor) = {
1987 let Some(surf) = self.surfaces.get_mut(surface_id) else {
1988 return;
1989 };
1990 let buffer = surf.pending_buffer.take();
1991 let scale = surf.pending_buffer_scale;
1992 surf.buffer_scale = scale;
1993 surf.viewport_destination = surf.pending_viewport_destination;
1994 surf.is_opaque = surf.pending_opaque;
1995 if let Some(region) = surf.pending_input_region.take() {
1996 surf.input_region = region;
1997 }
1998 surf.pending_damage = false;
1999 if let Some(pos) = surf.pending_subsurface_position.take() {
2000 surf.subsurface_position = pos;
2001 }
2002 (buffer, scale, surf.is_cursor)
2003 };
2004 let Some(buf) = buffer else { return };
2005
2006 if let Some(old) = self.held_buffers.remove(surface_id) {
2009 old.release();
2010 }
2011
2012 if !is_cursor && let Some(shm) = buf.data::<ShmBufferData>() {
2018 let w = shm.width as u32;
2019 let h = shm.height as u32;
2020 let stride = shm.stride as usize;
2021 let offset = shm.offset as usize;
2022 let format = shm.format;
2023 if shm.width > 0
2027 && shm.height > 0
2028 && shm.stride >= 0
2029 && shm.offset >= 0
2030 && let Some(ref mut vk) = self.vulkan_renderer
2031 {
2032 let swap_rb =
2033 !matches!(format, wl_shm::Format::Abgr8888 | wl_shm::Format::Xbgr8888);
2034 let force_opaque =
2035 matches!(format, wl_shm::Format::Xrgb8888 | wl_shm::Format::Xbgr8888);
2036 let row_bytes = w as usize * 4;
2037 let uploaded = shm
2038 .pool
2039 .with_mmap(|slice| {
2040 let needed = stride
2044 .checked_mul(h as usize - 1)
2045 .and_then(|body| body.checked_add(offset))
2046 .and_then(|n| n.checked_add(row_bytes));
2047 match needed {
2048 Some(n) if n <= slice.len() => {}
2049 _ => return false,
2050 }
2051 vk.upload_surface_shm_mmap(
2052 surface_id,
2053 slice,
2054 offset,
2055 stride,
2056 w,
2057 h,
2058 swap_rb,
2059 force_opaque,
2060 )
2061 })
2062 .unwrap_or(false);
2063 if uploaded {
2064 self.surface_meta.insert(
2065 surface_id.clone(),
2066 super::render::SurfaceMeta {
2067 width: w,
2068 height: h,
2069 scale,
2070 y_invert: false,
2071 },
2072 );
2073 buf.release();
2074 return;
2075 }
2076 }
2077 }
2078
2079 if let Some((w, h, pixels)) = self.read_buffer(&buf) {
2080 let y_invert = matches!(pixels, PixelData::DmaBuf { y_invert: true, .. });
2081
2082 if let Some(ref mut vk) = self.vulkan_renderer {
2084 vk.upload_surface(surface_id, &pixels, w, h);
2085 }
2086
2087 self.surface_meta.insert(
2089 surface_id.clone(),
2090 super::render::SurfaceMeta {
2091 width: w,
2092 height: h,
2093 scale,
2094 y_invert,
2095 },
2096 );
2097
2098 if is_cursor {
2101 let rgba = pixels.to_rgba(w, h);
2102 if !rgba.is_empty() {
2103 self.cursor_rgba.insert(surface_id.clone(), (w, h, rgba));
2104 }
2105 }
2106
2107 if pixels.is_dmabuf() {
2108 self.held_buffers.insert(surface_id.clone(), buf);
2111 } else {
2112 buf.release();
2115 }
2116 } else {
2117 buf.release();
2118 }
2119 }
2120
2121 fn fire_surface_frame_callbacks(&mut self, surface_id: &ObjectId) {
2122 let (callbacks, feedbacks) = {
2123 let Some(surf) = self.surfaces.get_mut(surface_id) else {
2124 return;
2125 };
2126 (
2127 std::mem::take(&mut surf.pending_frame_callbacks),
2128 std::mem::take(&mut surf.pending_presentation_feedbacks),
2129 )
2130 };
2131 let time = elapsed_ms();
2132 for cb in callbacks {
2133 cb.done(time);
2134 }
2135 if !feedbacks.is_empty() {
2136 let (sec, nsec) = monotonic_timespec();
2137 for fb in feedbacks {
2140 for output in &self.outputs {
2141 if same_client(&fb, output) {
2142 fb.sync_output(output);
2143 }
2144 }
2145 let refresh_ns = if self.output_refresh_mhz > 0 {
2147 (1_000_000_000_000u64 / self.output_refresh_mhz as u64) as u32
2148 } else {
2149 0
2150 };
2151 fb.presented(
2152 (sec >> 32) as u32,
2153 sec as u32,
2154 nsec as u32,
2155 refresh_ns,
2156 0, 0, WpPresentationFeedbackKind::empty(),
2159 );
2160 }
2161 }
2162 }
2163
2164 fn forget_pointer_focus(&mut self, gone: &ObjectId) {
2178 if self.pointer_entered_id.as_ref() == Some(gone) {
2179 self.pointer_entered_id = None;
2180 }
2181 }
2182
2183 fn cleanup_dead_surfaces(&mut self) {
2188 self.fractional_scales.retain(|fs| fs.is_alive());
2190 self.outputs.retain(|o| o.is_alive());
2191 self.keyboards.retain(|k| k.is_alive());
2192 self.pointers.retain(|p| p.is_alive());
2193 self.data_devices.retain(|d| d.is_alive());
2194 self.primary_devices.retain(|d| d.is_alive());
2195 self.relative_pointers.retain(|p| p.is_alive());
2196 self.text_inputs.retain(|ti| ti.resource.is_alive());
2197 self.shm_pools.retain(|_, p| p.resource.is_alive());
2198 self.dmabuf_params.retain(|_, p| p.resource.is_alive());
2199 self.positioners.retain(|_, p| p.resource.is_alive());
2200 self.regions.retain(|_, (r, _)| r.is_alive());
2203
2204 let dead: Vec<ObjectId> = self
2205 .surfaces
2206 .iter()
2207 .filter(|(_, surf)| !surf.wl_surface.is_alive())
2208 .map(|(id, _)| id.clone())
2209 .collect();
2210
2211 for proto_id in &dead {
2212 self.surface_meta.remove(proto_id);
2213 if let Some(ref mut vk) = self.vulkan_renderer {
2214 vk.remove_surface(proto_id);
2215 }
2216 if let Some(held) = self.held_buffers.remove(proto_id) {
2217 held.release();
2218 }
2219 self.forget_pointer_focus(proto_id);
2220 if self.kb_focus_popup.as_ref() == Some(proto_id) {
2224 self.popup_grab_stack.retain(|id| id != proto_id);
2225 self.kb_focus_popup = self.popup_grab_stack.last().cloned();
2226 if let Some(next) = self.keyboard_focus_wl() {
2227 self.send_keyboard_enter(&next);
2228 }
2229 }
2230 if let Some(surf) = self.surfaces.remove(proto_id) {
2231 for fb in surf.pending_presentation_feedbacks {
2234 fb.discarded();
2235 }
2236 if let Some(ref parent_id) = surf.parent_surface_id
2237 && let Some(parent) = self.surfaces.get_mut(parent_id)
2238 {
2239 parent.children.retain(|c| c != proto_id);
2240 }
2241 if surf.surface_id > 0 {
2242 self.toplevel_surface_ids.remove(&surf.surface_id);
2243 if self.focused_surface_id == surf.surface_id {
2248 self.focused_surface_id = 0;
2249 }
2250 self.last_request_frame_ms.remove(&surf.surface_id);
2251 self.last_reported_size.remove(&surf.surface_id);
2252 self.surface_sizes.remove(&surf.surface_id);
2253 if let Some(ref mut vk) = self.vulkan_renderer {
2254 vk.destroy_external_outputs_for_surface(surf.surface_id as u32);
2255 }
2256 let _ = self.event_tx.send(CompositorEvent::SurfaceDestroyed {
2257 surface_id: surf.surface_id,
2258 });
2259 (self.event_notify)();
2260 }
2261 }
2262 }
2263 }
2264
2265 fn fire_frame_callbacks_for_toplevel(&mut self, toplevel_sid: u16) {
2266 let Some(root_id) = self.toplevel_surface_ids.get(&toplevel_sid).cloned() else {
2267 return;
2268 };
2269 let tree = self.collect_surface_tree(&root_id);
2270 for sid in &tree {
2271 self.fire_surface_frame_callbacks(sid);
2272 }
2273 let _ = self.display_handle.flush_clients();
2274 }
2275
2276 fn handle_cursor_commit(&mut self, surface_id: &ObjectId) {
2277 self.apply_pending_state(surface_id);
2278 let hotspot = self
2279 .surfaces
2280 .get(surface_id)
2281 .map(|s| s.cursor_hotspot)
2282 .unwrap_or((0, 0));
2283 if let Some((w, h, rgba)) = self.cursor_rgba.get(surface_id)
2284 && !rgba.is_empty()
2285 {
2286 let _ = self.event_tx.send(CompositorEvent::SurfaceCursor {
2287 surface_id: self.focused_surface_id,
2288 cursor: CursorImage::Custom {
2289 hotspot_x: hotspot.0 as u16,
2290 hotspot_y: hotspot.1 as u16,
2291 width: *w as u16,
2292 height: *h as u16,
2293 rgba: rgba.clone(),
2294 },
2295 });
2296 }
2297 self.fire_surface_frame_callbacks(surface_id);
2298 let _ = self.display_handle.flush_clients();
2299 }
2300
2301 fn handle_command(&mut self, cmd: CompositorCommand) {
2302 match cmd {
2303 CompositorCommand::KeyInput {
2304 surface_id: _,
2305 keycode,
2306 pressed,
2307 } => {
2308 let serial = self.next_serial();
2309 let time = elapsed_ms();
2310 let state = if pressed {
2311 wl_keyboard::KeyState::Pressed
2312 } else {
2313 wl_keyboard::KeyState::Released
2314 };
2315 let focused_wl = self.keyboard_focus_wl();
2319 for kb in &self.keyboards {
2320 if let Some(ref wl) = focused_wl
2321 && same_client(kb, wl)
2322 {
2323 kb.key(serial, time, keycode, state);
2324 }
2325 }
2326 self.update_and_send_modifiers(keycode, pressed);
2331 let _ = self.display_handle.flush_clients();
2332 }
2333 CompositorCommand::TextInput { text } => {
2334 let focused_wl = self
2335 .toplevel_surface_ids
2336 .get(&self.focused_surface_id)
2337 .and_then(|root_id| self.surfaces.get(root_id))
2338 .map(|s| s.wl_surface.clone());
2339 let Some(focused_wl) = focused_wl else { return };
2340
2341 const KEY_LEFTSHIFT: u32 = 42;
2357 let saved_mods_depressed = self.mods_depressed;
2358 for ch in text.chars() {
2359 if let Some((kc, need_shift)) = char_to_keycode(ch) {
2360 let time = elapsed_ms();
2361 if need_shift {
2362 let serial = self.next_serial();
2363 for kb in &self.keyboards {
2364 if same_client(kb, &focused_wl) {
2365 kb.key(
2366 serial,
2367 time,
2368 KEY_LEFTSHIFT,
2369 wl_keyboard::KeyState::Pressed,
2370 );
2371 }
2372 }
2373 self.update_and_send_modifiers(KEY_LEFTSHIFT, true);
2374 }
2375 let serial = self.next_serial();
2376 for kb in &self.keyboards {
2377 if same_client(kb, &focused_wl) {
2378 kb.key(serial, time, kc, wl_keyboard::KeyState::Pressed);
2379 }
2380 }
2381 let serial = self.next_serial();
2382 for kb in &self.keyboards {
2383 if same_client(kb, &focused_wl) {
2384 kb.key(serial, time, kc, wl_keyboard::KeyState::Released);
2385 }
2386 }
2387 if need_shift {
2388 let serial = self.next_serial();
2389 for kb in &self.keyboards {
2390 if same_client(kb, &focused_wl) {
2391 kb.key(
2392 serial,
2393 time,
2394 KEY_LEFTSHIFT,
2395 wl_keyboard::KeyState::Released,
2396 );
2397 }
2398 }
2399 self.update_and_send_modifiers(KEY_LEFTSHIFT, false);
2400 }
2401 }
2402 }
2405 if self.mods_depressed != saved_mods_depressed {
2409 self.mods_depressed = saved_mods_depressed;
2410 let serial = self.next_serial();
2411 for kb in &self.keyboards {
2412 if same_client(kb, &focused_wl) {
2413 kb.modifiers(serial, self.mods_depressed, 0, self.mods_locked, 0);
2414 }
2415 }
2416 }
2417 let _ = self.display_handle.flush_clients();
2418 }
2419 CompositorCommand::PointerMotion { surface_id, x, y } => {
2420 let time = elapsed_ms();
2421 let (mut x, mut y) =
2426 if let Some(&(cw, ch, lw, lh)) = self.last_reported_size.get(&surface_id) {
2427 let sx = if cw > 0 { lw as f64 / cw as f64 } else { 1.0 };
2428 let sy = if ch > 0 { lh as f64 / ch as f64 } else { 1.0 };
2429 (x * sx, y * sy)
2430 } else {
2431 (x, y)
2432 };
2433 if let Some((gx, gy, _, _)) = self
2437 .toplevel_surface_ids
2438 .get(&surface_id)
2439 .and_then(|rid| self.surfaces.get(rid))
2440 .and_then(|s| s.xdg_geometry)
2441 {
2442 x += gx as f64;
2443 y += gy as f64;
2444 }
2445 let target_wl = self
2448 .toplevel_surface_ids
2449 .get(&surface_id)
2450 .and_then(|root_id| self.hit_test_surface_at(root_id, x, y))
2451 .map(|(wl_surface, lx, ly)| (wl_surface.id(), wl_surface, lx, ly));
2452
2453 static PTR_DBG: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
2454 let pn = PTR_DBG.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2455 if pn < 5 || pn.is_multiple_of(500) {
2456 let root = self.toplevel_surface_ids.get(&surface_id).cloned();
2457 let lrs = self.last_reported_size.get(&surface_id).copied();
2458 eprintln!(
2459 "[pointer #{pn}] sid={surface_id} logical=({x:.1},{y:.1}) lrs={lrs:?} root={root:?} hit={:?}",
2460 target_wl.as_ref().map(|(pid, _, lx, ly)| format!(
2461 "proto={pid:?} local=({lx:.1},{ly:.1})"
2462 ))
2463 );
2464 }
2465 if let Some((proto_id, wl_surface, lx, ly)) = target_wl {
2466 self.pointer_entered_local = (lx, ly);
2470 let matching_ptrs = self
2471 .pointers
2472 .iter()
2473 .filter(|p| same_client(*p, &wl_surface))
2474 .count();
2475 if let Some(change) = focus_transition(
2476 self.pointer_entered_id.as_ref(),
2477 &proto_id,
2478 matching_ptrs > 0,
2479 ) {
2480 let serial = self.next_serial();
2481 if matching_ptrs == 0 {
2482 eprintln!(
2489 "[pointer-enter] proto={proto_id:?} has no pointer yet (total_ptrs={}); deferring",
2490 self.pointers.len()
2491 );
2492 }
2493 if let Some(ref leaving) = change.leave {
2495 let old_wl = self
2496 .surfaces
2497 .values()
2498 .find(|s| s.wl_surface.id() == *leaving)
2499 .map(|s| s.wl_surface.clone());
2500 if let Some(old_wl) = old_wl {
2501 for ptr in &self.pointers {
2502 if same_client(ptr, &old_wl) {
2503 ptr.leave(serial, &old_wl);
2504 ptr.frame();
2505 }
2506 }
2507 }
2508 }
2509 for ptr in &self.pointers {
2510 if same_client(ptr, &wl_surface) {
2511 ptr.enter(serial, &wl_surface, lx, ly);
2512 }
2513 }
2514 self.pointer_entered_id = change.entered;
2520 }
2521 for ptr in &self.pointers {
2522 if same_client(ptr, &wl_surface) {
2523 ptr.motion(time, lx, ly);
2524 ptr.frame();
2525 }
2526 }
2527 }
2528 let _ = self.display_handle.flush_clients();
2531 }
2532 CompositorCommand::PointerButton {
2533 surface_id: _,
2534 button,
2535 pressed,
2536 } => {
2537 let serial = self.next_serial();
2538 let time = elapsed_ms();
2539 let state = if pressed {
2540 wl_pointer::ButtonState::Pressed
2541 } else {
2542 wl_pointer::ButtonState::Released
2543 };
2544
2545 let mut dismissed = false;
2548 if pressed && !self.popup_grab_stack.is_empty() {
2549 let click_on_grabbed = self.pointer_entered_id.as_ref().is_some_and(|eid| {
2550 self.popup_grab_stack.iter().any(|gid| {
2551 self.surfaces
2552 .get(gid)
2553 .is_some_and(|s| s.wl_surface.id() == *eid)
2554 })
2555 });
2556 if !click_on_grabbed {
2557 while let Some(grab_wl_id) = self.popup_grab_stack.pop() {
2559 if let Some(surf) = self.surfaces.get(&grab_wl_id)
2560 && let Some(ref popup) = surf.xdg_popup
2561 {
2562 popup.popup_done();
2563 }
2564 self.unfocus_popup(&grab_wl_id);
2569 }
2570 let _ = self.display_handle.flush_clients();
2571 dismissed = true;
2572 }
2573 }
2574
2575 let (routing, swallow) =
2578 button_routing(pressed, button, dismissed, self.popup_dismiss_button);
2579 self.popup_dismiss_button = swallow;
2580
2581 if routing == ButtonRouting::Deliver {
2582 let focused_wl = self
2583 .surfaces
2584 .values()
2585 .find(|s| Some(s.wl_surface.id()) == self.pointer_entered_id)
2586 .map(|s| s.wl_surface.clone());
2587 for ptr in &self.pointers {
2588 if let Some(ref wl) = focused_wl
2589 && same_client(ptr, wl)
2590 {
2591 ptr.button(serial, time, button, state);
2592 ptr.frame();
2593 }
2594 }
2595 }
2596 let _ = self.display_handle.flush_clients();
2597 }
2598 CompositorCommand::PointerAxis {
2599 surface_id: _,
2600 axis,
2601 value,
2602 } => {
2603 let time = elapsed_ms();
2604 let wl_axis = if axis == 0 {
2605 wl_pointer::Axis::VerticalScroll
2606 } else {
2607 wl_pointer::Axis::HorizontalScroll
2608 };
2609 let focused_wl = self
2610 .surfaces
2611 .values()
2612 .find(|s| Some(s.wl_surface.id()) == self.pointer_entered_id)
2613 .map(|s| s.wl_surface.clone());
2614 for ptr in &self.pointers {
2615 if let Some(ref wl) = focused_wl
2616 && same_client(ptr, wl)
2617 {
2618 ptr.axis(time, wl_axis, value);
2619 ptr.frame();
2620 }
2621 }
2622 let _ = self.display_handle.flush_clients();
2623 }
2624 CompositorCommand::SurfaceResize {
2625 surface_id,
2626 width,
2627 height,
2628 scale_120,
2629 } => {
2630 let s_in = (scale_120 as i32).max(120);
2633 let w = (width as i32) * 120 / s_in;
2634 let h = (height as i32) * 120 / s_in;
2635 self.surface_sizes.insert(surface_id, (w, h));
2636
2637 let mut output_changed = false;
2640
2641 if scale_120 > 0 && scale_120 != self.output_scale_120 {
2643 self.output_scale_120 = scale_120;
2644 output_changed = true;
2645 }
2646
2647 let s120 = self.output_scale_120 as i32;
2648
2649 let (max_w, max_h) = self
2654 .surface_sizes
2655 .values()
2656 .fold((0i32, 0i32), |(mw, mh), &(sw, sh)| (mw.max(sw), mh.max(sh)));
2657 let max_w = max_w.max(1);
2659 let max_h = max_h.max(1);
2660 if max_w != self.output_width || max_h != self.output_height {
2661 self.output_width = max_w;
2662 self.output_height = max_h;
2663 output_changed = true;
2664 }
2665
2666 if output_changed {
2670 let int_scale = ((s120) + 119) / 120;
2671 for output in &self.outputs {
2672 output.geometry(
2673 0,
2674 0,
2675 0,
2676 0,
2677 wl_output::Subpixel::None,
2678 "blit".to_string(),
2679 "virtual".to_string(),
2680 wl_output::Transform::Normal,
2681 );
2682 let mode_w = self.output_width * s120 / 120;
2684 let mode_h = self.output_height * s120 / 120;
2685 output.mode(
2686 wl_output::Mode::Current | wl_output::Mode::Preferred,
2687 mode_w,
2688 mode_h,
2689 self.output_refresh_mhz as i32,
2690 );
2691 if output.version() >= 2 {
2692 output.scale(int_scale);
2693 }
2694 }
2695 for fs in &self.fractional_scales {
2696 fs.preferred_scale(s120 as u32);
2697 }
2698 }
2699
2700 if output_changed {
2703 for output in &self.outputs {
2704 if output.version() >= 2 {
2705 output.done();
2706 }
2707 }
2708 }
2709
2710 let states = xdg_toplevel_states(&[
2711 xdg_toplevel::State::Activated,
2712 xdg_toplevel::State::Maximized,
2713 ]);
2714
2715 if output_changed {
2716 for (&sid, root_id) in &self.toplevel_surface_ids {
2720 let (lw, lh) = self.surface_sizes.get(&sid).copied().unwrap_or((w, h));
2721 if let Some(surf) = self.surfaces.get(root_id) {
2722 if let Some(ref tl) = surf.xdg_toplevel {
2723 tl.configure(lw, lh, states.clone());
2724 }
2725 if let Some(ref xs) = surf.xdg_surface {
2726 let serial = self.serial.wrapping_add(1);
2727 self.serial = serial;
2728 xs.configure(serial);
2729 }
2730 }
2731 }
2732 let all_sids: Vec<u16> = self.toplevel_surface_ids.keys().copied().collect();
2735 for sid in all_sids {
2736 self.fire_frame_callbacks_for_toplevel(sid);
2737 }
2738
2739 self.pointer_entered_id = None;
2742 self.pending_kb_reenter = true;
2743 } else {
2744 if let Some(root_id) = self.toplevel_surface_ids.get(&surface_id)
2749 && let Some(surf) = self.surfaces.get(root_id)
2750 {
2751 if let Some(ref tl) = surf.xdg_toplevel {
2752 tl.configure(w, h, states);
2753 }
2754 if let Some(ref xs) = surf.xdg_surface {
2755 let serial = self.serial.wrapping_add(1);
2756 self.serial = serial;
2757 xs.configure(serial);
2758 }
2759 }
2760 self.fire_frame_callbacks_for_toplevel(surface_id);
2761 }
2762 let _ = self.display_handle.flush_clients();
2763 }
2764 CompositorCommand::SurfaceFocus { surface_id } => {
2765 self.set_keyboard_focus(surface_id);
2766 let _ = self.display_handle.flush_clients();
2767 }
2768 CompositorCommand::SurfaceClose { surface_id } => {
2769 if let Some(root_id) = self.toplevel_surface_ids.get(&surface_id)
2770 && let Some(surf) = self.surfaces.get(root_id)
2771 && let Some(ref tl) = surf.xdg_toplevel
2772 {
2773 tl.close();
2774 }
2775 let _ = self.display_handle.flush_clients();
2776 }
2777 CompositorCommand::ClipboardOffer { mime_type, data } => {
2778 self.external_clipboard = Some(ExternalClipboard { mime_type, data });
2779 if let Some(src) = self.selection_source.take() {
2785 src.cancelled();
2786 }
2787 self.offer_external_clipboard();
2788 }
2789 CompositorCommand::Capture {
2790 surface_id,
2791 scale_120,
2792 reply,
2793 } => {
2794 let cap_s120 = if scale_120 > 0 {
2797 scale_120
2798 } else {
2799 self.output_scale_120
2800 };
2801 let result = if let Some(root_id) = self.toplevel_surface_ids.get(&surface_id) {
2802 if let Some(ref mut vk) = self.vulkan_renderer {
2803 vk.render_tree_sized(
2810 root_id,
2811 &self.surfaces,
2812 &self.surface_meta,
2813 cap_s120,
2814 None,
2815 surface_id,
2816 )
2817 .into_iter()
2818 .next()
2819 .map(|(_sid, w, h, pixels)| {
2820 let rgba = pixels.to_rgba(w, h);
2821 (w, h, rgba)
2822 })
2823 } else {
2824 None
2825 }
2826 } else {
2827 None
2828 };
2829 let _ = reply.send(result);
2830 }
2831 CompositorCommand::RequestFrame { surface_id } => {
2832 self.last_request_frame_ms.insert(surface_id, elapsed_ms());
2836 self.fire_frame_callbacks_for_toplevel(surface_id);
2837 }
2838 CompositorCommand::ReleaseKeys { keycodes } => {
2839 let time = elapsed_ms();
2840 let focused_wl = self
2841 .toplevel_surface_ids
2842 .get(&self.focused_surface_id)
2843 .and_then(|root_id| self.surfaces.get(root_id))
2844 .map(|s| s.wl_surface.clone());
2845 for keycode in &keycodes {
2846 let serial = self.next_serial();
2847 for kb in &self.keyboards {
2848 if let Some(ref wl) = focused_wl
2849 && same_client(kb, wl)
2850 {
2851 kb.key(serial, time, *keycode, wl_keyboard::KeyState::Released);
2852 }
2853 }
2854 }
2855 for keycode in &keycodes {
2857 self.update_and_send_modifiers(*keycode, false);
2858 }
2859 let _ = self.display_handle.flush_clients();
2860 }
2861 CompositorCommand::ClipboardListMimes { reply } => {
2862 let mimes = self.collect_clipboard_mime_types();
2863 let _ = reply.send(mimes);
2864 }
2865 CompositorCommand::ClipboardGet { mime_type, reply } => {
2866 let data = self.get_clipboard_content(&mime_type);
2867 let _ = reply.send(data);
2868 }
2869 CompositorCommand::SetExternalOutputBuffers {
2870 surface_id,
2871 target_w,
2872 target_h,
2873 buffers,
2874 } => {
2875 let installed = !buffers.is_empty();
2876 if let Some(ref mut vk) = self.vulkan_renderer {
2877 vk.set_external_output_buffers(surface_id, target_w, target_h, buffers);
2878 }
2879 if installed && self.toplevel_surface_ids.contains_key(&(surface_id as u16)) {
2890 self.pending_recomposite_toplevels.insert(surface_id as u16);
2891 }
2892 }
2893 CompositorCommand::RegisterDownscaleTarget {
2894 surface_id,
2895 target_w,
2896 target_h,
2897 } => {
2898 if let Some(ref mut vk) = self.vulkan_renderer {
2899 vk.register_downscale_target(surface_id, target_w, target_h);
2900 }
2901 if self.toplevel_surface_ids.contains_key(&(surface_id as u16)) {
2904 self.pending_recomposite_toplevels.insert(surface_id as u16);
2905 }
2906 }
2907 CompositorCommand::ClearDownscaleTarget {
2908 surface_id,
2909 target_w,
2910 target_h,
2911 } => {
2912 if let Some(ref mut vk) = self.vulkan_renderer {
2913 vk.clear_downscale_target(surface_id, target_w, target_h);
2914 }
2915 }
2916 CompositorCommand::SetRefreshRate { mhz } => {
2917 let diff = (mhz as i64 - self.output_refresh_mhz as i64).unsigned_abs();
2921 if diff > 2000 && mhz > 0 {
2922 self.output_refresh_mhz = mhz;
2923 let s120 = self.output_scale_120 as i32;
2924 let mode_w = self.output_width * s120 / 120;
2925 let mode_h = self.output_height * s120 / 120;
2926 for output in &self.outputs {
2927 output.mode(
2928 wl_output::Mode::Current | wl_output::Mode::Preferred,
2929 mode_w,
2930 mode_h,
2931 mhz as i32,
2932 );
2933 if output.version() >= 2 {
2934 output.done();
2935 }
2936 }
2937 let _ = self.display_handle.flush_clients();
2938 }
2939 }
2940 CompositorCommand::SetVulkanEncoder {
2941 surface_id,
2942 codec,
2943 qp,
2944 width,
2945 height,
2946 } => {
2947 if let Some(ref mut vk) = self.vulkan_renderer {
2948 vk.create_vulkan_encoder(surface_id, codec, qp, width, height);
2949 }
2950 }
2951 CompositorCommand::RequestVulkanKeyframe { surface_id } => {
2952 if let Some(ref mut vk) = self.vulkan_renderer {
2953 vk.request_encoder_keyframe(surface_id);
2954 }
2955 }
2956 CompositorCommand::DestroyVulkanEncoder { surface_id } => {
2957 if let Some(ref mut vk) = self.vulkan_renderer {
2958 vk.destroy_vulkan_encoder(surface_id);
2959 }
2960 }
2961 CompositorCommand::Shutdown => {
2962 self.shutdown.store(true, Ordering::Relaxed);
2963 self.loop_signal.stop();
2964 }
2965 }
2966 }
2967
2968 fn send_dmabuf_feedback(&self, fb: &ZwpLinuxDmabufFeedbackV1) {
2972 use std::os::unix::fs::MetadataExt;
2973
2974 let modifiers: &[(u32, u64)] = self
2976 .vulkan_renderer
2977 .as_ref()
2978 .map(|vk| vk.supported_dmabuf_modifiers.as_slice())
2979 .unwrap_or(&[]);
2980
2981 let entry_size = 16usize;
2983 let table_size = modifiers.len() * entry_size;
2984 let mut table_data = vec![0u8; table_size];
2985 for (i, &(fmt, modifier)) in modifiers.iter().enumerate() {
2986 let off = i * entry_size;
2987 table_data[off..off + 4].copy_from_slice(&fmt.to_ne_bytes());
2988 table_data[off + 8..off + 16].copy_from_slice(&modifier.to_ne_bytes());
2990 }
2991
2992 let name = c"dmabuf-feedback-table";
2994 let raw_fd = unsafe { libc::memfd_create(name.as_ptr(), libc::MFD_CLOEXEC) };
2995 if raw_fd < 0 {
2996 eprintln!("[compositor] memfd_create for dmabuf feedback failed");
2997 fb.done();
2998 return;
2999 }
3000 let table_fd = unsafe { OwnedFd::from_raw_fd(raw_fd) };
3001 if !table_data.is_empty() {
3002 use std::io::Write;
3003 let mut file = std::fs::File::from(table_fd.try_clone().unwrap());
3004 if file.write_all(&table_data).is_err() {
3005 eprintln!("[compositor] failed to write dmabuf feedback table");
3006 fb.done();
3007 return;
3008 }
3009 }
3010 fb.format_table(table_fd.as_fd(), table_size as u32);
3011
3012 let dev = std::fs::metadata(&self.gpu_device)
3014 .map(|m| m.rdev())
3015 .unwrap_or(0);
3016 let dev_bytes = dev.to_ne_bytes().to_vec();
3017 fb.main_device(dev_bytes.clone());
3018
3019 fb.tranche_target_device(dev_bytes);
3021
3022 let indices: Vec<u8> = (0..modifiers.len() as u16)
3024 .flat_map(|i| i.to_ne_bytes())
3025 .collect();
3026 fb.tranche_formats(indices);
3027
3028 fb.tranche_flags(
3029 wayland_protocols::wp::linux_dmabuf::zv1::server::zwp_linux_dmabuf_feedback_v1::TrancheFlags::empty(),
3030 );
3031 fb.tranche_done();
3032 fb.done();
3033 }
3034}
3035
3036impl Compositor {
3037 fn collect_clipboard_mime_types(&self) -> Vec<String> {
3039 if let Some(ref src) = self.selection_source {
3041 let data = src.data::<DataSourceData>().unwrap();
3042 return data.mime_types.lock().unwrap().clone();
3043 }
3044 if let Some(ref cb) = self.external_clipboard
3046 && !cb.mime_type.is_empty()
3047 {
3048 let mut mimes = vec![cb.mime_type.clone()];
3049 if cb.mime_type.starts_with("text/plain") {
3051 if cb.mime_type != "text/plain" {
3052 mimes.push("text/plain".to_string());
3053 }
3054 if cb.mime_type != "text/plain;charset=utf-8" {
3055 mimes.push("text/plain;charset=utf-8".to_string());
3056 }
3057 mimes.push("UTF8_STRING".to_string());
3058 }
3059 return mimes;
3060 }
3061 Vec::new()
3062 }
3063
3064 fn get_clipboard_content(&mut self, mime_type: &str) -> Option<Vec<u8>> {
3066 if let Some(ref cb) = self.external_clipboard
3068 && self.selection_source.is_none()
3069 {
3070 let matches = cb.mime_type == mime_type
3072 || (cb.mime_type.starts_with("text/plain")
3073 && (mime_type == "text/plain"
3074 || mime_type == "text/plain;charset=utf-8"
3075 || mime_type == "UTF8_STRING"));
3076 if matches {
3077 return Some(cb.data.clone());
3078 }
3079 return None;
3080 }
3081 if let Some(src) = self.selection_source.clone() {
3083 return self.read_data_source_sync(&src, mime_type);
3084 }
3085 None
3086 }
3087
3088 fn read_data_source_sync(&mut self, source: &WlDataSource, mime_type: &str) -> Option<Vec<u8>> {
3090 let mut fds = [0i32; 2];
3091 if unsafe { libc::pipe(fds.as_mut_ptr()) } != 0 {
3092 return None;
3093 }
3094 let read_fd = unsafe { OwnedFd::from_raw_fd(fds[0]) };
3095 let write_fd = unsafe { OwnedFd::from_raw_fd(fds[1]) };
3096 source.send(mime_type.to_string(), write_fd.as_fd());
3097 let _ = self.display_handle.flush_clients();
3098 drop(write_fd); unsafe {
3101 libc::fcntl(read_fd.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
3102 }
3103 std::thread::sleep(std::time::Duration::from_millis(5));
3104 let mut buf = Vec::new();
3105 let mut tmp = [0u8; 8192];
3106 loop {
3107 let n = unsafe {
3108 libc::read(
3109 read_fd.as_raw_fd(),
3110 tmp.as_mut_ptr() as *mut libc::c_void,
3111 tmp.len(),
3112 )
3113 };
3114 if n <= 0 {
3115 break;
3116 }
3117 buf.extend_from_slice(&tmp[..n as usize]);
3118 if buf.len() > 1024 * 1024 {
3119 break; }
3121 }
3122 if buf.is_empty() { None } else { Some(buf) }
3123 }
3124}
3125
3126fn monotonic_timespec() -> (i64, i64) {
3132 let mut ts = libc::timespec {
3133 tv_sec: 0,
3134 tv_nsec: 0,
3135 };
3136 unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts) };
3138 (ts.tv_sec, ts.tv_nsec)
3139}
3140
3141fn elapsed_ms() -> u32 {
3142 let (sec, nsec) = monotonic_timespec();
3147 (sec as u32)
3148 .wrapping_mul(1000)
3149 .wrapping_add(nsec as u32 / 1_000_000)
3150}
3151
3152fn same_client<R1: Resource, R2: Resource>(a: &R1, b: &R2) -> bool {
3154 match (a.client(), b.client()) {
3155 (Some(ca), Some(cb)) => ca.id() == cb.id(),
3156 _ => false,
3157 }
3158}
3159
3160fn yuv420_to_rgb(y: u8, u: u8, v: u8) -> [u8; 3] {
3161 let y = (y as i32 - 16).max(0);
3162 let u = u as i32 - 128;
3163 let v = v as i32 - 128;
3164 let r = ((298 * y + 409 * v + 128) >> 8).clamp(0, 255) as u8;
3165 let g = ((298 * y - 100 * u - 208 * v + 128) >> 8).clamp(0, 255) as u8;
3166 let b = ((298 * y + 516 * u + 128) >> 8).clamp(0, 255) as u8;
3167 [r, g, b]
3168}
3169
3170fn xdg_toplevel_states(states: &[xdg_toplevel::State]) -> Vec<u8> {
3172 let mut bytes = Vec::with_capacity(states.len() * 4);
3173 for state in states {
3174 bytes.extend_from_slice(&(*state as u32).to_ne_bytes());
3175 }
3176 bytes
3177}
3178
3179fn create_keymap_fd(keymap_data: &[u8]) -> Option<OwnedFd> {
3180 use std::io::Write;
3181 let name = c"blit-keymap";
3182 let raw_fd = unsafe { libc::memfd_create(name.as_ptr(), libc::MFD_CLOEXEC) };
3183 if raw_fd < 0 {
3184 return None;
3185 }
3186 let fd = unsafe { OwnedFd::from_raw_fd(raw_fd) };
3187 let mut file = std::fs::File::from(fd);
3188 file.write_all(keymap_data).ok()?;
3189 Some(file.into())
3190}
3191
3192impl GlobalDispatch<WlCompositor, ()> for Compositor {
3199 fn bind(
3200 _state: &mut Self,
3201 _handle: &DisplayHandle,
3202 _client: &Client,
3203 resource: New<WlCompositor>,
3204 _data: &(),
3205 data_init: &mut DataInit<'_, Self>,
3206 ) {
3207 data_init.init(resource, ());
3208 }
3209}
3210
3211impl Dispatch<WlCompositor, ()> for Compositor {
3212 fn request(
3213 state: &mut Self,
3214 _client: &Client,
3215 _resource: &WlCompositor,
3216 request: <WlCompositor as Resource>::Request,
3217 _data: &(),
3218 _dh: &DisplayHandle,
3219 data_init: &mut DataInit<'_, Self>,
3220 ) {
3221 use wayland_server::protocol::wl_compositor::Request;
3222 match request {
3223 Request::CreateSurface { id } => {
3224 let surface = data_init.init(id, ());
3225 let proto_id = surface.id();
3226 state.surfaces.insert(
3227 proto_id,
3228 Surface {
3229 surface_id: 0,
3230 wl_surface: surface,
3231 pending_buffer: None,
3232 pending_buffer_scale: 1,
3233 pending_damage: false,
3234 pending_frame_callbacks: Vec::new(),
3235 pending_presentation_feedbacks: Vec::new(),
3236 pending_opaque: false,
3237 pending_input_region: None,
3238 input_region: None,
3239 buffer_scale: 1,
3240 is_opaque: false,
3241 parent_surface_id: None,
3242 pending_subsurface_position: None,
3243 subsurface_position: (0, 0),
3244 children: Vec::new(),
3245 xdg_surface: None,
3246 xdg_toplevel: None,
3247 xdg_popup: None,
3248 xdg_geometry: None,
3249 title: String::new(),
3250 app_id: String::new(),
3251 pending_viewport_destination: None,
3252 viewport_destination: None,
3253 is_cursor: false,
3254 cursor_hotspot: (0, 0),
3255 },
3256 );
3257 }
3258 Request::CreateRegion { id } => {
3259 data_init.init(id, ());
3260 }
3261 _ => {}
3262 }
3263 }
3264}
3265
3266impl Dispatch<WlSurface, ()> for Compositor {
3269 fn request(
3270 state: &mut Self,
3271 _client: &Client,
3272 resource: &WlSurface,
3273 request: <WlSurface as Resource>::Request,
3274 _data: &(),
3275 _dh: &DisplayHandle,
3276 data_init: &mut DataInit<'_, Self>,
3277 ) {
3278 use wayland_server::protocol::wl_surface::Request;
3279 let sid = resource.id();
3280 match request {
3281 Request::Attach { buffer, x: _, y: _ } => {
3282 if let Some(surf) = state.surfaces.get_mut(&sid) {
3283 surf.pending_buffer = buffer;
3284 }
3285 }
3286 Request::Damage { .. } | Request::DamageBuffer { .. } => {
3287 if let Some(surf) = state.surfaces.get_mut(&sid) {
3288 surf.pending_damage = true;
3289 }
3290 }
3291 Request::Frame { callback } => {
3292 let cb = data_init.init(callback, ());
3293 if let Some(surf) = state.surfaces.get_mut(&sid) {
3294 surf.pending_frame_callbacks.push(cb);
3295 }
3296 }
3297 Request::SetBufferScale { scale } => {
3298 if let Some(surf) = state.surfaces.get_mut(&sid) {
3299 surf.pending_buffer_scale = scale;
3300 }
3301 }
3302 Request::SetOpaqueRegion { region: _ } => {
3303 if let Some(surf) = state.surfaces.get_mut(&sid) {
3304 surf.pending_opaque = true;
3305 }
3306 }
3307 Request::SetInputRegion { region } => {
3308 let ops = region.map(|r| {
3313 state
3314 .regions
3315 .get(&r.id())
3316 .map(|(_, ops)| ops.clone())
3317 .unwrap_or_default()
3318 });
3319 if let Some(surf) = state.surfaces.get_mut(&sid) {
3320 surf.pending_input_region = Some(ops);
3321 }
3322 }
3323 Request::Commit => {
3324 let is_cursor = state.surfaces.get(&sid).is_some_and(|s| s.is_cursor);
3325 if is_cursor {
3326 state.handle_cursor_commit(&sid);
3327 } else {
3328 state.handle_surface_commit(&sid);
3329 }
3330 }
3331 Request::SetBufferTransform { .. } => {}
3332 Request::Offset { .. } => {}
3333 Request::Destroy => {
3334 state.surface_meta.remove(&sid);
3335 state.cursor_rgba.remove(&sid);
3336 if let Some(ref mut vk) = state.vulkan_renderer {
3337 vk.remove_surface(&sid);
3338 }
3339 if let Some(held) = state.held_buffers.remove(&sid) {
3340 held.release();
3341 }
3342 state.forget_pointer_focus(&sid);
3343 if let Some(parent_id) = state
3344 .surfaces
3345 .get(&sid)
3346 .and_then(|s| s.parent_surface_id.clone())
3347 && let Some(parent) = state.surfaces.get_mut(&parent_id)
3348 {
3349 parent.children.retain(|c| *c != sid);
3350 }
3351 if let Some(surf) = state.surfaces.remove(&sid) {
3352 for fb in surf.pending_presentation_feedbacks {
3353 fb.discarded();
3354 }
3355 if surf.surface_id > 0 {
3356 state.toplevel_surface_ids.remove(&surf.surface_id);
3357 state.last_reported_size.remove(&surf.surface_id);
3358 state.surface_sizes.remove(&surf.surface_id);
3359 if let Some(ref mut vk) = state.vulkan_renderer {
3360 vk.destroy_external_outputs_for_surface(surf.surface_id as u32);
3361 }
3362 let _ = state.event_tx.send(CompositorEvent::SurfaceDestroyed {
3363 surface_id: surf.surface_id,
3364 });
3365 (state.event_notify)();
3366 }
3367 }
3368 }
3369 _ => {}
3370 }
3371 }
3372}
3373
3374impl Dispatch<WlCallback, ()> for Compositor {
3376 fn request(
3377 _: &mut Self,
3378 _: &Client,
3379 _: &WlCallback,
3380 _: <WlCallback as Resource>::Request,
3381 _: &(),
3382 _: &DisplayHandle,
3383 _: &mut DataInit<'_, Self>,
3384 ) {
3385 }
3386}
3387
3388impl GlobalDispatch<WpPresentation, ()> for Compositor {
3390 fn bind(
3391 _: &mut Self,
3392 _: &DisplayHandle,
3393 _: &Client,
3394 resource: New<WpPresentation>,
3395 _: &(),
3396 data_init: &mut DataInit<'_, Self>,
3397 ) {
3398 let pres = data_init.init(resource, ());
3399 pres.clock_id(libc::CLOCK_MONOTONIC as u32);
3401 }
3402}
3403
3404impl Dispatch<WpPresentation, ()> for Compositor {
3405 fn request(
3406 state: &mut Self,
3407 _: &Client,
3408 _: &WpPresentation,
3409 request: <WpPresentation as Resource>::Request,
3410 _: &(),
3411 _: &DisplayHandle,
3412 data_init: &mut DataInit<'_, Self>,
3413 ) {
3414 use wp_presentation::Request;
3415 match request {
3416 Request::Feedback { surface, callback } => {
3417 let fb = data_init.init(callback, ());
3418 let sid = surface.id();
3419 if let Some(surf) = state.surfaces.get_mut(&sid) {
3420 surf.pending_presentation_feedbacks.push(fb);
3421 }
3422 }
3423 Request::Destroy => {}
3424 _ => {}
3425 }
3426 }
3427}
3428
3429impl Dispatch<WpPresentationFeedback, ()> for Compositor {
3431 fn request(
3432 _: &mut Self,
3433 _: &Client,
3434 _: &WpPresentationFeedback,
3435 _: <WpPresentationFeedback as Resource>::Request,
3436 _: &(),
3437 _: &DisplayHandle,
3438 _: &mut DataInit<'_, Self>,
3439 ) {
3440 }
3441}
3442
3443impl Dispatch<WlRegion, ()> for Compositor {
3445 fn request(
3446 state: &mut Self,
3447 _: &Client,
3448 resource: &WlRegion,
3449 request: <WlRegion as Resource>::Request,
3450 _: &(),
3451 _: &DisplayHandle,
3452 _: &mut DataInit<'_, Self>,
3453 ) {
3454 use wayland_server::protocol::wl_region::Request;
3455 let rid = resource.id();
3456 match request {
3457 Request::Add {
3458 x,
3459 y,
3460 width,
3461 height,
3462 } => {
3463 state
3464 .regions
3465 .entry(rid)
3466 .or_insert_with(|| (resource.clone(), Vec::new()))
3467 .1
3468 .push(RegionOp {
3469 add: true,
3470 x,
3471 y,
3472 w: width,
3473 h: height,
3474 });
3475 }
3476 Request::Subtract {
3477 x,
3478 y,
3479 width,
3480 height,
3481 } => {
3482 state
3483 .regions
3484 .entry(rid)
3485 .or_insert_with(|| (resource.clone(), Vec::new()))
3486 .1
3487 .push(RegionOp {
3488 add: false,
3489 x,
3490 y,
3491 w: width,
3492 h: height,
3493 });
3494 }
3495 Request::Destroy => {
3496 state.regions.remove(&rid);
3500 }
3501 _ => {}
3502 }
3503 }
3504}
3505
3506impl GlobalDispatch<WlSubcompositor, ()> for Compositor {
3508 fn bind(
3509 _: &mut Self,
3510 _: &DisplayHandle,
3511 _: &Client,
3512 resource: New<WlSubcompositor>,
3513 _: &(),
3514 data_init: &mut DataInit<'_, Self>,
3515 ) {
3516 data_init.init(resource, ());
3517 }
3518}
3519
3520impl Dispatch<WlSubcompositor, ()> for Compositor {
3521 fn request(
3522 state: &mut Self,
3523 _: &Client,
3524 _: &WlSubcompositor,
3525 request: <WlSubcompositor as Resource>::Request,
3526 _: &(),
3527 _: &DisplayHandle,
3528 data_init: &mut DataInit<'_, Self>,
3529 ) {
3530 use wayland_server::protocol::wl_subcompositor::Request;
3531 match request {
3532 Request::GetSubsurface {
3533 id,
3534 surface,
3535 parent,
3536 } => {
3537 let child_id = surface.id();
3538 let parent_id = parent.id();
3539 data_init.init(
3540 id,
3541 SubsurfaceData {
3542 wl_surface_id: child_id.clone(),
3543 parent_surface_id: parent_id.clone(),
3544 },
3545 );
3546 if let Some(surf) = state.surfaces.get_mut(&child_id) {
3547 surf.parent_surface_id = Some(parent_id.clone());
3548 }
3549 if let Some(parent_surf) = state.surfaces.get_mut(&parent_id)
3550 && !parent_surf.children.contains(&child_id)
3551 {
3552 parent_surf.children.push(child_id);
3553 }
3554 }
3555 Request::Destroy => {}
3556 _ => {}
3557 }
3558 }
3559}
3560
3561impl Dispatch<WlSubsurface, SubsurfaceData> for Compositor {
3563 fn request(
3564 state: &mut Self,
3565 _: &Client,
3566 _: &WlSubsurface,
3567 request: <WlSubsurface as Resource>::Request,
3568 data: &SubsurfaceData,
3569 _: &DisplayHandle,
3570 _: &mut DataInit<'_, Self>,
3571 ) {
3572 use wayland_server::protocol::wl_subsurface::Request;
3573 match request {
3574 Request::SetPosition { x, y } => {
3575 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3576 surf.pending_subsurface_position = Some((x, y));
3577 }
3578 }
3579 Request::PlaceAbove { sibling } => {
3580 let sibling_id = sibling.id();
3581 if let Some(parent) = state.surfaces.get_mut(&data.parent_surface_id) {
3582 let child_id = &data.wl_surface_id;
3583 parent.children.retain(|c| c != child_id);
3584 let pos = parent
3585 .children
3586 .iter()
3587 .position(|c| *c == sibling_id)
3588 .map(|p| p + 1)
3589 .unwrap_or(parent.children.len());
3590 parent.children.insert(pos, child_id.clone());
3591 }
3592 }
3593 Request::PlaceBelow { sibling } => {
3594 let sibling_id = sibling.id();
3595 if let Some(parent) = state.surfaces.get_mut(&data.parent_surface_id) {
3596 let child_id = &data.wl_surface_id;
3597 parent.children.retain(|c| c != child_id);
3598 let pos = parent
3599 .children
3600 .iter()
3601 .position(|c| *c == sibling_id)
3602 .unwrap_or(0);
3603 parent.children.insert(pos, child_id.clone());
3604 }
3605 }
3606 Request::SetSync | Request::SetDesync => {}
3607 Request::Destroy => {
3608 let child_id = &data.wl_surface_id;
3609 if let Some(parent) = state.surfaces.get_mut(&data.parent_surface_id) {
3610 parent.children.retain(|c| c != child_id);
3611 }
3612 if let Some(surf) = state.surfaces.get_mut(child_id) {
3613 surf.parent_surface_id = None;
3614 }
3615 }
3616 _ => {}
3617 }
3618 }
3619}
3620
3621impl GlobalDispatch<XdgWmBase, ()> for Compositor {
3623 fn bind(
3624 _: &mut Self,
3625 _: &DisplayHandle,
3626 _: &Client,
3627 resource: New<XdgWmBase>,
3628 _: &(),
3629 data_init: &mut DataInit<'_, Self>,
3630 ) {
3631 data_init.init(resource, ());
3632 }
3633}
3634
3635impl Dispatch<XdgWmBase, ()> for Compositor {
3636 fn request(
3637 state: &mut Self,
3638 _: &Client,
3639 _: &XdgWmBase,
3640 request: <XdgWmBase as Resource>::Request,
3641 _: &(),
3642 _: &DisplayHandle,
3643 data_init: &mut DataInit<'_, Self>,
3644 ) {
3645 use xdg_wm_base::Request;
3646 match request {
3647 Request::GetXdgSurface { id, surface } => {
3648 let wl_surface_id = surface.id();
3649 let xdg_surface = data_init.init(
3650 id,
3651 XdgSurfaceData {
3652 wl_surface_id: wl_surface_id.clone(),
3653 },
3654 );
3655 if let Some(surf) = state.surfaces.get_mut(&wl_surface_id) {
3656 surf.xdg_surface = Some(xdg_surface);
3657 }
3658 }
3659 Request::CreatePositioner { id } => {
3660 let positioner = data_init.init(id, ());
3661 let pos_id = positioner.id();
3662 state.positioners.insert(
3663 pos_id,
3664 PositionerState {
3665 resource: positioner,
3666 geometry: PositionerGeometry {
3667 size: (0, 0),
3668 anchor_rect: (0, 0, 0, 0),
3669 anchor: 0,
3670 gravity: 0,
3671 constraint_adjustment: 0,
3672 offset: (0, 0),
3673 },
3674 },
3675 );
3676 }
3677 Request::Pong { .. } => {}
3678 Request::Destroy => {}
3679 _ => {}
3680 }
3681 }
3682}
3683
3684impl Dispatch<XdgSurface, XdgSurfaceData> for Compositor {
3686 fn request(
3687 state: &mut Self,
3688 _: &Client,
3689 resource: &XdgSurface,
3690 request: <XdgSurface as Resource>::Request,
3691 data: &XdgSurfaceData,
3692 _: &DisplayHandle,
3693 data_init: &mut DataInit<'_, Self>,
3694 ) {
3695 use xdg_surface::Request;
3696 match request {
3697 Request::GetToplevel { id } => {
3698 let toplevel = data_init.init(
3699 id,
3700 XdgToplevelData {
3701 wl_surface_id: data.wl_surface_id.clone(),
3702 },
3703 );
3704 let surface_id = state.allocate_surface_id();
3705 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3706 surf.xdg_toplevel = Some(toplevel.clone());
3707 surf.surface_id = surface_id;
3708 }
3709 state
3710 .toplevel_surface_ids
3711 .insert(surface_id, data.wl_surface_id.clone());
3712
3713 let (cw, ch) = state
3718 .surface_sizes
3719 .get(&surface_id)
3720 .copied()
3721 .unwrap_or((state.output_width, state.output_height));
3722 let states = xdg_toplevel_states(&[
3723 xdg_toplevel::State::Activated,
3724 xdg_toplevel::State::Maximized,
3725 ]);
3726 toplevel.configure(cw, ch, states);
3727 let serial = state.next_serial();
3728 resource.configure(serial);
3729
3730 state.set_keyboard_focus(surface_id);
3733 if let Some(surf) = state.surfaces.get(&data.wl_surface_id) {
3736 for output in &state.outputs {
3737 if same_client(output, &surf.wl_surface) {
3738 surf.wl_surface.enter(output);
3739 }
3740 }
3741 }
3742 let _ = state.display_handle.flush_clients();
3743
3744 let _ = state.event_tx.send(CompositorEvent::SurfaceCreated {
3745 surface_id,
3746 title: String::new(),
3747 app_id: String::new(),
3748 parent_id: 0,
3749 width: 0,
3750 height: 0,
3751 });
3752 (state.event_notify)();
3753 if state.verbose {
3754 eprintln!("[compositor] new_toplevel sid={surface_id}");
3755 }
3756 }
3757 Request::GetPopup {
3758 id,
3759 parent,
3760 positioner,
3761 } => {
3762 let popup = data_init.init(
3763 id,
3764 XdgPopupData {
3765 wl_surface_id: data.wl_surface_id.clone(),
3766 },
3767 );
3768
3769 let parent_wl_id: Option<ObjectId> = parent
3772 .as_ref()
3773 .and_then(|p| p.data::<XdgSurfaceData>())
3774 .map(|d| d.wl_surface_id.clone());
3775
3776 let parent_geom_offset = parent_wl_id
3781 .as_ref()
3782 .and_then(|pid| state.surfaces.get(pid))
3783 .and_then(|s| s.xdg_geometry)
3784 .map(|(gx, gy, _, _)| (gx, gy))
3785 .unwrap_or((0, 0));
3786
3787 let parent_abs = parent_wl_id
3792 .as_ref()
3793 .map(|pid| {
3794 let abs = state.surface_absolute_position(pid);
3795 (abs.0 + parent_geom_offset.0, abs.1 + parent_geom_offset.1)
3796 })
3797 .unwrap_or((0, 0));
3798 let (_, toplevel_root) = parent_wl_id
3801 .as_ref()
3802 .map(|pid| state.find_toplevel_root(pid))
3803 .unwrap_or_else(|| {
3804 (data.wl_surface_id.clone(), None)
3806 });
3807 let bounds = toplevel_root
3808 .and_then(|_| {
3809 let root_wl_id = parent_wl_id.as_ref().map(|pid| {
3810 let (rid, _) = state.find_toplevel_root(pid);
3811 rid
3812 })?;
3813 let surf = state.surfaces.get(&root_wl_id)?;
3814 if let Some((gx, gy, gw, gh)) = surf.xdg_geometry
3815 && gw > 0
3816 && gh > 0
3817 {
3818 return Some((gx, gy, gw, gh));
3819 }
3820
3821 let sm = state.surface_meta.get(&root_wl_id)?;
3824 let s = (sm.scale).max(1);
3825 let (lw, lh) = surf
3826 .viewport_destination
3827 .filter(|&(dw, dh)| dw > 0 && dh > 0)
3828 .unwrap_or((sm.width as i32 / s, sm.height as i32 / s));
3829 Some((0, 0, lw, lh))
3830 })
3831 .unwrap_or((0, 0, state.output_width, state.output_height));
3832
3833 eprintln!(
3834 "[popup] parent_abs={parent_abs:?} bounds={bounds:?} parent_wl={parent_wl_id:?} geom_off={parent_geom_offset:?}"
3835 );
3836 let pos_id = positioner.id();
3838 let (px, py, pw, ph) = state
3839 .positioners
3840 .get(&pos_id)
3841 .map(|p| p.geometry.compute_position(parent_abs, bounds))
3842 .unwrap_or((0, 0, 200, 200));
3843 eprintln!("[popup] result=({px},{py},{pw},{ph})");
3844
3845 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3846 surf.xdg_popup = Some(popup.clone());
3847 surf.parent_surface_id = parent_wl_id.clone();
3848 surf.subsurface_position =
3853 (parent_geom_offset.0 + px, parent_geom_offset.1 + py);
3854 }
3855 if let Some(ref parent_id) = parent_wl_id
3856 && let Some(parent_surf) = state.surfaces.get_mut(parent_id)
3857 && !parent_surf.children.contains(&data.wl_surface_id)
3858 {
3859 parent_surf.children.push(data.wl_surface_id.clone());
3860 }
3861
3862 popup.configure(px, py, pw, ph);
3863 let serial = state.next_serial();
3864 resource.configure(serial);
3865 let _ = state.display_handle.flush_clients();
3866 }
3867 Request::SetWindowGeometry {
3868 x,
3869 y,
3870 width,
3871 height,
3872 } => {
3873 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3874 if surf.xdg_popup.is_some() {
3882 let (old_gx, old_gy) = surf
3883 .xdg_geometry
3884 .map(|(gx, gy, _, _)| (gx, gy))
3885 .unwrap_or((0, 0));
3886 surf.subsurface_position.0 += old_gx - x;
3887 surf.subsurface_position.1 += old_gy - y;
3888 }
3889 surf.xdg_geometry = Some((x, y, width, height));
3890 }
3891 }
3892 Request::AckConfigure { .. } => {}
3893 Request::Destroy => {}
3894 _ => {}
3895 }
3896 }
3897}
3898
3899impl Dispatch<XdgToplevel, XdgToplevelData> for Compositor {
3901 fn request(
3902 state: &mut Self,
3903 _: &Client,
3904 _: &XdgToplevel,
3905 request: <XdgToplevel as Resource>::Request,
3906 data: &XdgToplevelData,
3907 _: &DisplayHandle,
3908 _: &mut DataInit<'_, Self>,
3909 ) {
3910 use xdg_toplevel::Request;
3911 match request {
3912 Request::SetTitle { title } => {
3913 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id)
3914 && surf.title != title
3915 {
3916 surf.title = title.clone();
3917 if surf.surface_id > 0 {
3918 let _ = state.event_tx.send(CompositorEvent::SurfaceTitle {
3919 surface_id: surf.surface_id,
3920 title,
3921 });
3922 (state.event_notify)();
3923 }
3924 }
3925 }
3926 Request::SetAppId { app_id } => {
3927 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id)
3928 && surf.app_id != app_id
3929 {
3930 surf.app_id = app_id.clone();
3931 if surf.surface_id > 0 {
3932 let _ = state.event_tx.send(CompositorEvent::SurfaceAppId {
3933 surface_id: surf.surface_id,
3934 app_id,
3935 });
3936 (state.event_notify)();
3937 }
3938 }
3939 }
3940 Request::Destroy => {
3941 let wl_surface_id = &data.wl_surface_id;
3942 state.surface_meta.remove(wl_surface_id);
3943 state.cursor_rgba.remove(wl_surface_id);
3944 if let Some(ref mut vk) = state.vulkan_renderer {
3945 vk.remove_surface(wl_surface_id);
3946 }
3947 if let Some(held) = state.held_buffers.remove(wl_surface_id) {
3948 held.release();
3949 }
3950 if let Some(surf) = state.surfaces.get_mut(wl_surface_id) {
3951 let sid = surf.surface_id;
3952 surf.xdg_toplevel = None;
3953 if sid > 0 {
3954 state.toplevel_surface_ids.remove(&sid);
3955 state.last_reported_size.remove(&sid);
3956 state.surface_sizes.remove(&sid);
3957 if let Some(ref mut vk) = state.vulkan_renderer {
3958 vk.destroy_external_outputs_for_surface(sid as u32);
3959 }
3960 let _ = state
3961 .event_tx
3962 .send(CompositorEvent::SurfaceDestroyed { surface_id: sid });
3963 (state.event_notify)();
3964 surf.surface_id = 0;
3965 }
3966 }
3967 }
3968 _ => {}
3969 }
3970 }
3971}
3972
3973impl Dispatch<XdgPopup, XdgPopupData> for Compositor {
3975 fn request(
3976 state: &mut Self,
3977 _: &Client,
3978 _: &XdgPopup,
3979 request: <XdgPopup as Resource>::Request,
3980 data: &XdgPopupData,
3981 _: &DisplayHandle,
3982 _: &mut DataInit<'_, Self>,
3983 ) {
3984 use xdg_popup::Request;
3985 match request {
3986 Request::Grab { seat: _, serial: _ } => {
3987 state
3990 .popup_grab_stack
3991 .retain(|id| *id != data.wl_surface_id);
3992 state.popup_grab_stack.push(data.wl_surface_id.clone());
3993 state.focus_popup(&data.wl_surface_id);
3997 }
3998 Request::Reposition { positioner, token } => {
3999 let pos_id = positioner.id();
4001 if let Some(surf) = state.surfaces.get(&data.wl_surface_id)
4002 && let Some(parent_id) = surf.parent_surface_id.clone()
4003 {
4004 let parent_geom_offset = state
4005 .surfaces
4006 .get(&parent_id)
4007 .and_then(|s| s.xdg_geometry)
4008 .map(|(gx, gy, _, _)| (gx, gy))
4009 .unwrap_or((0, 0));
4010 let parent_abs = {
4011 let abs = state.surface_absolute_position(&parent_id);
4012 (abs.0 + parent_geom_offset.0, abs.1 + parent_geom_offset.1)
4013 };
4014 let (root_id, toplevel_root) = state.find_toplevel_root(&parent_id);
4015 let bounds = toplevel_root
4016 .and_then(|_| {
4017 let surf = state.surfaces.get(&root_id)?;
4018 if let Some((gx, gy, gw, gh)) = surf.xdg_geometry
4019 && gw > 0
4020 && gh > 0
4021 {
4022 return Some((gx, gy, gw, gh));
4023 }
4024 let sm = state.surface_meta.get(&root_id)?;
4025 let s = (sm.scale).max(1);
4026 let (lw, lh) = surf
4027 .viewport_destination
4028 .filter(|&(dw, dh)| dw > 0 && dh > 0)
4029 .unwrap_or((sm.width as i32 / s, sm.height as i32 / s));
4030 Some((0, 0, lw, lh))
4031 })
4032 .unwrap_or((0, 0, state.output_width, state.output_height));
4033 let (px, py, pw, ph) = state
4034 .positioners
4035 .get(&pos_id)
4036 .map(|p| p.geometry.compute_position(parent_abs, bounds))
4037 .unwrap_or((0, 0, 200, 200));
4038 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
4039 let old_gx = surf.xdg_geometry.map(|(gx, _, _, _)| gx).unwrap_or(0);
4042 let old_gy = surf.xdg_geometry.map(|(_, gy, _, _)| gy).unwrap_or(0);
4043 surf.subsurface_position = (
4044 parent_geom_offset.0 + px - old_gx,
4045 parent_geom_offset.1 + py - old_gy,
4046 );
4047 if let Some(ref popup) = surf.xdg_popup {
4048 popup.configure(px, py, pw, ph);
4049 popup.repositioned(token);
4050 }
4051 if let Some(ref xs) = surf.xdg_surface {
4052 let serial = state.serial.wrapping_add(1);
4053 state.serial = serial;
4054 xs.configure(serial);
4055 }
4056 }
4057 }
4058 }
4059 Request::Destroy => {
4060 state
4062 .popup_grab_stack
4063 .retain(|id| *id != data.wl_surface_id);
4064 state.unfocus_popup(&data.wl_surface_id);
4070 if let Some(parent_id) = state
4072 .surfaces
4073 .get(&data.wl_surface_id)
4074 .and_then(|s| s.parent_surface_id.clone())
4075 && let Some(parent) = state.surfaces.get_mut(&parent_id)
4076 {
4077 parent.children.retain(|c| *c != data.wl_surface_id);
4078 }
4079 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
4080 surf.xdg_popup = None;
4081 surf.parent_surface_id = None;
4082 }
4083 }
4084 _ => {}
4085 }
4086 }
4087}
4088
4089use wayland_protocols::xdg::shell::server::xdg_positioner;
4091impl Dispatch<XdgPositioner, ()> for Compositor {
4092 fn request(
4093 state: &mut Self,
4094 _: &Client,
4095 resource: &XdgPositioner,
4096 request: <XdgPositioner as Resource>::Request,
4097 _: &(),
4098 _: &DisplayHandle,
4099 _: &mut DataInit<'_, Self>,
4100 ) {
4101 use xdg_positioner::Request;
4102 let pos_id = resource.id();
4103 let Some(pos) = state.positioners.get_mut(&pos_id) else {
4104 return;
4105 };
4106 match request {
4107 Request::SetSize { width, height } => {
4108 pos.geometry.size = (width, height);
4109 }
4110 Request::SetAnchorRect {
4111 x,
4112 y,
4113 width,
4114 height,
4115 } => {
4116 pos.geometry.anchor_rect = (x, y, width, height);
4117 }
4118 Request::SetAnchor {
4119 anchor: wayland_server::WEnum::Value(v),
4120 } => {
4121 pos.geometry.anchor = v as u32;
4122 }
4123 Request::SetGravity {
4124 gravity: wayland_server::WEnum::Value(v),
4125 } => {
4126 pos.geometry.gravity = v as u32;
4127 }
4128 Request::SetOffset { x, y } => {
4129 pos.geometry.offset = (x, y);
4130 }
4131 Request::SetConstraintAdjustment {
4132 constraint_adjustment,
4133 } => {
4134 pos.geometry.constraint_adjustment = constraint_adjustment.into();
4135 }
4136 Request::Destroy => {
4137 state.positioners.remove(&pos_id);
4138 }
4139 _ => {}
4140 }
4141 }
4142}
4143
4144impl GlobalDispatch<ZxdgDecorationManagerV1, ()> for Compositor {
4146 fn bind(
4147 _: &mut Self,
4148 _: &DisplayHandle,
4149 _: &Client,
4150 resource: New<ZxdgDecorationManagerV1>,
4151 _: &(),
4152 data_init: &mut DataInit<'_, Self>,
4153 ) {
4154 data_init.init(resource, ());
4155 }
4156}
4157
4158impl Dispatch<ZxdgDecorationManagerV1, ()> for Compositor {
4159 fn request(
4160 _: &mut Self,
4161 _: &Client,
4162 _: &ZxdgDecorationManagerV1,
4163 request: <ZxdgDecorationManagerV1 as Resource>::Request,
4164 _: &(),
4165 _: &DisplayHandle,
4166 data_init: &mut DataInit<'_, Self>,
4167 ) {
4168 use zxdg_decoration_manager_v1::Request;
4169 match request {
4170 Request::GetToplevelDecoration { id, toplevel: _ } => {
4171 let decoration = data_init.init(id, ());
4172 decoration.configure(zxdg_toplevel_decoration_v1::Mode::ServerSide);
4174 }
4175 Request::Destroy => {}
4176 _ => {}
4177 }
4178 }
4179}
4180
4181impl Dispatch<ZxdgToplevelDecorationV1, ()> for Compositor {
4182 fn request(
4183 _: &mut Self,
4184 _: &Client,
4185 resource: &ZxdgToplevelDecorationV1,
4186 request: <ZxdgToplevelDecorationV1 as Resource>::Request,
4187 _: &(),
4188 _: &DisplayHandle,
4189 _: &mut DataInit<'_, Self>,
4190 ) {
4191 use zxdg_toplevel_decoration_v1::Request;
4192 match request {
4193 Request::SetMode { .. } | Request::UnsetMode => {
4194 resource.configure(zxdg_toplevel_decoration_v1::Mode::ServerSide);
4195 }
4196 Request::Destroy => {}
4197 _ => {}
4198 }
4199 }
4200}
4201
4202impl GlobalDispatch<WlShm, ()> for Compositor {
4204 fn bind(
4205 _: &mut Self,
4206 _: &DisplayHandle,
4207 _: &Client,
4208 resource: New<WlShm>,
4209 _: &(),
4210 data_init: &mut DataInit<'_, Self>,
4211 ) {
4212 let shm = data_init.init(resource, ());
4213 shm.format(wl_shm::Format::Argb8888);
4214 shm.format(wl_shm::Format::Xrgb8888);
4215 shm.format(wl_shm::Format::Abgr8888);
4216 shm.format(wl_shm::Format::Xbgr8888);
4217 }
4218}
4219
4220impl Dispatch<WlShm, ()> for Compositor {
4221 fn request(
4222 state: &mut Self,
4223 _: &Client,
4224 _: &WlShm,
4225 request: <WlShm as Resource>::Request,
4226 _: &(),
4227 _: &DisplayHandle,
4228 data_init: &mut DataInit<'_, Self>,
4229 ) {
4230 use wayland_server::protocol::wl_shm::Request;
4231 if let Request::CreatePool { id, fd, size } = request {
4232 let pool = data_init.init(id, ());
4233 let pool_id = pool.id();
4234 state
4235 .shm_pools
4236 .insert(pool_id, Arc::new(ShmPool::new(pool, fd, size)));
4237 }
4238 }
4239}
4240
4241impl Dispatch<WlShmPool, ()> for Compositor {
4243 fn request(
4244 state: &mut Self,
4245 _: &Client,
4246 resource: &WlShmPool,
4247 request: <WlShmPool as Resource>::Request,
4248 _: &(),
4249 _: &DisplayHandle,
4250 data_init: &mut DataInit<'_, Self>,
4251 ) {
4252 use wayland_server::protocol::wl_shm_pool::Request;
4253 let pool_id = resource.id();
4254 match request {
4255 Request::CreateBuffer {
4256 id,
4257 offset,
4258 width,
4259 height,
4260 stride,
4261 format,
4262 } => {
4263 let fmt = match format {
4265 wayland_server::WEnum::Value(f) => f,
4266 _ => wl_shm::Format::Argb8888, };
4268 let Some(pool) = state.shm_pools.get(&pool_id).cloned() else {
4269 return;
4270 };
4271 data_init.init(
4272 id,
4273 ShmBufferData {
4274 pool,
4275 offset,
4276 width,
4277 height,
4278 stride,
4279 format: fmt,
4280 },
4281 );
4282 }
4283 Request::Resize { size } => {
4284 if let Some(pool) = state.shm_pools.get(&pool_id) {
4285 pool.resize(size);
4286 }
4287 }
4288 Request::Destroy => {
4289 state.shm_pools.remove(&pool_id);
4292 }
4293 _ => {}
4294 }
4295 }
4296}
4297
4298impl Dispatch<WlBuffer, ShmBufferData> for Compositor {
4300 fn request(
4301 _: &mut Self,
4302 _: &Client,
4303 _: &WlBuffer,
4304 _: <WlBuffer as Resource>::Request,
4305 _: &ShmBufferData,
4306 _: &DisplayHandle,
4307 _: &mut DataInit<'_, Self>,
4308 ) {
4309 }
4310}
4311
4312impl Dispatch<WlBuffer, DmaBufBufferData> for Compositor {
4314 fn request(
4315 _: &mut Self,
4316 _: &Client,
4317 _: &WlBuffer,
4318 _: <WlBuffer as Resource>::Request,
4319 _: &DmaBufBufferData,
4320 _: &DisplayHandle,
4321 _: &mut DataInit<'_, Self>,
4322 ) {
4323 }
4324}
4325
4326impl GlobalDispatch<WlOutput, ()> for Compositor {
4328 fn bind(
4329 state: &mut Self,
4330 _: &DisplayHandle,
4331 _: &Client,
4332 resource: New<WlOutput>,
4333 _: &(),
4334 data_init: &mut DataInit<'_, Self>,
4335 ) {
4336 let output = data_init.init(resource, ());
4337 output.geometry(
4338 0,
4339 0,
4340 0,
4341 0,
4342 wl_output::Subpixel::Unknown,
4343 "Virtual".to_string(),
4344 "Headless".to_string(),
4345 wl_output::Transform::Normal,
4346 );
4347 let s120 = state.output_scale_120 as i32;
4348 let mode_w = state.output_width * s120 / 120;
4349 let mode_h = state.output_height * s120 / 120;
4350 output.mode(
4351 wl_output::Mode::Current | wl_output::Mode::Preferred,
4352 mode_w,
4353 mode_h,
4354 state.output_refresh_mhz as i32,
4355 );
4356 if output.version() >= 2 {
4357 output.scale(((state.output_scale_120 as i32) + 119) / 120);
4358 }
4359 if output.version() >= 2 {
4360 output.done();
4361 }
4362 state.outputs.push(output);
4363 }
4364}
4365
4366impl Dispatch<WlOutput, ()> for Compositor {
4367 fn request(
4368 state: &mut Self,
4369 _: &Client,
4370 resource: &WlOutput,
4371 request: <WlOutput as Resource>::Request,
4372 _: &(),
4373 _: &DisplayHandle,
4374 _: &mut DataInit<'_, Self>,
4375 ) {
4376 use wayland_server::protocol::wl_output::Request;
4377 if let Request::Release = request {
4378 state.outputs.retain(|o| o.id() != resource.id());
4379 }
4380 }
4381}
4382
4383impl GlobalDispatch<WlSeat, ()> for Compositor {
4385 fn bind(
4386 _: &mut Self,
4387 _: &DisplayHandle,
4388 _: &Client,
4389 resource: New<WlSeat>,
4390 _: &(),
4391 data_init: &mut DataInit<'_, Self>,
4392 ) {
4393 let seat = data_init.init(resource, ());
4394 seat.capabilities(wl_seat::Capability::Keyboard | wl_seat::Capability::Pointer);
4395 if seat.version() >= 2 {
4396 seat.name("headless".to_string());
4397 }
4398 }
4399}
4400
4401impl Dispatch<WlSeat, ()> for Compositor {
4402 fn request(
4403 state: &mut Self,
4404 _: &Client,
4405 _: &WlSeat,
4406 request: <WlSeat as Resource>::Request,
4407 _: &(),
4408 _: &DisplayHandle,
4409 data_init: &mut DataInit<'_, Self>,
4410 ) {
4411 use wayland_server::protocol::wl_seat::Request;
4412 match request {
4413 Request::GetKeyboard { id } => {
4414 let kb = data_init.init(id, ());
4415 if let Some(fd) = create_keymap_fd(&state.keyboard_keymap_data) {
4416 kb.keymap(
4417 wl_keyboard::KeymapFormat::XkbV1,
4418 fd.as_fd(),
4419 state.keyboard_keymap_data.len() as u32,
4420 );
4421 }
4422 if kb.version() >= 4 {
4423 kb.repeat_info(25, 200);
4424 }
4425 state.keyboards.push(kb);
4426 }
4427 Request::GetPointer { id } => {
4428 let ptr = data_init.init(id, ());
4429 let entered = state.pointer_entered_id.as_ref().and_then(|eid| {
4436 state
4437 .surfaces
4438 .values()
4439 .find(|s| s.wl_surface.id() == *eid)
4440 .map(|s| s.wl_surface.clone())
4441 });
4442 if let Some(wl) = entered
4443 && same_client(&ptr, &wl)
4444 {
4445 let serial = state.next_serial();
4446 let (lx, ly) = state.pointer_entered_local;
4447 ptr.enter(serial, &wl, lx, ly);
4448 ptr.frame();
4451 let _ = state.display_handle.flush_clients();
4452 }
4453 state.pointers.push(ptr);
4454 }
4455 Request::GetTouch { id } => {
4456 data_init.init(id, ());
4457 }
4458 Request::Release => {}
4459 _ => {}
4460 }
4461 }
4462}
4463
4464impl Dispatch<WlKeyboard, ()> for Compositor {
4466 fn request(
4467 state: &mut Self,
4468 _: &Client,
4469 resource: &WlKeyboard,
4470 request: <WlKeyboard as Resource>::Request,
4471 _: &(),
4472 _: &DisplayHandle,
4473 _: &mut DataInit<'_, Self>,
4474 ) {
4475 if let wl_keyboard::Request::Release = request {
4476 state.keyboards.retain(|k| k.id() != resource.id());
4477 }
4478 }
4479}
4480
4481impl Dispatch<WlPointer, ()> for Compositor {
4483 fn request(
4484 state: &mut Self,
4485 _: &Client,
4486 resource: &WlPointer,
4487 request: <WlPointer as Resource>::Request,
4488 _: &(),
4489 _: &DisplayHandle,
4490 _: &mut DataInit<'_, Self>,
4491 ) {
4492 use wl_pointer::Request;
4493 match request {
4494 Request::SetCursor {
4495 serial: _,
4496 surface,
4497 hotspot_x,
4498 hotspot_y,
4499 } => {
4500 if let Some(surface) = surface {
4501 let sid = surface.id();
4502 if let Some(surf) = state.surfaces.get_mut(&sid) {
4503 surf.is_cursor = true;
4504 surf.cursor_hotspot = (hotspot_x, hotspot_y);
4505 }
4506 } else {
4507 let _ = state.event_tx.send(CompositorEvent::SurfaceCursor {
4508 surface_id: state.focused_surface_id,
4509 cursor: CursorImage::Hidden,
4510 });
4511 }
4512 }
4513 Request::Release => {
4514 state.pointers.retain(|p| p.id() != resource.id());
4515 }
4516 _ => {}
4517 }
4518 }
4519}
4520
4521impl Dispatch<wayland_server::protocol::wl_touch::WlTouch, ()> for Compositor {
4523 fn request(
4524 _: &mut Self,
4525 _: &Client,
4526 _: &wayland_server::protocol::wl_touch::WlTouch,
4527 _: <wayland_server::protocol::wl_touch::WlTouch as Resource>::Request,
4528 _: &(),
4529 _: &DisplayHandle,
4530 _: &mut DataInit<'_, Self>,
4531 ) {
4532 }
4533}
4534
4535impl GlobalDispatch<ZwpLinuxDmabufV1, ()> for Compositor {
4537 fn bind(
4538 state: &mut Self,
4539 _: &DisplayHandle,
4540 _: &Client,
4541 resource: New<ZwpLinuxDmabufV1>,
4542 _: &(),
4543 data_init: &mut DataInit<'_, Self>,
4544 ) {
4545 let dmabuf = data_init.init(resource, ());
4546 if dmabuf.version() >= 4 {
4549 return;
4550 }
4551 if dmabuf.version() >= 3 {
4552 if let Some(ref vk) = state.vulkan_renderer
4558 && !vk.supported_dmabuf_modifiers.is_empty()
4559 {
4560 for &(drm_fmt, modifier) in &vk.supported_dmabuf_modifiers {
4561 let mod_hi = (modifier >> 32) as u32;
4562 let mod_lo = (modifier & 0xFFFFFFFF) as u32;
4563 dmabuf.modifier(drm_fmt, mod_hi, mod_lo);
4564 }
4565 }
4566 } else if state
4570 .vulkan_renderer
4571 .as_ref()
4572 .is_some_and(|vk| vk.has_dmabuf())
4573 {
4574 dmabuf.format(drm_fourcc::ARGB8888);
4575 dmabuf.format(drm_fourcc::XRGB8888);
4576 dmabuf.format(drm_fourcc::ABGR8888);
4577 dmabuf.format(drm_fourcc::XBGR8888);
4578 }
4579 }
4580}
4581
4582impl Dispatch<ZwpLinuxDmabufV1, ()> for Compositor {
4583 fn request(
4584 state: &mut Self,
4585 _: &Client,
4586 _: &ZwpLinuxDmabufV1,
4587 request: <ZwpLinuxDmabufV1 as Resource>::Request,
4588 _: &(),
4589 _: &DisplayHandle,
4590 data_init: &mut DataInit<'_, Self>,
4591 ) {
4592 use zwp_linux_dmabuf_v1::Request;
4593 match request {
4594 Request::CreateParams { params_id } => {
4595 data_init.init(params_id, ());
4596 }
4597 Request::GetDefaultFeedback { id } => {
4598 let fb = data_init.init(id, ());
4599 state.send_dmabuf_feedback(&fb);
4600 }
4601 Request::GetSurfaceFeedback { id, .. } => {
4602 let fb = data_init.init(id, ());
4603 state.send_dmabuf_feedback(&fb);
4604 }
4605 Request::Destroy => {}
4606 _ => {}
4607 }
4608 }
4609}
4610
4611impl Dispatch<ZwpLinuxDmabufFeedbackV1, ()> for Compositor {
4612 fn request(
4613 _: &mut Self,
4614 _: &Client,
4615 _: &ZwpLinuxDmabufFeedbackV1,
4616 _request: <ZwpLinuxDmabufFeedbackV1 as Resource>::Request,
4617 _: &(),
4618 _: &DisplayHandle,
4619 _data_init: &mut DataInit<'_, Self>,
4620 ) {
4621 }
4623}
4624
4625impl Dispatch<ZwpLinuxBufferParamsV1, ()> for Compositor {
4627 fn request(
4628 state: &mut Self,
4629 client: &Client,
4630 resource: &ZwpLinuxBufferParamsV1,
4631 request: <ZwpLinuxBufferParamsV1 as Resource>::Request,
4632 _: &(),
4633 dh: &DisplayHandle,
4634 data_init: &mut DataInit<'_, Self>,
4635 ) {
4636 use zwp_linux_buffer_params_v1::Request;
4637 let params_id = resource.id();
4638 match request {
4639 Request::Add {
4640 fd,
4641 plane_idx: _,
4642 offset,
4643 stride,
4644 modifier_hi,
4645 modifier_lo,
4646 } => {
4647 let modifier = ((modifier_hi as u64) << 32) | (modifier_lo as u64);
4648 let entry = state
4649 .dmabuf_params
4650 .entry(params_id.clone())
4651 .or_insert_with(|| DmaBufParamsPending {
4652 resource: resource.clone(),
4653 planes: Vec::new(),
4654 modifier,
4655 });
4656 entry.modifier = modifier;
4657 entry.planes.push(DmaBufPlane { fd, offset, stride });
4658 }
4659 Request::Create {
4660 width,
4661 height,
4662 format,
4663 flags,
4664 } => {
4665 let pending = state.dmabuf_params.remove(¶ms_id);
4666 let (planes, modifier) = match pending {
4667 Some(p) => (p.planes, p.modifier),
4668 None => {
4669 resource.failed();
4670 return;
4671 }
4672 };
4673 let y_invert = flags
4674 .into_result()
4675 .ok()
4676 .is_some_and(|f| f.contains(zwp_linux_buffer_params_v1::Flags::YInvert));
4677 match client.create_resource::<WlBuffer, DmaBufBufferData, Compositor>(
4678 dh,
4679 1,
4680 DmaBufBufferData {
4681 width,
4682 height,
4683 fourcc: format,
4684 modifier,
4685 planes,
4686 y_invert,
4687 },
4688 ) {
4689 Ok(buffer) => resource.created(&buffer),
4690 Err(_) => resource.failed(),
4691 }
4692 }
4693 Request::CreateImmed {
4694 buffer_id,
4695 width,
4696 height,
4697 format,
4698 flags,
4699 } => {
4700 let (planes, modifier) = state
4701 .dmabuf_params
4702 .remove(¶ms_id)
4703 .map(|p| (p.planes, p.modifier))
4704 .unwrap_or_default();
4705 let y_invert = flags
4706 .into_result()
4707 .ok()
4708 .is_some_and(|f| f.contains(zwp_linux_buffer_params_v1::Flags::YInvert));
4709 data_init.init(
4710 buffer_id,
4711 DmaBufBufferData {
4712 width,
4713 height,
4714 fourcc: format,
4715 modifier,
4716 planes,
4717 y_invert,
4718 },
4719 );
4720 }
4721 Request::Destroy => {
4722 state.dmabuf_params.remove(¶ms_id);
4723 }
4724 _ => {}
4725 }
4726 }
4727}
4728
4729impl GlobalDispatch<WpFractionalScaleManagerV1, ()> for Compositor {
4731 fn bind(
4732 _: &mut Self,
4733 _: &DisplayHandle,
4734 _: &Client,
4735 resource: New<WpFractionalScaleManagerV1>,
4736 _: &(),
4737 data_init: &mut DataInit<'_, Self>,
4738 ) {
4739 data_init.init(resource, ());
4740 }
4741}
4742
4743impl Dispatch<WpFractionalScaleManagerV1, ()> for Compositor {
4744 fn request(
4745 state: &mut Self,
4746 _: &Client,
4747 _: &WpFractionalScaleManagerV1,
4748 request: <WpFractionalScaleManagerV1 as Resource>::Request,
4749 _: &(),
4750 _: &DisplayHandle,
4751 data_init: &mut DataInit<'_, Self>,
4752 ) {
4753 use wp_fractional_scale_manager_v1::Request;
4754 match request {
4755 Request::GetFractionalScale { id, surface: _ } => {
4756 let fs = data_init.init(id, ());
4757 fs.preferred_scale(state.output_scale_120 as u32);
4759 state.fractional_scales.push(fs);
4760 }
4761 Request::Destroy => {}
4762 _ => {}
4763 }
4764 }
4765}
4766
4767impl Dispatch<WpFractionalScaleV1, ()> for Compositor {
4769 fn request(
4770 state: &mut Self,
4771 _: &Client,
4772 resource: &WpFractionalScaleV1,
4773 _: <WpFractionalScaleV1 as Resource>::Request,
4774 _: &(),
4775 _: &DisplayHandle,
4776 _: &mut DataInit<'_, Self>,
4777 ) {
4778 state
4780 .fractional_scales
4781 .retain(|fs| fs.id() != resource.id());
4782 }
4783}
4784
4785impl GlobalDispatch<WpViewporter, ()> for Compositor {
4787 fn bind(
4788 _: &mut Self,
4789 _: &DisplayHandle,
4790 _: &Client,
4791 resource: New<WpViewporter>,
4792 _: &(),
4793 data_init: &mut DataInit<'_, Self>,
4794 ) {
4795 data_init.init(resource, ());
4796 }
4797}
4798
4799impl Dispatch<WpViewporter, ()> for Compositor {
4800 fn request(
4801 _: &mut Self,
4802 _: &Client,
4803 _: &WpViewporter,
4804 request: <WpViewporter as Resource>::Request,
4805 _: &(),
4806 _: &DisplayHandle,
4807 data_init: &mut DataInit<'_, Self>,
4808 ) {
4809 use wp_viewporter::Request;
4810 match request {
4811 Request::GetViewport { id, surface } => {
4812 let obj_id = surface.id();
4815 data_init.init(id, obj_id);
4816 }
4817 Request::Destroy => {}
4818 _ => {}
4819 }
4820 }
4821}
4822
4823impl Dispatch<WpViewport, ObjectId> for Compositor {
4825 fn request(
4826 state: &mut Self,
4827 _: &Client,
4828 _: &WpViewport,
4829 request: <WpViewport as Resource>::Request,
4830 surface_obj_id: &ObjectId,
4831 _: &DisplayHandle,
4832 _: &mut DataInit<'_, Self>,
4833 ) {
4834 use wayland_protocols::wp::viewporter::server::wp_viewport::Request;
4835 match request {
4836 Request::SetDestination { width, height } => {
4837 if let Some(surf) = state.surfaces.get_mut(surface_obj_id) {
4838 if width > 0 && height > 0 {
4840 surf.pending_viewport_destination = Some((width, height));
4841 } else {
4842 surf.pending_viewport_destination = None;
4843 }
4844 }
4845 }
4846 Request::SetSource { .. } => {
4847 }
4849 Request::Destroy => {}
4850 _ => {}
4851 }
4852 }
4853}
4854
4855impl GlobalDispatch<WlDataDeviceManager, ()> for Compositor {
4862 fn bind(
4863 _: &mut Self,
4864 _: &DisplayHandle,
4865 _: &Client,
4866 resource: New<WlDataDeviceManager>,
4867 _: &(),
4868 data_init: &mut DataInit<'_, Self>,
4869 ) {
4870 data_init.init(resource, ());
4871 }
4872}
4873
4874impl Dispatch<WlDataDeviceManager, ()> for Compositor {
4875 fn request(
4876 state: &mut Self,
4877 _: &Client,
4878 _: &WlDataDeviceManager,
4879 request: <WlDataDeviceManager as Resource>::Request,
4880 _: &(),
4881 _: &DisplayHandle,
4882 data_init: &mut DataInit<'_, Self>,
4883 ) {
4884 use wl_data_device_manager::Request;
4885 match request {
4886 Request::CreateDataSource { id } => {
4887 data_init.init(
4888 id,
4889 DataSourceData {
4890 mime_types: std::sync::Mutex::new(Vec::new()),
4891 },
4892 );
4893 }
4894 Request::GetDataDevice { id, seat: _ } => {
4895 let dd = data_init.init(id, ());
4896 state.data_devices.push(dd);
4897 }
4898 _ => {}
4899 }
4900 }
4901}
4902
4903impl Dispatch<WlDataSource, DataSourceData> for Compositor {
4904 fn request(
4905 _: &mut Self,
4906 _: &Client,
4907 _: &WlDataSource,
4908 request: <WlDataSource as Resource>::Request,
4909 data: &DataSourceData,
4910 _: &DisplayHandle,
4911 _: &mut DataInit<'_, Self>,
4912 ) {
4913 use wl_data_source::Request;
4914 match request {
4915 Request::Offer { mime_type } => {
4916 data.mime_types.lock().unwrap().push(mime_type);
4917 }
4918 Request::Destroy => {}
4919 _ => {} }
4921 }
4922
4923 fn destroyed(
4924 state: &mut Self,
4925 _: wayland_server::backend::ClientId,
4926 resource: &WlDataSource,
4927 _: &DataSourceData,
4928 ) {
4929 if state
4930 .selection_source
4931 .as_ref()
4932 .is_some_and(|s| s.id() == resource.id())
4933 {
4934 state.selection_source = None;
4935 }
4936 }
4937}
4938
4939impl Dispatch<WlDataDevice, ()> for Compositor {
4940 fn request(
4941 state: &mut Self,
4942 _: &Client,
4943 _: &WlDataDevice,
4944 request: <WlDataDevice as Resource>::Request,
4945 _: &(),
4946 _: &DisplayHandle,
4947 _: &mut DataInit<'_, Self>,
4948 ) {
4949 use wl_data_device::Request;
4950 match request {
4951 Request::SetSelection { source, serial: _ } => {
4952 state.selection_source = source.clone();
4953 if let Some(ref src) = source {
4955 let data = src.data::<DataSourceData>().unwrap();
4956 let mimes = data.mime_types.lock().unwrap();
4957 let text_mime = mimes
4958 .iter()
4959 .find(|m| {
4960 m.as_str() == "text/plain;charset=utf-8"
4961 || m.as_str() == "text/plain"
4962 || m.as_str() == "UTF8_STRING"
4963 })
4964 .cloned();
4965 drop(mimes);
4966 if let Some(mime) = text_mime {
4967 state.read_data_source_and_emit(src, &mime);
4968 }
4969 }
4970 }
4971 Request::Release => {}
4972 _ => {} }
4974 }
4975
4976 fn destroyed(
4977 state: &mut Self,
4978 _: wayland_server::backend::ClientId,
4979 resource: &WlDataDevice,
4980 _: &(),
4981 ) {
4982 state.data_devices.retain(|d| d.id() != resource.id());
4983 }
4984}
4985
4986impl Dispatch<WlDataOffer, DataOfferData> for Compositor {
4987 fn request(
4988 state: &mut Self,
4989 _: &Client,
4990 _: &WlDataOffer,
4991 request: <WlDataOffer as Resource>::Request,
4992 data: &DataOfferData,
4993 _: &DisplayHandle,
4994 _: &mut DataInit<'_, Self>,
4995 ) {
4996 use wl_data_offer::Request;
4997 match request {
4998 Request::Receive { mime_type, fd } => {
4999 if data.external {
5000 if let Some(ref cb) = state.external_clipboard
5002 && (cb.mime_type == mime_type
5003 || mime_type == "text/plain"
5004 || mime_type == "text/plain;charset=utf-8"
5005 || mime_type == "UTF8_STRING")
5006 {
5007 use std::io::Write;
5008 let mut f = std::fs::File::from(fd);
5009 let _ = f.write_all(&cb.data);
5010 }
5011 } else if let Some(ref src) = state.selection_source {
5012 src.send(mime_type, fd.as_fd());
5014 }
5015 }
5016 Request::Destroy => {}
5017 _ => {} }
5019 }
5020}
5021
5022impl Compositor {
5023 fn read_data_source_and_emit(&mut self, source: &WlDataSource, mime_type: &str) {
5026 let mut fds = [0i32; 2];
5027 if unsafe { libc::pipe(fds.as_mut_ptr()) } != 0 {
5028 return;
5029 }
5030 let read_fd = unsafe { OwnedFd::from_raw_fd(fds[0]) };
5031 let write_fd = unsafe { OwnedFd::from_raw_fd(fds[1]) };
5032 source.send(mime_type.to_string(), write_fd.as_fd());
5033 let _ = self.display_handle.flush_clients();
5034 unsafe {
5036 libc::fcntl(read_fd.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
5037 }
5038 std::thread::sleep(std::time::Duration::from_millis(5));
5040 let mut buf = Vec::new();
5041 let mut tmp = [0u8; 8192];
5042 loop {
5043 let n = unsafe {
5044 libc::read(
5045 read_fd.as_raw_fd(),
5046 tmp.as_mut_ptr() as *mut libc::c_void,
5047 tmp.len(),
5048 )
5049 };
5050 if n <= 0 {
5051 break;
5052 }
5053 buf.extend_from_slice(&tmp[..n as usize]);
5054 if buf.len() > 1024 * 1024 {
5055 break; }
5057 }
5058 if !buf.is_empty() {
5059 let _ = self.event_tx.send(CompositorEvent::ClipboardContent {
5060 mime_type: mime_type.to_string(),
5061 data: buf,
5062 });
5063 (self.event_notify)();
5064 }
5065 }
5066
5067 fn offer_external_clipboard(&mut self) {
5069 let Some(ref cb) = self.external_clipboard else {
5070 return;
5071 };
5072 let mime = cb.mime_type.clone();
5073 for dd in &self.data_devices {
5074 if let Some(client) = dd.client() {
5075 let offer = client
5076 .create_resource::<WlDataOffer, DataOfferData, Compositor>(
5077 &self.display_handle,
5078 dd.version(),
5079 DataOfferData { external: true },
5080 )
5081 .unwrap();
5082 dd.data_offer(&offer);
5083 offer.offer(mime.clone());
5084 if mime.starts_with("text/plain") {
5086 if mime != "text/plain" {
5087 offer.offer("text/plain".to_string());
5088 }
5089 if mime != "text/plain;charset=utf-8" {
5090 offer.offer("text/plain;charset=utf-8".to_string());
5091 }
5092 offer.offer("UTF8_STRING".to_string());
5093 }
5094 dd.selection(Some(&offer));
5095 }
5096 }
5097 let _ = self.display_handle.flush_clients();
5098 }
5099}
5100
5101impl GlobalDispatch<ZwpPrimarySelectionDeviceManagerV1, ()> for Compositor {
5104 fn bind(
5105 _: &mut Self,
5106 _: &DisplayHandle,
5107 _: &Client,
5108 resource: New<ZwpPrimarySelectionDeviceManagerV1>,
5109 _: &(),
5110 data_init: &mut DataInit<'_, Self>,
5111 ) {
5112 data_init.init(resource, ());
5113 }
5114}
5115
5116impl Dispatch<ZwpPrimarySelectionDeviceManagerV1, ()> for Compositor {
5117 fn request(
5118 state: &mut Self,
5119 _: &Client,
5120 _: &ZwpPrimarySelectionDeviceManagerV1,
5121 request: <ZwpPrimarySelectionDeviceManagerV1 as Resource>::Request,
5122 _: &(),
5123 _: &DisplayHandle,
5124 data_init: &mut DataInit<'_, Self>,
5125 ) {
5126 use zwp_primary_selection_device_manager_v1::Request;
5127 match request {
5128 Request::CreateSource { id } => {
5129 data_init.init(
5130 id,
5131 PrimarySourceData {
5132 mime_types: std::sync::Mutex::new(Vec::new()),
5133 },
5134 );
5135 }
5136 Request::GetDevice { id, seat: _ } => {
5137 let pd = data_init.init(id, ());
5138 state.primary_devices.push(pd);
5139 }
5140 Request::Destroy => {}
5141 _ => {}
5142 }
5143 }
5144}
5145
5146impl Dispatch<ZwpPrimarySelectionSourceV1, PrimarySourceData> for Compositor {
5147 fn request(
5148 _: &mut Self,
5149 _: &Client,
5150 _: &ZwpPrimarySelectionSourceV1,
5151 request: <ZwpPrimarySelectionSourceV1 as Resource>::Request,
5152 data: &PrimarySourceData,
5153 _: &DisplayHandle,
5154 _: &mut DataInit<'_, Self>,
5155 ) {
5156 use zwp_primary_selection_source_v1::Request;
5157 match request {
5158 Request::Offer { mime_type } => {
5159 data.mime_types.lock().unwrap().push(mime_type);
5160 }
5161 Request::Destroy => {}
5162 _ => {}
5163 }
5164 }
5165
5166 fn destroyed(
5167 state: &mut Self,
5168 _: wayland_server::backend::ClientId,
5169 resource: &ZwpPrimarySelectionSourceV1,
5170 _: &PrimarySourceData,
5171 ) {
5172 if state
5173 .primary_source
5174 .as_ref()
5175 .is_some_and(|s| s.id() == resource.id())
5176 {
5177 state.primary_source = None;
5178 }
5179 }
5180}
5181
5182impl Dispatch<ZwpPrimarySelectionDeviceV1, ()> for Compositor {
5183 fn request(
5184 state: &mut Self,
5185 _: &Client,
5186 _: &ZwpPrimarySelectionDeviceV1,
5187 request: <ZwpPrimarySelectionDeviceV1 as Resource>::Request,
5188 _: &(),
5189 _: &DisplayHandle,
5190 _: &mut DataInit<'_, Self>,
5191 ) {
5192 use zwp_primary_selection_device_v1::Request;
5193 match request {
5194 Request::SetSelection { source, serial: _ } => {
5195 state.primary_source = source;
5196 }
5197 Request::Destroy => {}
5198 _ => {}
5199 }
5200 }
5201
5202 fn destroyed(
5203 state: &mut Self,
5204 _: wayland_server::backend::ClientId,
5205 resource: &ZwpPrimarySelectionDeviceV1,
5206 _: &(),
5207 ) {
5208 state.primary_devices.retain(|d| d.id() != resource.id());
5209 }
5210}
5211
5212impl Dispatch<ZwpPrimarySelectionOfferV1, PrimaryOfferData> for Compositor {
5213 fn request(
5214 state: &mut Self,
5215 _: &Client,
5216 _: &ZwpPrimarySelectionOfferV1,
5217 request: <ZwpPrimarySelectionOfferV1 as Resource>::Request,
5218 data: &PrimaryOfferData,
5219 _: &DisplayHandle,
5220 _: &mut DataInit<'_, Self>,
5221 ) {
5222 use zwp_primary_selection_offer_v1::Request;
5223 match request {
5224 Request::Receive { mime_type, fd } => {
5225 if data.external {
5226 if let Some(ref cb) = state.external_primary {
5227 use std::io::Write;
5228 let mut f = std::fs::File::from(fd);
5229 let _ = f.write_all(&cb.data);
5230 let _ = mime_type; }
5232 } else if let Some(ref src) = state.primary_source {
5233 src.send(mime_type, fd.as_fd());
5234 }
5235 }
5236 Request::Destroy => {}
5237 _ => {}
5238 }
5239 }
5240}
5241
5242impl GlobalDispatch<ZwpPointerConstraintsV1, ()> for Compositor {
5245 fn bind(
5246 _: &mut Self,
5247 _: &DisplayHandle,
5248 _: &Client,
5249 resource: New<ZwpPointerConstraintsV1>,
5250 _: &(),
5251 data_init: &mut DataInit<'_, Self>,
5252 ) {
5253 data_init.init(resource, ());
5254 }
5255}
5256
5257impl Dispatch<ZwpPointerConstraintsV1, ()> for Compositor {
5258 fn request(
5259 _: &mut Self,
5260 _: &Client,
5261 _: &ZwpPointerConstraintsV1,
5262 request: <ZwpPointerConstraintsV1 as Resource>::Request,
5263 _: &(),
5264 _: &DisplayHandle,
5265 data_init: &mut DataInit<'_, Self>,
5266 ) {
5267 use zwp_pointer_constraints_v1::Request;
5268 match request {
5269 Request::LockPointer {
5270 id,
5271 surface: _,
5272 pointer: _,
5273 region: _,
5274 lifetime: _,
5275 } => {
5276 let lp = data_init.init(id, ());
5277 lp.locked();
5279 }
5280 Request::ConfinePointer {
5281 id,
5282 surface: _,
5283 pointer: _,
5284 region: _,
5285 lifetime: _,
5286 } => {
5287 let cp = data_init.init(id, ());
5288 cp.confined();
5289 }
5290 Request::Destroy => {}
5291 _ => {}
5292 }
5293 }
5294}
5295
5296impl Dispatch<ZwpLockedPointerV1, ()> for Compositor {
5297 fn request(
5298 _: &mut Self,
5299 _: &Client,
5300 _: &ZwpLockedPointerV1,
5301 _: <ZwpLockedPointerV1 as Resource>::Request,
5302 _: &(),
5303 _: &DisplayHandle,
5304 _: &mut DataInit<'_, Self>,
5305 ) {
5306 }
5308}
5309
5310impl Dispatch<ZwpConfinedPointerV1, ()> for Compositor {
5311 fn request(
5312 _: &mut Self,
5313 _: &Client,
5314 _: &ZwpConfinedPointerV1,
5315 _: <ZwpConfinedPointerV1 as Resource>::Request,
5316 _: &(),
5317 _: &DisplayHandle,
5318 _: &mut DataInit<'_, Self>,
5319 ) {
5320 }
5322}
5323
5324impl GlobalDispatch<ZwpRelativePointerManagerV1, ()> for Compositor {
5327 fn bind(
5328 _: &mut Self,
5329 _: &DisplayHandle,
5330 _: &Client,
5331 resource: New<ZwpRelativePointerManagerV1>,
5332 _: &(),
5333 data_init: &mut DataInit<'_, Self>,
5334 ) {
5335 data_init.init(resource, ());
5336 }
5337}
5338
5339impl Dispatch<ZwpRelativePointerManagerV1, ()> for Compositor {
5340 fn request(
5341 state: &mut Self,
5342 _: &Client,
5343 _: &ZwpRelativePointerManagerV1,
5344 request: <ZwpRelativePointerManagerV1 as Resource>::Request,
5345 _: &(),
5346 _: &DisplayHandle,
5347 data_init: &mut DataInit<'_, Self>,
5348 ) {
5349 use zwp_relative_pointer_manager_v1::Request;
5350 match request {
5351 Request::GetRelativePointer { id, pointer: _ } => {
5352 let rp = data_init.init(id, ());
5353 state.relative_pointers.push(rp);
5354 }
5355 Request::Destroy => {}
5356 _ => {}
5357 }
5358 }
5359}
5360
5361impl Dispatch<ZwpRelativePointerV1, ()> for Compositor {
5362 fn request(
5363 state: &mut Self,
5364 _: &Client,
5365 resource: &ZwpRelativePointerV1,
5366 _: <ZwpRelativePointerV1 as Resource>::Request,
5367 _: &(),
5368 _: &DisplayHandle,
5369 _: &mut DataInit<'_, Self>,
5370 ) {
5371 state
5373 .relative_pointers
5374 .retain(|rp| rp.id() != resource.id());
5375 }
5376}
5377
5378impl GlobalDispatch<ZwpTextInputManagerV3, ()> for Compositor {
5381 fn bind(
5382 _: &mut Self,
5383 _: &DisplayHandle,
5384 _: &Client,
5385 resource: New<ZwpTextInputManagerV3>,
5386 _: &(),
5387 data_init: &mut DataInit<'_, Self>,
5388 ) {
5389 data_init.init(resource, ());
5390 }
5391}
5392
5393impl Dispatch<ZwpTextInputManagerV3, ()> for Compositor {
5394 fn request(
5395 state: &mut Self,
5396 _: &Client,
5397 _: &ZwpTextInputManagerV3,
5398 request: <ZwpTextInputManagerV3 as Resource>::Request,
5399 _: &(),
5400 _: &DisplayHandle,
5401 data_init: &mut DataInit<'_, Self>,
5402 ) {
5403 use zwp_text_input_manager_v3::Request;
5404 match request {
5405 Request::GetTextInput { id, seat: _ } => {
5406 let ti = data_init.init(id, ());
5407 state.text_inputs.push(TextInputState {
5408 resource: ti,
5409 enabled: false,
5410 });
5411 }
5412 Request::Destroy => {}
5413 _ => {}
5414 }
5415 }
5416}
5417
5418impl Dispatch<ZwpTextInputV3, ()> for Compositor {
5419 fn request(
5420 state: &mut Self,
5421 _: &Client,
5422 resource: &ZwpTextInputV3,
5423 request: <ZwpTextInputV3 as Resource>::Request,
5424 _: &(),
5425 _: &DisplayHandle,
5426 _: &mut DataInit<'_, Self>,
5427 ) {
5428 use zwp_text_input_v3::Request;
5429 match request {
5430 Request::Enable => {
5431 if let Some(ti) = state
5432 .text_inputs
5433 .iter_mut()
5434 .find(|t| t.resource.id() == resource.id())
5435 {
5436 ti.enabled = true;
5437 }
5438 }
5439 Request::Disable => {
5440 if let Some(ti) = state
5441 .text_inputs
5442 .iter_mut()
5443 .find(|t| t.resource.id() == resource.id())
5444 {
5445 ti.enabled = false;
5446 }
5447 }
5448 Request::Commit => {
5449 }
5451 Request::Destroy => {
5452 state
5453 .text_inputs
5454 .retain(|t| t.resource.id() != resource.id());
5455 }
5456 _ => {}
5459 }
5460 }
5461}
5462
5463impl GlobalDispatch<XdgActivationV1, ()> for Compositor {
5466 fn bind(
5467 _: &mut Self,
5468 _: &DisplayHandle,
5469 _: &Client,
5470 resource: New<XdgActivationV1>,
5471 _: &(),
5472 data_init: &mut DataInit<'_, Self>,
5473 ) {
5474 data_init.init(resource, ());
5475 }
5476}
5477
5478impl Dispatch<XdgActivationV1, ()> for Compositor {
5479 fn request(
5480 state: &mut Self,
5481 _: &Client,
5482 _: &XdgActivationV1,
5483 request: <XdgActivationV1 as Resource>::Request,
5484 _: &(),
5485 _: &DisplayHandle,
5486 data_init: &mut DataInit<'_, Self>,
5487 ) {
5488 use xdg_activation_v1::Request;
5489 match request {
5490 Request::GetActivationToken { id } => {
5491 let serial = state.next_activation_token;
5492 state.next_activation_token = serial.wrapping_add(1);
5493 data_init.init(id, ActivationTokenData { serial });
5494 }
5495 Request::Activate {
5496 token: _,
5497 surface: _,
5498 } => {
5499 }
5502 Request::Destroy => {}
5503 _ => {}
5504 }
5505 }
5506}
5507
5508impl Dispatch<XdgActivationTokenV1, ActivationTokenData> for Compositor {
5509 fn request(
5510 _: &mut Self,
5511 _: &Client,
5512 resource: &XdgActivationTokenV1,
5513 request: <XdgActivationTokenV1 as Resource>::Request,
5514 data: &ActivationTokenData,
5515 _: &DisplayHandle,
5516 _: &mut DataInit<'_, Self>,
5517 ) {
5518 use xdg_activation_token_v1::Request;
5519 match request {
5520 Request::Commit => {
5521 resource.done(format!("blit-token-{}", data.serial));
5524 }
5525 Request::SetSerial { .. } | Request::SetAppId { .. } | Request::SetSurface { .. } => {}
5526 Request::Destroy => {}
5527 _ => {}
5528 }
5529 }
5530}
5531
5532impl GlobalDispatch<WpCursorShapeManagerV1, ()> for Compositor {
5535 fn bind(
5536 _: &mut Self,
5537 _: &DisplayHandle,
5538 _: &Client,
5539 resource: New<WpCursorShapeManagerV1>,
5540 _: &(),
5541 data_init: &mut DataInit<'_, Self>,
5542 ) {
5543 data_init.init(resource, ());
5544 }
5545}
5546
5547impl Dispatch<WpCursorShapeManagerV1, ()> for Compositor {
5548 fn request(
5549 _: &mut Self,
5550 _: &Client,
5551 _: &WpCursorShapeManagerV1,
5552 request: <WpCursorShapeManagerV1 as Resource>::Request,
5553 _: &(),
5554 _: &DisplayHandle,
5555 data_init: &mut DataInit<'_, Self>,
5556 ) {
5557 use wp_cursor_shape_manager_v1::Request;
5558 match request {
5559 Request::GetPointer {
5560 cursor_shape_device,
5561 pointer: _,
5562 } => {
5563 data_init.init(cursor_shape_device, ());
5564 }
5565 Request::GetTabletToolV2 {
5566 cursor_shape_device,
5567 tablet_tool: _,
5568 } => {
5569 data_init.init(cursor_shape_device, ());
5570 }
5571 Request::Destroy => {}
5572 _ => {}
5573 }
5574 }
5575}
5576
5577impl Dispatch<WpCursorShapeDeviceV1, ()> for Compositor {
5578 fn request(
5579 state: &mut Self,
5580 _: &Client,
5581 _: &WpCursorShapeDeviceV1,
5582 request: <WpCursorShapeDeviceV1 as Resource>::Request,
5583 _: &(),
5584 _: &DisplayHandle,
5585 _: &mut DataInit<'_, Self>,
5586 ) {
5587 use wp_cursor_shape_device_v1::Request;
5588 match request {
5589 Request::SetShape { serial: _, shape } => {
5590 use wayland_server::WEnum;
5591 use wp_cursor_shape_device_v1::Shape;
5592 let name = match shape {
5593 WEnum::Value(Shape::Default) => "default",
5594 WEnum::Value(Shape::ContextMenu) => "context-menu",
5595 WEnum::Value(Shape::Help) => "help",
5596 WEnum::Value(Shape::Pointer) => "pointer",
5597 WEnum::Value(Shape::Progress) => "progress",
5598 WEnum::Value(Shape::Wait) => "wait",
5599 WEnum::Value(Shape::Cell) => "cell",
5600 WEnum::Value(Shape::Crosshair) => "crosshair",
5601 WEnum::Value(Shape::Text) => "text",
5602 WEnum::Value(Shape::VerticalText) => "vertical-text",
5603 WEnum::Value(Shape::Alias) => "alias",
5604 WEnum::Value(Shape::Copy) => "copy",
5605 WEnum::Value(Shape::Move) => "move",
5606 WEnum::Value(Shape::NoDrop) => "no-drop",
5607 WEnum::Value(Shape::NotAllowed) => "not-allowed",
5608 WEnum::Value(Shape::Grab) => "grab",
5609 WEnum::Value(Shape::Grabbing) => "grabbing",
5610 WEnum::Value(Shape::EResize) => "e-resize",
5611 WEnum::Value(Shape::NResize) => "n-resize",
5612 WEnum::Value(Shape::NeResize) => "ne-resize",
5613 WEnum::Value(Shape::NwResize) => "nw-resize",
5614 WEnum::Value(Shape::SResize) => "s-resize",
5615 WEnum::Value(Shape::SeResize) => "se-resize",
5616 WEnum::Value(Shape::SwResize) => "sw-resize",
5617 WEnum::Value(Shape::WResize) => "w-resize",
5618 WEnum::Value(Shape::EwResize) => "ew-resize",
5619 WEnum::Value(Shape::NsResize) => "ns-resize",
5620 WEnum::Value(Shape::NeswResize) => "nesw-resize",
5621 WEnum::Value(Shape::NwseResize) => "nwse-resize",
5622 WEnum::Value(Shape::ColResize) => "col-resize",
5623 WEnum::Value(Shape::RowResize) => "row-resize",
5624 WEnum::Value(Shape::AllScroll) => "all-scroll",
5625 WEnum::Value(Shape::ZoomIn) => "zoom-in",
5626 WEnum::Value(Shape::ZoomOut) => "zoom-out",
5627 _ => "default",
5628 };
5629 let _ = state.event_tx.send(CompositorEvent::SurfaceCursor {
5630 surface_id: state.focused_surface_id,
5631 cursor: CursorImage::Named(name.to_string()),
5632 });
5633 (state.event_notify)();
5634 }
5635 Request::Destroy => {}
5636 _ => {}
5637 }
5638 }
5639}
5640
5641impl wayland_server::backend::ClientData for ClientState {
5643 fn initialized(&self, _: wayland_server::backend::ClientId) {}
5644 fn disconnected(
5645 &self,
5646 _: wayland_server::backend::ClientId,
5647 _: wayland_server::backend::DisconnectReason,
5648 ) {
5649 }
5650}
5651
5652pub struct CompositorHandle {
5657 pub event_rx: mpsc::Receiver<CompositorEvent>,
5658 pub command_tx: mpsc::Sender<CompositorCommand>,
5659 pub socket_name: String,
5660 pub thread: std::thread::JoinHandle<()>,
5661 pub shutdown: Arc<AtomicBool>,
5662 pub vulkan_video_encode: bool,
5664 pub vulkan_video_encode_av1: bool,
5666 loop_signal: LoopSignal,
5667}
5668
5669impl CompositorHandle {
5670 pub fn wake(&self) {
5671 self.loop_signal.wakeup();
5672 }
5673}
5674
5675pub fn spawn_compositor(
5676 verbose: bool,
5677 event_notify: Arc<dyn Fn() + Send + Sync>,
5678 gpu_device: &str,
5679) -> CompositorHandle {
5680 let _gpu_device = gpu_device.to_string();
5681 let (event_tx, event_rx) = mpsc::channel();
5682 let (command_tx, command_rx) = mpsc::channel();
5683 let (socket_tx, socket_rx) = mpsc::sync_channel(1);
5684 let (signal_tx, signal_rx) = mpsc::sync_channel::<LoopSignal>(1);
5685 let (caps_tx, caps_rx) = mpsc::sync_channel::<(bool, bool)>(1);
5686 let shutdown = Arc::new(AtomicBool::new(false));
5687 let shutdown_clone = shutdown.clone();
5688
5689 let runtime_dir = std::env::var_os("XDG_RUNTIME_DIR")
5690 .map(std::path::PathBuf::from)
5691 .filter(|p| {
5692 let probe = p.join(".blit-probe");
5693 if std::fs::write(&probe, b"").is_ok() {
5694 let _ = std::fs::remove_file(&probe);
5695 true
5696 } else {
5697 false
5698 }
5699 })
5700 .unwrap_or_else(std::env::temp_dir);
5701
5702 let runtime_dir_clone = runtime_dir.clone();
5703 let thread = std::thread::Builder::new()
5704 .name("compositor".into())
5705 .spawn(move || {
5706 unsafe { std::env::set_var("XDG_RUNTIME_DIR", &runtime_dir_clone) };
5707 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
5708 run_compositor(
5709 event_tx,
5710 command_rx,
5711 socket_tx,
5712 signal_tx,
5713 caps_tx,
5714 event_notify,
5715 shutdown_clone,
5716 verbose,
5717 _gpu_device,
5718 );
5719 }));
5720 if let Err(e) = result {
5721 let msg = if let Some(s) = e.downcast_ref::<&str>() {
5722 s.to_string()
5723 } else if let Some(s) = e.downcast_ref::<String>() {
5724 s.clone()
5725 } else {
5726 "unknown panic".to_string()
5727 };
5728 eprintln!("[compositor] PANIC: {msg}");
5729 }
5730 })
5731 .expect("failed to spawn compositor thread");
5732
5733 let socket_name = socket_rx.recv().expect("compositor failed to start");
5734 let socket_name = runtime_dir
5735 .join(&socket_name)
5736 .to_string_lossy()
5737 .into_owned();
5738 let loop_signal = signal_rx
5739 .recv()
5740 .expect("compositor failed to send loop signal");
5741 let (vulkan_video_encode, vulkan_video_encode_av1) = caps_rx.recv().unwrap_or((false, false));
5742
5743 CompositorHandle {
5744 event_rx,
5745 command_tx,
5746 socket_name,
5747 thread,
5748 shutdown,
5749 vulkan_video_encode,
5750 vulkan_video_encode_av1,
5751 loop_signal,
5752 }
5753}
5754
5755#[allow(clippy::too_many_arguments)]
5756fn run_compositor(
5757 event_tx: mpsc::Sender<CompositorEvent>,
5758 command_rx: mpsc::Receiver<CompositorCommand>,
5759 socket_tx: mpsc::SyncSender<String>,
5760 signal_tx: mpsc::SyncSender<LoopSignal>,
5761 caps_tx: mpsc::SyncSender<(bool, bool)>,
5762 event_notify: Arc<dyn Fn() + Send + Sync>,
5763 shutdown: Arc<AtomicBool>,
5764 verbose: bool,
5765 gpu_device: String,
5766) {
5767 let mut event_loop: EventLoop<Compositor> =
5768 EventLoop::try_new().expect("failed to create event loop");
5769 let loop_signal = event_loop.get_signal();
5770
5771 let display: Display<Compositor> = Display::new().expect("failed to create display");
5772 let dh = display.handle();
5773
5774 eprintln!("[compositor] trying Vulkan renderer for {gpu_device}");
5777 let vulkan_renderer = super::vulkan_render::VulkanRenderer::try_new(&gpu_device);
5778 let has_dmabuf = vulkan_renderer.as_ref().is_some_and(|vk| vk.has_dmabuf());
5779 eprintln!(
5780 "[compositor] Vulkan renderer: {} (dmabuf={})",
5781 vulkan_renderer.is_some(),
5782 has_dmabuf,
5783 );
5784 if vulkan_renderer.is_none() {
5785 eprintln!(
5786 "[compositor] WARNING: no Vulkan renderer — clients can connect but NO frames will be composited (windows will never appear)."
5787 );
5788 eprintln!(
5789 "[compositor] WARNING: install a Vulkan driver; on GPU-less hosts a software driver works (e.g. `apt install mesa-vulkan-drivers` for lavapipe)."
5790 );
5791 }
5792
5793 dh.create_global::<Compositor, WlCompositor, ()>(6, ());
5795 dh.create_global::<Compositor, WlSubcompositor, ()>(1, ());
5796 dh.create_global::<Compositor, XdgWmBase, ()>(6, ());
5797 dh.create_global::<Compositor, WlShm, ()>(1, ());
5798 dh.create_global::<Compositor, WlOutput, ()>(4, ());
5799 dh.create_global::<Compositor, WlSeat, ()>(9, ());
5800 if has_dmabuf {
5805 dh.create_global::<Compositor, ZwpLinuxDmabufV1, ()>(4, ());
5806 }
5807 dh.create_global::<Compositor, WpViewporter, ()>(1, ());
5808 dh.create_global::<Compositor, WpFractionalScaleManagerV1, ()>(1, ());
5809 dh.create_global::<Compositor, ZxdgDecorationManagerV1, ()>(1, ());
5810 dh.create_global::<Compositor, WlDataDeviceManager, ()>(3, ());
5811 dh.create_global::<Compositor, ZwpPointerConstraintsV1, ()>(1, ());
5812 dh.create_global::<Compositor, ZwpRelativePointerManagerV1, ()>(1, ());
5813 dh.create_global::<Compositor, XdgActivationV1, ()>(1, ());
5814 dh.create_global::<Compositor, WpCursorShapeManagerV1, ()>(1, ());
5815 dh.create_global::<Compositor, ZwpPrimarySelectionDeviceManagerV1, ()>(1, ());
5816 dh.create_global::<Compositor, WpPresentation, ()>(1, ());
5817 dh.create_global::<Compositor, ZwpTextInputManagerV3, ()>(1, ());
5818
5819 let keymap_string = include_str!("../data/us-qwerty.xkb");
5821 let mut keymap_data = keymap_string.as_bytes().to_vec();
5822 keymap_data.push(0); let listening_socket = wayland_server::ListeningSocket::bind_auto("wayland", 0..33)
5826 .unwrap_or_else(|e| {
5827 let dir = std::env::var("XDG_RUNTIME_DIR").unwrap_or_else(|_| "(unset)".into());
5828 panic!("failed to create wayland socket in XDG_RUNTIME_DIR={dir}: {e}\nhint: ensure the directory exists and is writable by the current user");
5829 });
5830 let socket_name = listening_socket
5831 .socket_name()
5832 .unwrap()
5833 .to_string_lossy()
5834 .into_owned();
5835 socket_tx.send(socket_name).unwrap();
5836 let _ = signal_tx.send(loop_signal.clone());
5837
5838 let mut compositor = Compositor {
5839 display_handle: dh,
5840 surfaces: HashMap::new(),
5841 regions: HashMap::new(),
5842 toplevel_surface_ids: HashMap::new(),
5843 last_request_frame_ms: HashMap::new(),
5844 next_surface_id: 1,
5845 shm_pools: HashMap::new(),
5846 surface_meta: HashMap::new(),
5847 dmabuf_params: HashMap::new(),
5848 vulkan_renderer,
5849 output_width: 1920,
5850 output_height: 1080,
5851 output_refresh_mhz: 60_000,
5852 output_scale_120: 120,
5853 outputs: Vec::new(),
5854 keyboards: Vec::new(),
5855 pointers: Vec::new(),
5856 keyboard_keymap_data: keymap_data,
5857 mods_depressed: 0,
5858 mods_locked: 0,
5859 serial: 0,
5860 event_tx,
5861 event_notify,
5862 loop_signal: loop_signal.clone(),
5863 pending_commits: HashMap::new(),
5864 pending_native_sizes: HashMap::new(),
5865 pending_recomposite_toplevels: HashSet::new(),
5866 focused_surface_id: 0,
5867 pointer_entered_id: None,
5868 pointer_entered_local: (0.0, 0.0),
5869 pending_kb_reenter: false,
5870 gpu_device,
5871 verbose,
5872 shutdown: shutdown.clone(),
5873 last_reported_size: HashMap::new(),
5874 surface_sizes: HashMap::new(),
5875 positioners: HashMap::new(),
5876 fractional_scales: Vec::new(),
5877 data_devices: Vec::new(),
5878 selection_source: None,
5879 external_clipboard: None,
5880 primary_devices: Vec::new(),
5881 primary_source: None,
5882 external_primary: None,
5883 relative_pointers: Vec::new(),
5884 text_inputs: Vec::new(),
5885 text_input_serial: 0,
5886 next_activation_token: 1,
5887 popup_grab_stack: Vec::new(),
5888 kb_focus_popup: None,
5889 popup_dismiss_button: None,
5890 held_buffers: HashMap::new(),
5891 cursor_rgba: HashMap::new(),
5892 };
5893
5894 {
5896 let (vve, vve_av1) = compositor
5897 .vulkan_renderer
5898 .as_ref()
5899 .map(|vk| (vk.has_video_encode(), vk.has_video_encode_av1()))
5900 .unwrap_or((false, false));
5901 let _ = caps_tx.send((vve, vve_av1));
5902 }
5903
5904 let handle = event_loop.handle();
5905
5906 let display_source = Generic::new(display, Interest::READ, calloop::Mode::Level);
5908 handle
5909 .insert_source(display_source, |_, display, state| {
5910 let d = unsafe { display.get_mut() };
5911 if let Err(e) = d.dispatch_clients(state)
5912 && state.verbose
5913 {
5914 eprintln!("[compositor] dispatch_clients error: {e}");
5915 }
5916 state.cleanup_dead_surfaces();
5917 if let Err(e) = d.flush_clients()
5918 && state.verbose
5919 {
5920 eprintln!("[compositor] flush_clients error: {e}");
5921 }
5922 Ok(PostAction::Continue)
5923 })
5924 .expect("failed to insert display source");
5925
5926 let socket_source = Generic::new(listening_socket, Interest::READ, calloop::Mode::Level);
5928 handle
5929 .insert_source(socket_source, |_, socket, state| {
5930 let ls = unsafe { socket.get_mut() };
5931 if let Some(client_stream) = ls.accept().ok().flatten()
5932 && let Err(e) = state
5933 .display_handle
5934 .insert_client(client_stream, Arc::new(ClientState))
5935 && state.verbose
5936 {
5937 eprintln!("[compositor] insert_client error: {e}");
5938 }
5939 Ok(PostAction::Continue)
5940 })
5941 .expect("failed to insert listening socket");
5942
5943 if verbose {
5944 eprintln!("[compositor] entering event loop");
5945 }
5946
5947 while !shutdown.load(Ordering::Relaxed) {
5948 while let Ok(cmd) = command_rx.try_recv() {
5950 match cmd {
5951 CompositorCommand::Shutdown => {
5952 shutdown.store(true, Ordering::Relaxed);
5953 return;
5954 }
5955 other => compositor.handle_command(other),
5956 }
5957 }
5958
5959 let poll_timeout = if compositor
5962 .vulkan_renderer
5963 .as_ref()
5964 .is_some_and(|vk| vk.has_pending())
5965 {
5966 std::time::Duration::from_millis(1)
5967 } else {
5968 std::time::Duration::from_secs(1)
5969 };
5970
5971 if let Err(e) = event_loop.dispatch(Some(poll_timeout), &mut compositor)
5972 && verbose
5973 {
5974 eprintln!("[compositor] event loop error: {e}");
5975 }
5976
5977 if let Some(ref mut vk) = compositor.vulkan_renderer {
5983 let retired = vk.try_retire_pending();
5984 if !retired.is_empty() {
5985 let s120_u32 = (compositor.output_scale_120 as u32).max(120);
5986 if let Some(&(sid, nw, nh, _)) = retired
5990 .iter()
5991 .max_by_key(|&&(_, w, h, _)| (w as u64) * (h as u64))
5992 {
5993 let log_w = (nw * 120).div_ceil(s120_u32);
5994 let log_h = (nh * 120).div_ceil(s120_u32);
5995 compositor
5996 .pending_native_sizes
5997 .entry(sid)
5998 .or_insert((nw, nh, log_w, log_h));
5999 }
6000 for (sid, w, h, pixels) in retired {
6001 let log_w = (w * 120).div_ceil(s120_u32);
6002 let log_h = (h * 120).div_ceil(s120_u32);
6003 compositor
6004 .pending_commits
6005 .insert((sid, w, h), (log_w, log_h, pixels));
6006 }
6007 }
6008 }
6009
6010 let can_recomposite = compositor
6019 .vulkan_renderer
6020 .as_ref()
6021 .is_some_and(|vk| !vk.has_pending());
6022 if can_recomposite
6023 && let Some(&sid) = compositor.pending_recomposite_toplevels.iter().next()
6024 {
6025 compositor.pending_recomposite_toplevels.remove(&sid);
6026 if let Some(root_id) = compositor.toplevel_surface_ids.get(&sid).cloned() {
6027 compositor.composite_toplevel_into_pending(&root_id, sid);
6028 loop_signal.wakeup();
6033 }
6034 }
6035
6036 if !compositor.pending_commits.is_empty() || !compositor.pending_native_sizes.is_empty() {
6037 compositor.flush_pending_commits();
6038 }
6039
6040 if let Err(e) = compositor.display_handle.flush_clients()
6041 && verbose
6042 {
6043 eprintln!("[compositor] flush error: {e}");
6044 }
6045 }
6046
6047 if verbose {
6048 eprintln!("[compositor] event loop exited");
6049 }
6050}