1use crate::positioner::PositionerGeometry;
9use std::collections::HashMap;
10use std::os::fd::{AsFd, AsRawFd, FromRawFd, OwnedFd};
11use std::sync::Arc;
12use std::sync::atomic::{AtomicBool, Ordering};
13use std::sync::mpsc;
14
15use calloop::generic::Generic;
16use calloop::{EventLoop, Interest, LoopSignal, PostAction};
17use wayland_protocols::wp::cursor_shape::v1::server::wp_cursor_shape_device_v1::{
18 self, WpCursorShapeDeviceV1,
19};
20use wayland_protocols::wp::cursor_shape::v1::server::wp_cursor_shape_manager_v1::{
21 self, WpCursorShapeManagerV1,
22};
23use wayland_protocols::wp::fractional_scale::v1::server::wp_fractional_scale_manager_v1::{
24 self, WpFractionalScaleManagerV1,
25};
26use wayland_protocols::wp::fractional_scale::v1::server::wp_fractional_scale_v1::WpFractionalScaleV1;
27use wayland_protocols::wp::presentation_time::server::wp_presentation::{
28 self, WpPresentation,
29};
30use wayland_protocols::wp::presentation_time::server::wp_presentation_feedback::{
31 Kind as WpPresentationFeedbackKind, WpPresentationFeedback,
32};
33use wayland_protocols::wp::linux_dmabuf::zv1::server::zwp_linux_buffer_params_v1::{
34 self, ZwpLinuxBufferParamsV1,
35};
36use wayland_protocols::wp::linux_dmabuf::zv1::server::zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1;
37use wayland_protocols::wp::linux_dmabuf::zv1::server::zwp_linux_dmabuf_v1::{
38 self, ZwpLinuxDmabufV1,
39};
40use wayland_protocols::wp::pointer_constraints::zv1::server::zwp_confined_pointer_v1::ZwpConfinedPointerV1;
41use wayland_protocols::wp::pointer_constraints::zv1::server::zwp_locked_pointer_v1::ZwpLockedPointerV1;
42use wayland_protocols::wp::pointer_constraints::zv1::server::zwp_pointer_constraints_v1::{
43 self, ZwpPointerConstraintsV1,
44};
45use wayland_protocols::wp::primary_selection::zv1::server::zwp_primary_selection_device_manager_v1::{
46 self, ZwpPrimarySelectionDeviceManagerV1,
47};
48use wayland_protocols::wp::primary_selection::zv1::server::zwp_primary_selection_device_v1::{
49 self, ZwpPrimarySelectionDeviceV1,
50};
51use wayland_protocols::wp::primary_selection::zv1::server::zwp_primary_selection_offer_v1::{
52 self, ZwpPrimarySelectionOfferV1,
53};
54use wayland_protocols::wp::primary_selection::zv1::server::zwp_primary_selection_source_v1::{
55 self, ZwpPrimarySelectionSourceV1,
56};
57use wayland_protocols::wp::relative_pointer::zv1::server::zwp_relative_pointer_manager_v1::{
58 self, ZwpRelativePointerManagerV1,
59};
60use wayland_protocols::wp::relative_pointer::zv1::server::zwp_relative_pointer_v1::ZwpRelativePointerV1;
61use wayland_protocols::wp::text_input::zv3::server::zwp_text_input_manager_v3::{
62 self, ZwpTextInputManagerV3,
63};
64use wayland_protocols::wp::text_input::zv3::server::zwp_text_input_v3::{
65 self, ZwpTextInputV3,
66};
67use wayland_protocols::wp::viewporter::server::wp_viewport::WpViewport;
68use wayland_protocols::wp::viewporter::server::wp_viewporter::{self, WpViewporter};
69use wayland_protocols::xdg::activation::v1::server::xdg_activation_token_v1::{
70 self, XdgActivationTokenV1,
71};
72use wayland_protocols::xdg::activation::v1::server::xdg_activation_v1::{
73 self, XdgActivationV1,
74};
75use wayland_protocols::xdg::decoration::zv1::server::zxdg_decoration_manager_v1::{
76 self, ZxdgDecorationManagerV1,
77};
78use wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1::{
79 self, ZxdgToplevelDecorationV1,
80};
81use wayland_protocols::xdg::shell::server::xdg_popup::{self, XdgPopup};
82use wayland_protocols::xdg::shell::server::xdg_positioner::XdgPositioner;
83use wayland_protocols::xdg::shell::server::xdg_surface::{self, XdgSurface};
84use wayland_protocols::xdg::shell::server::xdg_toplevel::{self, XdgToplevel};
85use wayland_protocols::xdg::shell::server::xdg_wm_base::{self, XdgWmBase};
86use wayland_server::protocol::wl_buffer::WlBuffer;
87use wayland_server::protocol::wl_callback::WlCallback;
88use wayland_server::protocol::wl_compositor::WlCompositor;
89use wayland_server::protocol::wl_data_device::{self, WlDataDevice};
90use wayland_server::protocol::wl_data_device_manager::{self, WlDataDeviceManager};
91use wayland_server::protocol::wl_data_offer::{self, WlDataOffer};
92use wayland_server::protocol::wl_data_source::{self, WlDataSource};
93use wayland_server::protocol::wl_keyboard::{self, WlKeyboard};
94use wayland_server::protocol::wl_output::{self, WlOutput};
95use wayland_server::protocol::wl_pointer::{self, WlPointer};
96use wayland_server::protocol::wl_region::WlRegion;
97use wayland_server::protocol::wl_seat::{self, WlSeat};
98use wayland_server::protocol::wl_shm::{self, WlShm};
99use wayland_server::protocol::wl_shm_pool::WlShmPool;
100use wayland_server::protocol::wl_subcompositor::WlSubcompositor;
101use wayland_server::protocol::wl_subsurface::WlSubsurface;
102use wayland_server::protocol::wl_surface::WlSurface;
103use wayland_server::backend::ObjectId;
104use wayland_server::{
105 Client, DataInit, Dispatch, Display, DisplayHandle, GlobalDispatch, New, Resource,
106};
107
108#[derive(Clone)]
114pub enum PixelData {
115 Bgra(Arc<Vec<u8>>),
116 Rgba(Arc<Vec<u8>>),
117 Nv12 {
118 data: Arc<Vec<u8>>,
119 y_stride: usize,
120 uv_stride: usize,
121 },
122 DmaBuf {
123 fd: Arc<OwnedFd>,
124 fourcc: u32,
125 modifier: u64,
126 stride: u32,
127 offset: u32,
128 y_invert: bool,
132 },
133 Nv12DmaBuf {
136 fd: Arc<OwnedFd>,
137 stride: u32,
138 uv_offset: u32,
139 width: u32,
140 height: u32,
141 sync_fd: Option<Arc<OwnedFd>>,
146 },
147 VaSurface {
149 surface_id: u32,
150 va_display: usize,
151 _fd: Arc<OwnedFd>,
152 },
153 Encoded {
156 data: Arc<Vec<u8>>,
157 is_keyframe: bool,
158 codec_flag: u8,
160 },
161}
162
163#[derive(Clone, Copy, Default)]
168pub struct ExternalOutputPlane {
169 pub offset: u32,
170 pub pitch: u32,
171}
172
173pub struct ExternalOutputBuffer {
174 pub fd: Arc<OwnedFd>,
175 pub fourcc: u32,
176 pub modifier: u64,
177 pub stride: u32,
178 pub offset: u32,
179 pub width: u32,
180 pub height: u32,
181 pub va_surface_id: u32,
182 pub va_display: usize,
183 pub planes: Vec<ExternalOutputPlane>,
185 pub nv12_fd: Option<Arc<OwnedFd>>,
189 pub nv12_stride: u32,
190 pub nv12_uv_offset: u32,
191 pub nv12_modifier: u64,
193 pub nv12_width: u32,
196 pub nv12_height: u32,
197}
198
199pub mod drm_fourcc {
200 pub const ARGB8888: u32 = u32::from_le_bytes(*b"AR24");
201 pub const XRGB8888: u32 = u32::from_le_bytes(*b"XR24");
202 pub const ABGR8888: u32 = u32::from_le_bytes(*b"AB24");
203 pub const XBGR8888: u32 = u32::from_le_bytes(*b"XB24");
204 pub const NV12: u32 = u32::from_le_bytes(*b"NV12");
205}
206
207impl PixelData {
208 pub fn to_rgba(&self, width: u32, height: u32) -> Vec<u8> {
209 let w = width as usize;
210 let h = height as usize;
211 match self {
212 PixelData::Rgba(data) => data.as_ref().clone(),
213 PixelData::Bgra(data) => {
214 let mut rgba = Vec::with_capacity(w * h * 4);
215 for px in data.chunks_exact(4) {
216 rgba.extend_from_slice(&[px[2], px[1], px[0], px[3]]);
217 }
218 rgba
219 }
220 PixelData::Nv12 {
221 data,
222 y_stride,
223 uv_stride,
224 } => {
225 let y_plane_size = *y_stride * h;
226 let uv_h = h.div_ceil(2);
227 let uv_plane_size = *uv_stride * uv_h;
228 if data.len() < y_plane_size + uv_plane_size {
229 return Vec::new();
230 }
231 let y_plane = &data[..y_plane_size];
232 let uv_plane = &data[y_plane_size..];
233 let mut rgba = Vec::with_capacity(w * h * 4);
234 for row in 0..h {
235 for col in 0..w {
236 let y = y_plane[row * y_stride + col];
237 let uv_idx = (row / 2) * uv_stride + (col / 2) * 2;
238 if uv_idx + 1 >= uv_plane.len() {
239 rgba.extend_from_slice(&[0, 0, 0, 255]);
240 continue;
241 }
242 let u = uv_plane[uv_idx];
243 let v = uv_plane[uv_idx + 1];
244 let [r, g, b] = yuv420_to_rgb(y, u, v);
245 rgba.extend_from_slice(&[r, g, b, 255]);
246 }
247 }
248 rgba
249 }
250 PixelData::DmaBuf {
251 fd,
252 fourcc,
253 stride,
254 offset,
255 ..
256 } => {
257 let raw = fd.as_raw_fd();
258 let stride_usize = *stride as usize;
259 let plane_offset = *offset as usize;
260 let map_size = plane_offset + stride_usize * h;
261 if map_size == 0 {
262 return Vec::new();
263 }
264 const DMA_BUF_SYNC_READ: u64 = 1;
271 const DMA_BUF_SYNC_START: u64 = 0;
272 const DMA_BUF_SYNC_END: u64 = 4;
273 const DMA_BUF_IOCTL_SYNC: libc::c_ulong = 0x40086200;
274 let did_sync = {
275 let mut pfd = libc::pollfd {
276 fd: raw,
277 events: libc::POLLIN,
278 revents: 0,
279 };
280 let ready = unsafe { libc::poll(&mut pfd, 1, 0) };
281 if ready > 0 {
282 let s: u64 = DMA_BUF_SYNC_START | DMA_BUF_SYNC_READ;
283 unsafe { libc::ioctl(raw, DMA_BUF_IOCTL_SYNC as _, &s) };
284 true
285 } else {
286 false
287 }
288 };
289 let ptr = unsafe {
290 libc::mmap(
291 std::ptr::null_mut(),
292 map_size,
293 libc::PROT_READ,
294 libc::MAP_SHARED,
295 raw,
296 0,
297 )
298 };
299 if ptr == libc::MAP_FAILED {
300 if did_sync {
301 let s: u64 = DMA_BUF_SYNC_END | DMA_BUF_SYNC_READ;
302 unsafe { libc::ioctl(raw, DMA_BUF_IOCTL_SYNC as _, &s) };
303 }
304 return Vec::new();
305 }
306 let slice = unsafe { std::slice::from_raw_parts(ptr as *const u8, map_size) };
307 let row_bytes = w * 4;
308 let mut pixels = Vec::with_capacity(w * h * 4);
309 for row in 0..h {
310 let start = plane_offset + row * stride_usize;
311 if start + row_bytes <= slice.len() {
312 pixels.extend_from_slice(&slice[start..start + row_bytes]);
313 }
314 }
315 let is_bgr_mem = matches!(*fourcc, drm_fourcc::ARGB8888 | drm_fourcc::XRGB8888);
316 let force_alpha = matches!(*fourcc, drm_fourcc::XRGB8888 | drm_fourcc::XBGR8888);
317 for px in pixels.chunks_exact_mut(4) {
318 if is_bgr_mem {
319 px.swap(0, 2);
320 }
321 if force_alpha {
322 px[3] = 255;
323 }
324 }
325 unsafe { libc::munmap(ptr, map_size) };
326 if did_sync {
327 let s: u64 = DMA_BUF_SYNC_END | DMA_BUF_SYNC_READ;
328 unsafe { libc::ioctl(raw, DMA_BUF_IOCTL_SYNC as _, &s) };
329 }
330 pixels
331 }
332 PixelData::Nv12DmaBuf {
333 fd,
334 stride,
335 uv_offset,
336 width: nv12_w,
337 height: nv12_h,
338 sync_fd,
339 } => {
340 if let Some(sync) = sync_fd {
346 let mut pfd = libc::pollfd {
347 fd: sync.as_raw_fd(),
348 events: libc::POLLIN,
349 revents: 0,
350 };
351 unsafe {
355 libc::poll(&mut pfd, 1, 10);
356 }
357 }
358 let nw = *nv12_w as usize;
359 let nh = *nv12_h as usize;
360 let stride_usize = *stride as usize;
361 let uv_off = *uv_offset as usize;
362 let y_plane_size = stride_usize * nh;
363 let uv_h = nh.div_ceil(2);
364 let uv_plane_size = stride_usize * uv_h;
365 let map_size = uv_off + uv_plane_size;
366 if map_size == 0 || nw == 0 || nh == 0 {
367 return Vec::new();
368 }
369 let raw = fd.as_raw_fd();
370 const DMA_BUF_SYNC_READ: u64 = 1;
371 const DMA_BUF_SYNC_START: u64 = 0;
372 const DMA_BUF_SYNC_END: u64 = 4;
373 const DMA_BUF_IOCTL_SYNC: libc::c_ulong = 0x40086200;
374 let s_start: u64 = DMA_BUF_SYNC_START | DMA_BUF_SYNC_READ;
375 let did_sync = unsafe { libc::ioctl(raw, DMA_BUF_IOCTL_SYNC as _, &s_start) == 0 };
376 let ptr = unsafe {
377 libc::mmap(
378 std::ptr::null_mut(),
379 map_size,
380 libc::PROT_READ,
381 libc::MAP_SHARED,
382 raw,
383 0,
384 )
385 };
386 if ptr == libc::MAP_FAILED {
387 if did_sync {
388 let s_end: u64 = DMA_BUF_SYNC_END | DMA_BUF_SYNC_READ;
389 unsafe { libc::ioctl(raw, DMA_BUF_IOCTL_SYNC as _, &s_end) };
390 }
391 return Vec::new();
392 }
393 let slice = unsafe { std::slice::from_raw_parts(ptr as *const u8, map_size) };
394 let y_plane = &slice[..y_plane_size.min(slice.len())];
395 let uv_plane = &slice[uv_off.min(slice.len())..];
396 let out_w = w.min(nw);
399 let out_h = h.min(nh);
400 let mut rgba = Vec::with_capacity(w * h * 4);
401 for row in 0..out_h {
402 for col in 0..out_w {
403 let y_idx = row * stride_usize + col;
404 let uv_idx = (row / 2) * stride_usize + (col / 2) * 2;
405 if y_idx >= y_plane.len() || uv_idx + 1 >= uv_plane.len() {
406 rgba.extend_from_slice(&[0, 0, 0, 255]);
407 continue;
408 }
409 let y = y_plane[y_idx];
410 let u = uv_plane[uv_idx];
411 let v = uv_plane[uv_idx + 1];
412 let [r, g, b] = yuv420_to_rgb(y, u, v);
413 rgba.extend_from_slice(&[r, g, b, 255]);
414 }
415 for _ in out_w..w {
417 rgba.extend_from_slice(&[0, 0, 0, 255]);
418 }
419 }
420 for _ in out_h..h {
421 for _ in 0..w {
422 rgba.extend_from_slice(&[0, 0, 0, 255]);
423 }
424 }
425 unsafe { libc::munmap(ptr, map_size) };
426 if did_sync {
427 let s_end: u64 = DMA_BUF_SYNC_END | DMA_BUF_SYNC_READ;
428 unsafe { libc::ioctl(raw, DMA_BUF_IOCTL_SYNC as _, &s_end) };
429 }
430 rgba
431 }
432 PixelData::VaSurface { .. } | PixelData::Encoded { .. } => Vec::new(),
433 }
434 }
435
436 pub fn is_empty(&self) -> bool {
437 match self {
438 PixelData::Bgra(v) | PixelData::Rgba(v) => v.is_empty(),
439 PixelData::Encoded { data, .. } => data.is_empty(),
440 PixelData::Nv12 { data, .. } => data.is_empty(),
441 PixelData::DmaBuf { .. }
442 | PixelData::VaSurface { .. }
443 | PixelData::Nv12DmaBuf { .. } => false,
444 }
445 }
446
447 pub fn is_dmabuf(&self) -> bool {
448 matches!(self, PixelData::DmaBuf { .. })
449 }
450
451 pub fn is_va_surface(&self) -> bool {
452 matches!(self, PixelData::VaSurface { .. })
453 }
454}
455
456#[derive(Clone)]
457pub enum CursorImage {
458 Named(String),
459 Custom {
460 hotspot_x: u16,
461 hotspot_y: u16,
462 width: u16,
463 height: u16,
464 rgba: Vec<u8>,
465 },
466 Hidden,
467}
468
469pub enum CompositorEvent {
470 SurfaceCreated {
471 surface_id: u16,
472 title: String,
473 app_id: String,
474 parent_id: u16,
475 width: u16,
476 height: u16,
477 },
478 SurfaceDestroyed {
479 surface_id: u16,
480 },
481 SurfaceCommit {
482 surface_id: u16,
483 width: u32,
484 height: u32,
485 pixels: PixelData,
486 timestamp_ms: u32,
490 },
491 SurfaceTitle {
492 surface_id: u16,
493 title: String,
494 },
495 SurfaceAppId {
496 surface_id: u16,
497 app_id: String,
498 },
499 SurfaceResized {
500 surface_id: u16,
501 width: u16,
502 height: u16,
503 },
504 ClipboardContent {
505 mime_type: String,
506 data: Vec<u8>,
507 },
508 SurfaceCursor {
509 surface_id: u16,
510 cursor: CursorImage,
511 },
512}
513
514pub enum CompositorCommand {
515 KeyInput {
516 surface_id: u16,
517 keycode: u32,
518 pressed: bool,
519 },
520 PointerMotion {
521 surface_id: u16,
522 x: f64,
523 y: f64,
524 },
525 PointerButton {
526 surface_id: u16,
527 button: u32,
528 pressed: bool,
529 },
530 PointerAxis {
531 surface_id: u16,
532 axis: u8,
533 value: f64,
534 },
535 SurfaceResize {
536 surface_id: u16,
537 width: u16,
538 height: u16,
539 scale_120: u16,
540 },
541 SurfaceFocus {
542 surface_id: u16,
543 },
544 SurfaceClose {
545 surface_id: u16,
546 },
547 ClipboardOffer {
548 mime_type: String,
549 data: Vec<u8>,
550 },
551 Capture {
552 surface_id: u16,
553 scale_120: u16,
554 reply: mpsc::SyncSender<Option<(u32, u32, Vec<u8>)>>,
555 },
556 RequestFrame {
557 surface_id: u16,
558 },
559 ReleaseKeys {
560 keycodes: Vec<u32>,
561 },
562 ClipboardListMimes {
564 reply: mpsc::SyncSender<Vec<String>>,
565 },
566 ClipboardGet {
568 mime_type: String,
569 reply: mpsc::SyncSender<Option<Vec<u8>>>,
570 },
571 SetExternalOutputBuffers {
575 surface_id: u32,
576 buffers: Vec<ExternalOutputBuffer>,
577 },
578 TextInput {
580 text: String,
581 },
582 SetRefreshRate {
584 mhz: u32,
585 },
586 SetVulkanEncoder {
588 surface_id: u32,
589 codec: u8,
590 qp: u8,
591 width: u32,
592 height: u32,
593 },
594 RequestVulkanKeyframe {
596 surface_id: u32,
597 },
598 DestroyVulkanEncoder {
600 surface_id: u32,
601 },
602 Shutdown,
603}
604
605pub(crate) struct Surface {
611 pub surface_id: u16,
612 pub wl_surface: WlSurface,
613
614 pending_buffer: Option<WlBuffer>,
616 pending_buffer_scale: i32,
617 pending_damage: bool,
618 pending_frame_callbacks: Vec<WlCallback>,
619 pending_presentation_feedbacks: Vec<WpPresentationFeedback>,
620 pending_opaque: bool,
621
622 pub buffer_scale: i32,
624 pub is_opaque: bool,
625
626 pub parent_surface_id: Option<ObjectId>,
628 pending_subsurface_position: Option<(i32, i32)>,
629 pub subsurface_position: (i32, i32),
630 pub children: Vec<ObjectId>,
631
632 xdg_surface: Option<XdgSurface>,
634 xdg_toplevel: Option<XdgToplevel>,
635 xdg_popup: Option<XdgPopup>,
636 pub xdg_geometry: Option<(i32, i32, i32, i32)>,
637
638 title: String,
639 app_id: String,
640
641 pending_viewport_destination: Option<(i32, i32)>,
643 pub viewport_destination: Option<(i32, i32)>,
648
649 is_cursor: bool,
650 cursor_hotspot: (i32, i32),
651}
652
653struct ShmPool {
654 resource: WlShmPool,
655 fd: OwnedFd,
656 inner: std::sync::Mutex<ShmPoolInner>,
657}
658
659struct ShmPoolInner {
660 size: usize,
661 mmap_ptr: *mut u8,
662}
663
664unsafe impl Send for ShmPoolInner {}
667
668impl ShmPool {
669 fn new(resource: WlShmPool, fd: OwnedFd, size: i32) -> Self {
670 let sz = size.max(0) as usize;
671 let ptr = if sz > 0 {
672 unsafe {
673 libc::mmap(
674 std::ptr::null_mut(),
675 sz,
676 libc::PROT_READ,
677 libc::MAP_SHARED,
678 fd.as_raw_fd(),
679 0,
680 )
681 }
682 } else {
683 libc::MAP_FAILED
684 };
685 ShmPool {
686 resource,
687 fd,
688 inner: std::sync::Mutex::new(ShmPoolInner {
689 size: sz,
690 mmap_ptr: if ptr == libc::MAP_FAILED {
691 std::ptr::null_mut()
692 } else {
693 ptr as *mut u8
694 },
695 }),
696 }
697 }
698
699 fn resize(&self, new_size: i32) {
700 let new_sz = new_size.max(0) as usize;
701 let mut inner = self.inner.lock().unwrap();
702 if new_sz <= inner.size {
703 return;
704 }
705 if !inner.mmap_ptr.is_null() {
706 unsafe {
707 libc::munmap(inner.mmap_ptr as *mut _, inner.size);
708 }
709 }
710 let ptr = unsafe {
711 libc::mmap(
712 std::ptr::null_mut(),
713 new_sz,
714 libc::PROT_READ,
715 libc::MAP_SHARED,
716 self.fd.as_raw_fd(),
717 0,
718 )
719 };
720 inner.mmap_ptr = if ptr == libc::MAP_FAILED {
721 std::ptr::null_mut()
722 } else {
723 ptr as *mut u8
724 };
725 inner.size = new_sz;
726 }
727
728 fn read_buffer(
729 &self,
730 offset: i32,
731 width: i32,
732 height: i32,
733 stride: i32,
734 format: wl_shm::Format,
735 ) -> Option<(u32, u32, PixelData)> {
736 let inner = self.inner.lock().unwrap();
737 if inner.mmap_ptr.is_null() {
738 return None;
739 }
740 let w = width as u32;
741 let h = height as u32;
742 let s = stride as usize;
743 let off = offset as usize;
744 let row_bytes = w as usize * 4;
745 let needed = off + s * (h as usize).saturating_sub(1) + row_bytes;
746 if needed > inner.size {
747 return None;
748 }
749 let mut bgra = if s == row_bytes && off == 0 {
750 let total = row_bytes * h as usize;
751 unsafe { std::slice::from_raw_parts(inner.mmap_ptr, total) }.to_vec()
752 } else {
753 let mut packed = Vec::with_capacity(row_bytes * h as usize);
754 for row in 0..h as usize {
755 let src = unsafe {
756 std::slice::from_raw_parts(inner.mmap_ptr.add(off + row * s), row_bytes)
757 };
758 packed.extend_from_slice(src);
759 }
760 packed
761 };
762 if matches!(format, wl_shm::Format::Xrgb8888 | wl_shm::Format::Xbgr8888) {
763 for px in bgra.chunks_exact_mut(4) {
764 px[3] = 255;
765 }
766 }
767 if matches!(format, wl_shm::Format::Abgr8888 | wl_shm::Format::Xbgr8888) {
768 Some((w, h, PixelData::Rgba(Arc::new(bgra))))
769 } else {
770 Some((w, h, PixelData::Bgra(Arc::new(bgra))))
771 }
772 }
773}
774
775impl Drop for ShmPool {
776 fn drop(&mut self) {
777 let inner = self.inner.get_mut().unwrap();
778 if !inner.mmap_ptr.is_null() {
779 unsafe {
780 libc::munmap(inner.mmap_ptr as *mut _, inner.size);
781 }
782 }
783 }
784}
785
786unsafe impl Send for ShmPool {}
787
788struct ShmBufferData {
789 pool: Arc<ShmPool>,
795 offset: i32,
796 width: i32,
797 height: i32,
798 stride: i32,
799 format: wl_shm::Format,
800}
801
802struct DmaBufBufferData {
803 width: i32,
804 height: i32,
805 fourcc: u32,
806 modifier: u64,
807 planes: Vec<DmaBufPlane>,
808 y_invert: bool,
809}
810
811struct DmaBufPlane {
812 fd: OwnedFd,
813 offset: u32,
814 stride: u32,
815}
816
817struct DmaBufParamsPending {
818 resource: ZwpLinuxBufferParamsV1,
819 planes: Vec<DmaBufPlane>,
820 modifier: u64,
821}
822
823struct ClientState;
824struct XdgSurfaceData {
825 wl_surface_id: ObjectId,
826}
827struct XdgToplevelData {
828 wl_surface_id: ObjectId,
829}
830struct XdgPopupData {
831 wl_surface_id: ObjectId,
832}
833struct SubsurfaceData {
834 wl_surface_id: ObjectId,
835 parent_surface_id: ObjectId,
836}
837
838struct DataSourceData {
841 mime_types: std::sync::Mutex<Vec<String>>,
842}
843
844struct DataOfferData {
845 external: bool,
849}
850
851struct ExternalClipboard {
853 mime_type: String,
854 data: Vec<u8>,
855}
856
857struct PrimarySourceData {
858 mime_types: std::sync::Mutex<Vec<String>>,
859}
860struct PrimaryOfferData {
861 external: bool,
862}
863
864struct ActivationTokenData {
866 serial: u32,
867}
868
869struct PositionerState {
870 resource: XdgPositioner,
871 geometry: PositionerGeometry,
872}
873
874fn char_to_keycode(ch: char) -> Option<(u32, bool)> {
882 const KEY_1: u32 = 2;
883 const KEY_2: u32 = 3;
884 const KEY_3: u32 = 4;
885 const KEY_4: u32 = 5;
886 const KEY_5: u32 = 6;
887 const KEY_6: u32 = 7;
888 const KEY_7: u32 = 8;
889 const KEY_8: u32 = 9;
890 const KEY_9: u32 = 10;
891 const KEY_0: u32 = 11;
892 const KEY_MINUS: u32 = 12;
893 const KEY_EQUAL: u32 = 13;
894 const KEY_TAB: u32 = 15;
895 const KEY_Q: u32 = 16;
896 const KEY_W: u32 = 17;
897 const KEY_E: u32 = 18;
898 const KEY_R: u32 = 19;
899 const KEY_T: u32 = 20;
900 const KEY_Y: u32 = 21;
901 const KEY_U: u32 = 22;
902 const KEY_I: u32 = 23;
903 const KEY_O: u32 = 24;
904 const KEY_P: u32 = 25;
905 const KEY_LEFTBRACE: u32 = 26;
906 const KEY_RIGHTBRACE: u32 = 27;
907 const KEY_ENTER: u32 = 28;
908 const KEY_A: u32 = 30;
909 const KEY_S: u32 = 31;
910 const KEY_D: u32 = 32;
911 const KEY_F: u32 = 33;
912 const KEY_G: u32 = 34;
913 const KEY_H: u32 = 35;
914 const KEY_J: u32 = 36;
915 const KEY_K: u32 = 37;
916 const KEY_L: u32 = 38;
917 const KEY_SEMICOLON: u32 = 39;
918 const KEY_APOSTROPHE: u32 = 40;
919 const KEY_GRAVE: u32 = 41;
920 const KEY_BACKSLASH: u32 = 43;
921 const KEY_Z: u32 = 44;
922 const KEY_X: u32 = 45;
923 const KEY_C: u32 = 46;
924 const KEY_V: u32 = 47;
925 const KEY_B: u32 = 48;
926 const KEY_N: u32 = 49;
927 const KEY_M: u32 = 50;
928 const KEY_COMMA: u32 = 51;
929 const KEY_DOT: u32 = 52;
930 const KEY_SLASH: u32 = 53;
931 const KEY_SPACE: u32 = 57;
932
933 fn letter_kc(ch: char) -> u32 {
934 match ch {
935 'a' => KEY_A,
936 'b' => KEY_B,
937 'c' => KEY_C,
938 'd' => KEY_D,
939 'e' => KEY_E,
940 'f' => KEY_F,
941 'g' => KEY_G,
942 'h' => KEY_H,
943 'i' => KEY_I,
944 'j' => KEY_J,
945 'k' => KEY_K,
946 'l' => KEY_L,
947 'm' => KEY_M,
948 'n' => KEY_N,
949 'o' => KEY_O,
950 'p' => KEY_P,
951 'q' => KEY_Q,
952 'r' => KEY_R,
953 's' => KEY_S,
954 't' => KEY_T,
955 'u' => KEY_U,
956 'v' => KEY_V,
957 'w' => KEY_W,
958 'x' => KEY_X,
959 'y' => KEY_Y,
960 'z' => KEY_Z,
961 _ => KEY_SPACE,
962 }
963 }
964
965 let (kc, shift) = match ch {
966 'a'..='z' => (letter_kc(ch), false),
967 'A'..='Z' => (letter_kc(ch.to_ascii_lowercase()), true),
968 '0' => (KEY_0, false),
969 '1'..='9' => (KEY_1 + (ch as u32 - '1' as u32), false),
970 ' ' => (KEY_SPACE, false),
971 '-' => (KEY_MINUS, false),
972 '=' => (KEY_EQUAL, false),
973 '[' => (KEY_LEFTBRACE, false),
974 ']' => (KEY_RIGHTBRACE, false),
975 ';' => (KEY_SEMICOLON, false),
976 '\'' => (KEY_APOSTROPHE, false),
977 ',' => (KEY_COMMA, false),
978 '.' => (KEY_DOT, false),
979 '/' => (KEY_SLASH, false),
980 '\\' => (KEY_BACKSLASH, false),
981 '`' => (KEY_GRAVE, false),
982 '\t' => (KEY_TAB, false),
983 '\n' => (KEY_ENTER, false),
984 '!' => (KEY_1, true),
985 '@' => (KEY_2, true),
986 '#' => (KEY_3, true),
987 '$' => (KEY_4, true),
988 '%' => (KEY_5, true),
989 '^' => (KEY_6, true),
990 '&' => (KEY_7, true),
991 '*' => (KEY_8, true),
992 '(' => (KEY_9, true),
993 ')' => (KEY_0, true),
994 '_' => (KEY_MINUS, true),
995 '+' => (KEY_EQUAL, true),
996 '{' => (KEY_LEFTBRACE, true),
997 '}' => (KEY_RIGHTBRACE, true),
998 ':' => (KEY_SEMICOLON, true),
999 '"' => (KEY_APOSTROPHE, true),
1000 '<' => (KEY_COMMA, true),
1001 '>' => (KEY_DOT, true),
1002 '?' => (KEY_SLASH, true),
1003 '|' => (KEY_BACKSLASH, true),
1004 '~' => (KEY_GRAVE, true),
1005 _ => return None,
1006 };
1007 Some((kc, shift))
1008}
1009
1010const MOD_SHIFT: u32 = 1 << 0;
1016const MOD_LOCK: u32 = 1 << 1;
1017const MOD_CONTROL: u32 = 1 << 2;
1018const MOD_MOD1: u32 = 1 << 3; const MOD_MOD4: u32 = 1 << 6; fn keycode_to_mod(keycode: u32) -> u32 {
1024 match keycode {
1025 42 | 54 => MOD_SHIFT, 58 => MOD_LOCK, 29 | 97 => MOD_CONTROL, 56 | 100 => MOD_MOD1, 125 | 126 => MOD_MOD4, _ => 0,
1031 }
1032}
1033
1034struct TextInputState {
1036 resource: ZwpTextInputV3,
1037 enabled: bool,
1039}
1040
1041struct Compositor {
1043 display_handle: DisplayHandle,
1044 surfaces: HashMap<ObjectId, Surface>,
1045 toplevel_surface_ids: HashMap<u16, ObjectId>,
1046 next_surface_id: u16,
1047 shm_pools: HashMap<ObjectId, Arc<ShmPool>>,
1048 surface_meta: HashMap<ObjectId, super::render::SurfaceMeta>,
1052 dmabuf_params: HashMap<ObjectId, DmaBufParamsPending>,
1053 vulkan_renderer: Option<super::vulkan_render::VulkanRenderer>,
1054 output_width: i32,
1055 output_height: i32,
1056 output_refresh_mhz: u32,
1059 output_scale_120: u16,
1063 outputs: Vec<WlOutput>,
1064 keyboards: Vec<WlKeyboard>,
1065 pointers: Vec<WlPointer>,
1066 keyboard_keymap_data: Vec<u8>,
1067 mods_depressed: u32,
1069 mods_locked: u32,
1071 serial: u32,
1072 event_tx: mpsc::Sender<CompositorEvent>,
1073 event_notify: Arc<dyn Fn() + Send + Sync>,
1074 loop_signal: LoopSignal,
1075 pending_commits: HashMap<u16, (u32, u32, u32, u32, PixelData)>,
1077 focused_surface_id: u16,
1078 pointer_entered_id: Option<ObjectId>,
1080 pending_kb_reenter: bool,
1084
1085 gpu_device: String,
1086 verbose: bool,
1087 shutdown: Arc<AtomicBool>,
1088 last_reported_size: HashMap<u16, (u32, u32, u32, u32)>,
1092 surface_sizes: HashMap<u16, (i32, i32)>,
1096 positioners: HashMap<ObjectId, PositionerState>,
1098 fractional_scales: Vec<WpFractionalScaleV1>,
1101
1102 data_devices: Vec<WlDataDevice>,
1105 selection_source: Option<WlDataSource>,
1108 external_clipboard: Option<ExternalClipboard>,
1110
1111 primary_devices: Vec<ZwpPrimarySelectionDeviceV1>,
1113 primary_source: Option<ZwpPrimarySelectionSourceV1>,
1114 external_primary: Option<ExternalClipboard>,
1115
1116 relative_pointers: Vec<ZwpRelativePointerV1>,
1118
1119 text_inputs: Vec<TextInputState>,
1124 #[expect(dead_code)]
1127 text_input_serial: u32,
1128
1129 next_activation_token: u32,
1131
1132 popup_grab_stack: Vec<ObjectId>,
1137
1138 held_buffers: HashMap<ObjectId, WlBuffer>,
1145
1146 cursor_rgba: HashMap<ObjectId, (u32, u32, Vec<u8>)>,
1151}
1152
1153impl Compositor {
1154 fn next_serial(&mut self) -> u32 {
1155 self.serial = self.serial.wrapping_add(1);
1156 self.serial
1157 }
1158
1159 fn update_and_send_modifiers(&mut self, keycode: u32, pressed: bool) {
1164 let m = keycode_to_mod(keycode);
1165 if m == 0 {
1166 return;
1167 }
1168 if keycode == 58 {
1169 if pressed {
1171 self.mods_locked ^= MOD_LOCK;
1172 }
1173 } else if pressed {
1174 self.mods_depressed |= m;
1175 } else {
1176 self.mods_depressed &= !m;
1177 }
1178 let serial = self.next_serial();
1179 let focused_wl = self
1180 .toplevel_surface_ids
1181 .get(&self.focused_surface_id)
1182 .and_then(|root_id| self.surfaces.get(root_id))
1183 .map(|s| s.wl_surface.clone());
1184 for kb in &self.keyboards {
1185 if let Some(ref wl) = focused_wl
1186 && same_client(kb, wl)
1187 {
1188 kb.modifiers(serial, self.mods_depressed, 0, self.mods_locked, 0);
1189 }
1190 }
1191 }
1192
1193 fn set_keyboard_focus(&mut self, new_surface_id: u16) {
1198 let old_id = self.focused_surface_id;
1199 if old_id == new_surface_id {
1200 self.focused_surface_id = new_surface_id;
1203 if let Some(root_id) = self.toplevel_surface_ids.get(&new_surface_id)
1204 && let Some(wl_surface) = self.surfaces.get(root_id).map(|s| s.wl_surface.clone())
1205 {
1206 let serial = self.next_serial();
1207 for kb in &self.keyboards {
1208 if same_client(kb, &wl_surface) {
1209 kb.enter(serial, &wl_surface, vec![]);
1210 }
1211 }
1212 for ti in &self.text_inputs {
1213 if same_client(&ti.resource, &wl_surface) {
1214 ti.resource.enter(&wl_surface);
1215 }
1216 }
1217 }
1218 return;
1219 }
1220
1221 if old_id != 0
1223 && let Some(old_root) = self.toplevel_surface_ids.get(&old_id)
1224 && let Some(old_wl) = self.surfaces.get(old_root).map(|s| s.wl_surface.clone())
1225 {
1226 let serial = self.next_serial();
1227 for kb in &self.keyboards {
1228 if same_client(kb, &old_wl) {
1229 kb.leave(serial, &old_wl);
1230 }
1231 }
1232 for ti in &self.text_inputs {
1233 if same_client(&ti.resource, &old_wl) {
1234 ti.resource.leave(&old_wl);
1235 }
1236 }
1237 }
1238
1239 self.focused_surface_id = new_surface_id;
1240
1241 if let Some(root_id) = self.toplevel_surface_ids.get(&new_surface_id)
1243 && let Some(wl_surface) = self.surfaces.get(root_id).map(|s| s.wl_surface.clone())
1244 {
1245 let serial = self.next_serial();
1246 for kb in &self.keyboards {
1247 if same_client(kb, &wl_surface) {
1248 kb.enter(serial, &wl_surface, vec![]);
1249 }
1250 }
1251 for ti in &self.text_inputs {
1252 if same_client(&ti.resource, &wl_surface) {
1253 ti.resource.enter(&wl_surface);
1254 }
1255 }
1256 }
1257 }
1258
1259 fn allocate_surface_id(&mut self) -> u16 {
1260 let mut id = self.next_surface_id;
1261 let start = id;
1262 loop {
1263 if !self.toplevel_surface_ids.contains_key(&id) {
1264 break;
1265 }
1266 id = id.wrapping_add(1);
1267 if id == 0 {
1268 id = 1;
1269 }
1270 if id == start {
1271 break;
1272 }
1273 }
1274 self.next_surface_id = id.wrapping_add(1);
1275 if self.next_surface_id == 0 {
1276 self.next_surface_id = 1;
1277 }
1278 id
1279 }
1280
1281 fn flush_pending_commits(&mut self) {
1282 for (surface_id, (width, height, log_w, log_h, pixels)) in self.pending_commits.drain() {
1283 let prev = self.last_reported_size.get(&surface_id).copied();
1284 if prev.is_none() || prev.map(|(pw, ph, _, _)| (pw, ph)) != Some((width, height)) {
1285 self.last_reported_size
1286 .insert(surface_id, (width, height, log_w, log_h));
1287 let _ = self.event_tx.send(CompositorEvent::SurfaceResized {
1288 surface_id,
1289 width: width as u16,
1290 height: height as u16,
1291 });
1292 }
1293 let _ = self.event_tx.send(CompositorEvent::SurfaceCommit {
1294 surface_id,
1295 width,
1296 height,
1297 pixels,
1298 timestamp_ms: elapsed_ms(),
1299 });
1300 }
1301 (self.event_notify)();
1302 }
1303
1304 fn read_shm_buffer(&self, buffer: &WlBuffer) -> Option<(u32, u32, PixelData)> {
1305 let data = buffer.data::<ShmBufferData>()?;
1306 let r = data.pool.read_buffer(
1307 data.offset,
1308 data.width,
1309 data.height,
1310 data.stride,
1311 data.format,
1312 );
1313 if r.is_none() {
1314 static N: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1315 let n = N.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1316 if n < 10 || n.is_multiple_of(100) {
1317 eprintln!(
1318 "[read_shm_buffer #{n}] pool.read_buffer=None off={} {}x{} stride={} fmt={:?}",
1319 data.offset, data.width, data.height, data.stride, data.format,
1320 );
1321 }
1322 }
1323 r
1324 }
1325
1326 fn read_dmabuf_buffer(&self, buffer: &WlBuffer) -> Option<(u32, u32, PixelData)> {
1327 let data = buffer.data::<DmaBufBufferData>()?;
1328 let width = data.width as u32;
1329 let height = data.height as u32;
1330 if width == 0 || height == 0 || data.planes.is_empty() {
1331 static N: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1332 let n = N.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1333 if n < 10 || n.is_multiple_of(100) {
1334 eprintln!(
1335 "[read_dmabuf_buffer #{n}] empty: {}x{} planes={}",
1336 width,
1337 height,
1338 data.planes.len()
1339 );
1340 }
1341 return None;
1342 }
1343 let plane = &data.planes[0];
1344 if matches!(
1345 data.fourcc,
1346 drm_fourcc::ARGB8888
1347 | drm_fourcc::XRGB8888
1348 | drm_fourcc::ABGR8888
1349 | drm_fourcc::XBGR8888
1350 ) {
1351 use std::os::fd::AsRawFd;
1354 let raw_fd = plane.fd.as_raw_fd();
1355 let _is_drm = {
1356 let mut link_buf = [0u8; 256];
1357 let path = format!("/proc/self/fd/{raw_fd}\0");
1358 let n = unsafe {
1359 libc::readlink(
1360 path.as_ptr() as *const _,
1361 link_buf.as_mut_ptr() as *mut _,
1362 255,
1363 )
1364 };
1365 n > 0 && link_buf[..n as usize].starts_with(b"/dev/dri/")
1366 };
1367
1368 let owned = plane.fd.try_clone().ok()?;
1372 return Some((
1373 width,
1374 height,
1375 PixelData::DmaBuf {
1376 fd: Arc::new(owned),
1377 fourcc: data.fourcc,
1378 modifier: data.modifier,
1379 stride: plane.stride,
1380 offset: plane.offset,
1381 y_invert: data.y_invert,
1382 },
1383 ));
1384 }
1385 static N: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1386 let n = N.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1387 if n < 10 || n.is_multiple_of(100) {
1388 eprintln!(
1389 "[read_dmabuf_buffer #{n}] unsupported fourcc=0x{:08x} ({}x{}) modifier=0x{:x}",
1390 data.fourcc, width, height, data.modifier,
1391 );
1392 }
1393 None
1394 }
1395
1396 fn read_buffer(&self, buffer: &WlBuffer) -> Option<(u32, u32, PixelData)> {
1397 if buffer.data::<ShmBufferData>().is_some() {
1401 return self.read_shm_buffer(buffer);
1402 }
1403 if buffer.data::<DmaBufBufferData>().is_some() {
1404 return self.read_dmabuf_buffer(buffer);
1405 }
1406 static N: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1407 let n = N.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1408 if n < 10 || n.is_multiple_of(100) {
1409 eprintln!(
1410 "[read_buffer #{n}] buffer has unknown role (neither Shm nor DmaBuf data attached)",
1411 );
1412 }
1413 None
1414 }
1415
1416 fn handle_surface_commit(&mut self, surface_id: &ObjectId) {
1417 let (root_id, toplevel_sid) = self.find_toplevel_root(surface_id);
1418
1419 let had_buffer = self
1424 .surfaces
1425 .get(surface_id)
1426 .is_some_and(|s| s.pending_buffer.is_some());
1427 {
1428 static N: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1429 let n = N.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1430 if n < 40 || n.is_multiple_of(200) {
1431 let children = self
1432 .surfaces
1433 .get(surface_id)
1434 .map(|s| s.children.len())
1435 .unwrap_or(0);
1436 eprintln!(
1437 "[commit-in #{n}] sid={surface_id:?} toplevel={toplevel_sid:?} root={root_id:?} had_buffer={had_buffer} children={children}",
1438 );
1439 }
1440 }
1441 self.apply_pending_state(surface_id);
1442
1443 let toplevel_sid = match toplevel_sid {
1444 Some(sid) => sid,
1445 None => {
1446 if let Some(held) = self.held_buffers.remove(surface_id) {
1449 held.release();
1450 }
1451 self.fire_surface_frame_callbacks(surface_id);
1454 let _ = self.display_handle.flush_clients();
1455 return;
1456 }
1457 };
1458
1459 let s120 = self.output_scale_120;
1463 let target_phys = self.surface_sizes.get(&toplevel_sid).map(|&(lw, lh)| {
1464 let pw = super::render::to_physical(lw as u32, s120 as u32);
1465 let ph = super::render::to_physical(lh as u32, s120 as u32);
1466 (pw, ph)
1467 });
1468 let composited = if let Some(ref mut vk) = self.vulkan_renderer {
1469 let r = vk.render_tree_sized(
1470 &root_id,
1471 &self.surfaces,
1472 &self.surface_meta,
1473 s120,
1474 target_phys,
1475 toplevel_sid,
1476 );
1477 if r.is_none() {
1478 static SC: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1479 let n = SC.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1480 if n < 100 || n.is_multiple_of(10) {
1483 eprintln!(
1484 "[commit #{n}] render_tree_sized=None sid={toplevel_sid} target={target_phys:?} meta={} textures={}",
1485 self.surface_meta.len(),
1486 vk.surface_texture_count(),
1487 );
1488 }
1489 }
1490 r
1491 } else {
1492 None
1493 };
1494
1495 if let Some((result_sid, w, h, ref pixels)) = composited
1496 && !pixels.is_empty()
1497 {
1498 let kind = match pixels {
1499 PixelData::Bgra(_) => "bgra",
1500 PixelData::Rgba(_) => "rgba",
1501 PixelData::Nv12 { .. } => "nv12",
1502 PixelData::VaSurface { .. } => "va-surface",
1503 PixelData::Nv12DmaBuf { .. } => "nv12-dmabuf",
1504 PixelData::Encoded { .. } => "vulkan-encoded",
1505 PixelData::DmaBuf { fd, .. } => {
1506 use std::os::fd::AsRawFd;
1507 let raw = fd.as_raw_fd();
1508 let mut lb = [0u8; 128];
1509 let p = format!("/proc/self/fd/{raw}\0");
1510 let n = unsafe {
1511 libc::readlink(p.as_ptr() as *const _, lb.as_mut_ptr() as *mut _, 127)
1512 };
1513 if n > 0 && lb[..n as usize].starts_with(b"/dev/dri/") {
1514 "dmabuf-drm"
1515 } else {
1516 "dmabuf-anon"
1517 }
1518 }
1519 };
1520 if self.verbose {
1521 static LC: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1522 let lc = LC.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1523 if lc < 3 || lc.is_multiple_of(1000) {
1524 eprintln!("[pending #{lc}] {w}x{h} kind={kind}");
1525 }
1526 }
1527 let s120_u32 = (s120 as u32).max(120);
1542 let log_w = (w * 120).div_ceil(s120_u32);
1543 let log_h = (h * 120).div_ceil(s120_u32);
1544 self.pending_commits
1545 .insert(result_sid, (w, h, log_w, log_h, composited.unwrap().3));
1546 }
1547
1548 if let Some(held) = self.held_buffers.remove(surface_id) {
1553 held.release();
1554 }
1555
1556 self.fire_frame_callbacks_for_toplevel(toplevel_sid);
1561
1562 if self.pending_kb_reenter {
1567 self.pending_kb_reenter = false;
1568 let root_ids: Vec<ObjectId> = self.toplevel_surface_ids.values().cloned().collect();
1569 for root_id in root_ids {
1570 let wl = self.surfaces.get(&root_id).map(|s| s.wl_surface.clone());
1571 if let Some(wl) = wl {
1572 let serial = self.next_serial();
1573 for kb in &self.keyboards {
1574 if same_client(kb, &wl) {
1575 kb.leave(serial, &wl);
1576 }
1577 }
1578 let serial = self.next_serial();
1579 for kb in &self.keyboards {
1580 if same_client(kb, &wl) {
1581 kb.enter(serial, &wl, vec![]);
1582 }
1583 }
1584 }
1585 }
1586 let _ = self.display_handle.flush_clients();
1587 }
1588
1589 if self.verbose {
1590 let cache_entries = self.surface_meta.len();
1591 let has_pending = self.pending_commits.contains_key(&toplevel_sid);
1592 static COMMIT_COUNT: std::sync::atomic::AtomicU64 =
1593 std::sync::atomic::AtomicU64::new(0);
1594 let n = COMMIT_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1595 if n < 5 || n.is_multiple_of(1000) {
1596 eprintln!(
1597 "[commit #{n}] sid={surface_id:?} root={root_id:?} cache={cache_entries} pending={has_pending} buf={had_buffer}",
1598 );
1599 }
1600 }
1601 }
1602
1603 fn surface_absolute_position(&self, surface_id: &ObjectId) -> (i32, i32) {
1607 let mut x = 0i32;
1608 let mut y = 0i32;
1609 let mut current = surface_id.clone();
1610 while let Some(surf) = self.surfaces.get(¤t) {
1611 x += surf.subsurface_position.0;
1612 y += surf.subsurface_position.1;
1613 match surf.parent_surface_id {
1614 Some(ref parent) => current = parent.clone(),
1615 None => break,
1616 }
1617 }
1618 (x, y)
1619 }
1620
1621 fn find_toplevel_root(&self, surface_id: &ObjectId) -> (ObjectId, Option<u16>) {
1622 let mut current = surface_id.clone();
1623 loop {
1624 match self.surfaces.get(¤t) {
1625 Some(surf) => {
1626 if let Some(ref parent) = surf.parent_surface_id {
1627 current = parent.clone();
1628 } else {
1629 return (
1630 current,
1631 if surf.surface_id > 0 {
1632 Some(surf.surface_id)
1633 } else {
1634 None
1635 },
1636 );
1637 }
1638 }
1639 None => return (current, None),
1640 }
1641 }
1642 }
1643
1644 fn collect_surface_tree(&self, root_id: &ObjectId) -> Vec<ObjectId> {
1645 let mut result = Vec::new();
1646 self.collect_tree_recursive(root_id, &mut result);
1647 result
1648 }
1649
1650 fn collect_tree_recursive(&self, surface_id: &ObjectId, result: &mut Vec<ObjectId>) {
1651 result.push(surface_id.clone());
1652 if let Some(surf) = self.surfaces.get(surface_id) {
1653 for child_id in &surf.children {
1654 self.collect_tree_recursive(child_id, result);
1655 }
1656 }
1657 }
1658
1659 fn hit_test_surface_at(
1664 &self,
1665 root_id: &ObjectId,
1666 x: f64,
1667 y: f64,
1668 ) -> Option<(WlSurface, f64, f64)> {
1669 self.hit_test_recursive(root_id, x, y, 0, 0).or_else(|| {
1670 self.surfaces
1672 .get(root_id)
1673 .map(|s| (s.wl_surface.clone(), x, y))
1674 })
1675 }
1676
1677 fn hit_test_recursive(
1678 &self,
1679 surface_id: &ObjectId,
1680 x: f64,
1681 y: f64,
1682 offset_x: i32,
1683 offset_y: i32,
1684 ) -> Option<(WlSurface, f64, f64)> {
1685 let surf = self.surfaces.get(surface_id)?;
1686 let sx = offset_x + surf.subsurface_position.0;
1687 let sy = offset_y + surf.subsurface_position.1;
1688
1689 for child_id in surf.children.iter().rev() {
1691 if let Some(hit) = self.hit_test_recursive(child_id, x, y, sx, sy) {
1692 return Some(hit);
1693 }
1694 }
1695
1696 if let Some(sm) = self.surface_meta.get(surface_id) {
1698 let s = sm.scale.max(1) as f64;
1699 let (w, h) = (sm.width, sm.height);
1700 let (lw, lh) = surf
1703 .viewport_destination
1704 .filter(|&(dw, dh)| dw > 0 && dh > 0)
1705 .map(|(dw, dh)| (dw as f64, dh as f64))
1706 .unwrap_or((w as f64 / s, h as f64 / s));
1707 let lx = x - sx as f64;
1708 let ly = y - sy as f64;
1709 if lx >= 0.0 && ly >= 0.0 && lx < lw && ly < lh {
1710 return Some((surf.wl_surface.clone(), lx, ly));
1711 }
1712 }
1713 None
1714 }
1715
1716 fn apply_pending_state(&mut self, surface_id: &ObjectId) {
1729 let (buffer, scale, is_cursor) = {
1730 let Some(surf) = self.surfaces.get_mut(surface_id) else {
1731 return;
1732 };
1733 let buffer = surf.pending_buffer.take();
1734 let scale = surf.pending_buffer_scale;
1735 surf.buffer_scale = scale;
1736 surf.viewport_destination = surf.pending_viewport_destination;
1737 surf.is_opaque = surf.pending_opaque;
1738 surf.pending_damage = false;
1739 if let Some(pos) = surf.pending_subsurface_position.take() {
1740 surf.subsurface_position = pos;
1741 }
1742 (buffer, scale, surf.is_cursor)
1743 };
1744 let Some(buf) = buffer else { return };
1745
1746 if let Some(old) = self.held_buffers.remove(surface_id) {
1749 old.release();
1750 }
1751
1752 if let Some((w, h, pixels)) = self.read_buffer(&buf) {
1753 let y_invert = matches!(pixels, PixelData::DmaBuf { y_invert: true, .. });
1754
1755 if let Some(ref mut vk) = self.vulkan_renderer {
1757 vk.upload_surface(surface_id, &pixels, w, h);
1758 }
1759
1760 self.surface_meta.insert(
1762 surface_id.clone(),
1763 super::render::SurfaceMeta {
1764 width: w,
1765 height: h,
1766 scale,
1767 y_invert,
1768 },
1769 );
1770
1771 if is_cursor {
1774 let rgba = pixels.to_rgba(w, h);
1775 if !rgba.is_empty() {
1776 self.cursor_rgba.insert(surface_id.clone(), (w, h, rgba));
1777 }
1778 }
1779
1780 if pixels.is_dmabuf() {
1781 self.held_buffers.insert(surface_id.clone(), buf);
1784 } else {
1785 buf.release();
1788 }
1789 } else {
1790 buf.release();
1791 }
1792 }
1793
1794 fn fire_surface_frame_callbacks(&mut self, surface_id: &ObjectId) {
1795 let (callbacks, feedbacks) = {
1796 let Some(surf) = self.surfaces.get_mut(surface_id) else {
1797 return;
1798 };
1799 (
1800 std::mem::take(&mut surf.pending_frame_callbacks),
1801 std::mem::take(&mut surf.pending_presentation_feedbacks),
1802 )
1803 };
1804 let time = elapsed_ms();
1805 for cb in callbacks {
1806 cb.done(time);
1807 }
1808 if !feedbacks.is_empty() {
1809 let (sec, nsec) = monotonic_timespec();
1810 for fb in feedbacks {
1813 for output in &self.outputs {
1814 if same_client(&fb, output) {
1815 fb.sync_output(output);
1816 }
1817 }
1818 let refresh_ns = if self.output_refresh_mhz > 0 {
1820 (1_000_000_000_000u64 / self.output_refresh_mhz as u64) as u32
1821 } else {
1822 0
1823 };
1824 fb.presented(
1825 (sec >> 32) as u32,
1826 sec as u32,
1827 nsec as u32,
1828 refresh_ns,
1829 0, 0, WpPresentationFeedbackKind::empty(),
1832 );
1833 }
1834 }
1835 }
1836
1837 fn cleanup_dead_surfaces(&mut self) {
1842 self.fractional_scales.retain(|fs| fs.is_alive());
1844 self.outputs.retain(|o| o.is_alive());
1845 self.keyboards.retain(|k| k.is_alive());
1846 self.pointers.retain(|p| p.is_alive());
1847 self.data_devices.retain(|d| d.is_alive());
1848 self.primary_devices.retain(|d| d.is_alive());
1849 self.relative_pointers.retain(|p| p.is_alive());
1850 self.text_inputs.retain(|ti| ti.resource.is_alive());
1851 self.shm_pools.retain(|_, p| p.resource.is_alive());
1852 self.dmabuf_params.retain(|_, p| p.resource.is_alive());
1853 self.positioners.retain(|_, p| p.resource.is_alive());
1854
1855 let dead: Vec<ObjectId> = self
1856 .surfaces
1857 .iter()
1858 .filter(|(_, surf)| !surf.wl_surface.is_alive())
1859 .map(|(id, _)| id.clone())
1860 .collect();
1861
1862 for proto_id in &dead {
1863 self.surface_meta.remove(proto_id);
1864 if let Some(ref mut vk) = self.vulkan_renderer {
1865 vk.remove_surface(proto_id);
1866 }
1867 if let Some(held) = self.held_buffers.remove(proto_id) {
1868 held.release();
1869 }
1870 if let Some(surf) = self.surfaces.remove(proto_id) {
1871 for fb in surf.pending_presentation_feedbacks {
1874 fb.discarded();
1875 }
1876 if let Some(ref parent_id) = surf.parent_surface_id
1877 && let Some(parent) = self.surfaces.get_mut(parent_id)
1878 {
1879 parent.children.retain(|c| c != proto_id);
1880 }
1881 if surf.surface_id > 0 {
1882 self.toplevel_surface_ids.remove(&surf.surface_id);
1883 self.last_reported_size.remove(&surf.surface_id);
1884 self.surface_sizes.remove(&surf.surface_id);
1885 let _ = self.event_tx.send(CompositorEvent::SurfaceDestroyed {
1886 surface_id: surf.surface_id,
1887 });
1888 (self.event_notify)();
1889 }
1890 }
1891 }
1892 }
1893
1894 fn fire_frame_callbacks_for_toplevel(&mut self, toplevel_sid: u16) {
1895 let Some(root_id) = self.toplevel_surface_ids.get(&toplevel_sid).cloned() else {
1896 return;
1897 };
1898 let tree = self.collect_surface_tree(&root_id);
1899 for sid in &tree {
1900 self.fire_surface_frame_callbacks(sid);
1901 }
1902 let _ = self.display_handle.flush_clients();
1903 }
1904
1905 fn handle_cursor_commit(&mut self, surface_id: &ObjectId) {
1906 self.apply_pending_state(surface_id);
1907 let hotspot = self
1908 .surfaces
1909 .get(surface_id)
1910 .map(|s| s.cursor_hotspot)
1911 .unwrap_or((0, 0));
1912 if let Some((w, h, rgba)) = self.cursor_rgba.get(surface_id)
1913 && !rgba.is_empty()
1914 {
1915 let _ = self.event_tx.send(CompositorEvent::SurfaceCursor {
1916 surface_id: self.focused_surface_id,
1917 cursor: CursorImage::Custom {
1918 hotspot_x: hotspot.0 as u16,
1919 hotspot_y: hotspot.1 as u16,
1920 width: *w as u16,
1921 height: *h as u16,
1922 rgba: rgba.clone(),
1923 },
1924 });
1925 }
1926 self.fire_surface_frame_callbacks(surface_id);
1927 let _ = self.display_handle.flush_clients();
1928 }
1929
1930 fn handle_command(&mut self, cmd: CompositorCommand) {
1931 match cmd {
1932 CompositorCommand::KeyInput {
1933 surface_id: _,
1934 keycode,
1935 pressed,
1936 } => {
1937 let serial = self.next_serial();
1938 let time = elapsed_ms();
1939 let state = if pressed {
1940 wl_keyboard::KeyState::Pressed
1941 } else {
1942 wl_keyboard::KeyState::Released
1943 };
1944 let focused_wl = self
1945 .toplevel_surface_ids
1946 .get(&self.focused_surface_id)
1947 .and_then(|root_id| self.surfaces.get(root_id))
1948 .map(|s| s.wl_surface.clone());
1949 for kb in &self.keyboards {
1950 if let Some(ref wl) = focused_wl
1951 && same_client(kb, wl)
1952 {
1953 kb.key(serial, time, keycode, state);
1954 }
1955 }
1956 self.update_and_send_modifiers(keycode, pressed);
1961 let _ = self.display_handle.flush_clients();
1962 }
1963 CompositorCommand::TextInput { text } => {
1964 let focused_wl = self
1965 .toplevel_surface_ids
1966 .get(&self.focused_surface_id)
1967 .and_then(|root_id| self.surfaces.get(root_id))
1968 .map(|s| s.wl_surface.clone());
1969 let Some(focused_wl) = focused_wl else { return };
1970
1971 const KEY_LEFTSHIFT: u32 = 42;
1987 let saved_mods_depressed = self.mods_depressed;
1988 for ch in text.chars() {
1989 if let Some((kc, need_shift)) = char_to_keycode(ch) {
1990 let time = elapsed_ms();
1991 if need_shift {
1992 let serial = self.next_serial();
1993 for kb in &self.keyboards {
1994 if same_client(kb, &focused_wl) {
1995 kb.key(
1996 serial,
1997 time,
1998 KEY_LEFTSHIFT,
1999 wl_keyboard::KeyState::Pressed,
2000 );
2001 }
2002 }
2003 self.update_and_send_modifiers(KEY_LEFTSHIFT, true);
2004 }
2005 let serial = self.next_serial();
2006 for kb in &self.keyboards {
2007 if same_client(kb, &focused_wl) {
2008 kb.key(serial, time, kc, wl_keyboard::KeyState::Pressed);
2009 }
2010 }
2011 let serial = self.next_serial();
2012 for kb in &self.keyboards {
2013 if same_client(kb, &focused_wl) {
2014 kb.key(serial, time, kc, wl_keyboard::KeyState::Released);
2015 }
2016 }
2017 if need_shift {
2018 let serial = self.next_serial();
2019 for kb in &self.keyboards {
2020 if same_client(kb, &focused_wl) {
2021 kb.key(
2022 serial,
2023 time,
2024 KEY_LEFTSHIFT,
2025 wl_keyboard::KeyState::Released,
2026 );
2027 }
2028 }
2029 self.update_and_send_modifiers(KEY_LEFTSHIFT, false);
2030 }
2031 }
2032 }
2035 if self.mods_depressed != saved_mods_depressed {
2039 self.mods_depressed = saved_mods_depressed;
2040 let serial = self.next_serial();
2041 for kb in &self.keyboards {
2042 if same_client(kb, &focused_wl) {
2043 kb.modifiers(serial, self.mods_depressed, 0, self.mods_locked, 0);
2044 }
2045 }
2046 }
2047 let _ = self.display_handle.flush_clients();
2048 }
2049 CompositorCommand::PointerMotion { surface_id, x, y } => {
2050 let time = elapsed_ms();
2051 let (mut x, mut y) =
2056 if let Some(&(cw, ch, lw, lh)) = self.last_reported_size.get(&surface_id) {
2057 let sx = if cw > 0 { lw as f64 / cw as f64 } else { 1.0 };
2058 let sy = if ch > 0 { lh as f64 / ch as f64 } else { 1.0 };
2059 (x * sx, y * sy)
2060 } else {
2061 (x, y)
2062 };
2063 if let Some((gx, gy, _, _)) = self
2067 .toplevel_surface_ids
2068 .get(&surface_id)
2069 .and_then(|rid| self.surfaces.get(rid))
2070 .and_then(|s| s.xdg_geometry)
2071 {
2072 x += gx as f64;
2073 y += gy as f64;
2074 }
2075 let target_wl = self
2078 .toplevel_surface_ids
2079 .get(&surface_id)
2080 .and_then(|root_id| self.hit_test_surface_at(root_id, x, y))
2081 .map(|(wl_surface, lx, ly)| (wl_surface.id(), wl_surface, lx, ly));
2082
2083 static PTR_DBG: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
2084 let pn = PTR_DBG.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2085 if pn < 5 || pn.is_multiple_of(500) {
2086 let root = self.toplevel_surface_ids.get(&surface_id).cloned();
2087 let lrs = self.last_reported_size.get(&surface_id).copied();
2088 eprintln!(
2089 "[pointer #{pn}] sid={surface_id} logical=({x:.1},{y:.1}) lrs={lrs:?} root={root:?} hit={:?}",
2090 target_wl.as_ref().map(|(pid, _, lx, ly)| format!(
2091 "proto={pid:?} local=({lx:.1},{ly:.1})"
2092 ))
2093 );
2094 }
2095 if let Some((proto_id, wl_surface, lx, ly)) = target_wl {
2096 if self.pointer_entered_id.as_ref() != Some(&proto_id) {
2097 let serial = self.next_serial();
2098 let matching_ptrs = self
2099 .pointers
2100 .iter()
2101 .filter(|p| same_client(*p, &wl_surface))
2102 .count();
2103 eprintln!(
2104 "[pointer-enter] proto={proto_id:?} matching_ptrs={matching_ptrs} total_ptrs={}",
2105 self.pointers.len()
2106 );
2107 if self.pointer_entered_id.is_some() {
2109 let old_wl = self
2110 .surfaces
2111 .values()
2112 .find(|s| Some(s.wl_surface.id()) == self.pointer_entered_id)
2113 .map(|s| s.wl_surface.clone());
2114 if let Some(old_wl) = old_wl {
2115 for ptr in &self.pointers {
2116 if same_client(ptr, &old_wl) {
2117 ptr.leave(serial, &old_wl);
2118 ptr.frame();
2119 }
2120 }
2121 }
2122 }
2123 for ptr in &self.pointers {
2124 if same_client(ptr, &wl_surface) {
2125 ptr.enter(serial, &wl_surface, lx, ly);
2126 }
2127 }
2128 self.pointer_entered_id = Some(proto_id);
2129 }
2130 for ptr in &self.pointers {
2131 if same_client(ptr, &wl_surface) {
2132 ptr.motion(time, lx, ly);
2133 ptr.frame();
2134 }
2135 }
2136 }
2137 let _ = self.display_handle.flush_clients();
2140 }
2141 CompositorCommand::PointerButton {
2142 surface_id: _,
2143 button,
2144 pressed,
2145 } => {
2146 let serial = self.next_serial();
2147 let time = elapsed_ms();
2148 let state = if pressed {
2149 wl_pointer::ButtonState::Pressed
2150 } else {
2151 wl_pointer::ButtonState::Released
2152 };
2153
2154 if pressed && !self.popup_grab_stack.is_empty() {
2157 let click_on_grabbed = self.pointer_entered_id.as_ref().is_some_and(|eid| {
2158 self.popup_grab_stack.iter().any(|gid| {
2159 self.surfaces
2160 .get(gid)
2161 .is_some_and(|s| s.wl_surface.id() == *eid)
2162 })
2163 });
2164 if !click_on_grabbed {
2165 while let Some(grab_wl_id) = self.popup_grab_stack.pop() {
2167 if let Some(surf) = self.surfaces.get(&grab_wl_id)
2168 && let Some(ref popup) = surf.xdg_popup
2169 {
2170 popup.popup_done();
2171 }
2172 }
2173 let _ = self.display_handle.flush_clients();
2174 }
2175 }
2176
2177 let focused_wl = self
2178 .surfaces
2179 .values()
2180 .find(|s| Some(s.wl_surface.id()) == self.pointer_entered_id)
2181 .map(|s| s.wl_surface.clone());
2182 for ptr in &self.pointers {
2183 if let Some(ref wl) = focused_wl
2184 && same_client(ptr, wl)
2185 {
2186 ptr.button(serial, time, button, state);
2187 ptr.frame();
2188 }
2189 }
2190 let _ = self.display_handle.flush_clients();
2191 }
2192 CompositorCommand::PointerAxis {
2193 surface_id: _,
2194 axis,
2195 value,
2196 } => {
2197 let time = elapsed_ms();
2198 let wl_axis = if axis == 0 {
2199 wl_pointer::Axis::VerticalScroll
2200 } else {
2201 wl_pointer::Axis::HorizontalScroll
2202 };
2203 let focused_wl = self
2204 .surfaces
2205 .values()
2206 .find(|s| Some(s.wl_surface.id()) == self.pointer_entered_id)
2207 .map(|s| s.wl_surface.clone());
2208 for ptr in &self.pointers {
2209 if let Some(ref wl) = focused_wl
2210 && same_client(ptr, wl)
2211 {
2212 ptr.axis(time, wl_axis, value);
2213 ptr.frame();
2214 }
2215 }
2216 let _ = self.display_handle.flush_clients();
2217 }
2218 CompositorCommand::SurfaceResize {
2219 surface_id,
2220 width,
2221 height,
2222 scale_120,
2223 } => {
2224 let s_in = (scale_120 as i32).max(120);
2227 let w = (width as i32) * 120 / s_in;
2228 let h = (height as i32) * 120 / s_in;
2229 self.surface_sizes.insert(surface_id, (w, h));
2230
2231 let mut output_changed = false;
2234
2235 if scale_120 > 0 && scale_120 != self.output_scale_120 {
2237 self.output_scale_120 = scale_120;
2238 output_changed = true;
2239 }
2240
2241 let s120 = self.output_scale_120 as i32;
2242
2243 let (max_w, max_h) = self
2248 .surface_sizes
2249 .values()
2250 .fold((0i32, 0i32), |(mw, mh), &(sw, sh)| (mw.max(sw), mh.max(sh)));
2251 let max_w = max_w.max(1);
2253 let max_h = max_h.max(1);
2254 if max_w != self.output_width || max_h != self.output_height {
2255 self.output_width = max_w;
2256 self.output_height = max_h;
2257 output_changed = true;
2258 }
2259
2260 if output_changed {
2264 let int_scale = ((s120) + 119) / 120;
2265 for output in &self.outputs {
2266 output.geometry(
2267 0,
2268 0,
2269 0,
2270 0,
2271 wl_output::Subpixel::None,
2272 "blit".to_string(),
2273 "virtual".to_string(),
2274 wl_output::Transform::Normal,
2275 );
2276 let mode_w = self.output_width * s120 / 120;
2278 let mode_h = self.output_height * s120 / 120;
2279 output.mode(
2280 wl_output::Mode::Current | wl_output::Mode::Preferred,
2281 mode_w,
2282 mode_h,
2283 self.output_refresh_mhz as i32,
2284 );
2285 if output.version() >= 2 {
2286 output.scale(int_scale);
2287 }
2288 }
2289 for fs in &self.fractional_scales {
2290 fs.preferred_scale(s120 as u32);
2291 }
2292 }
2293
2294 if output_changed {
2297 for output in &self.outputs {
2298 if output.version() >= 2 {
2299 output.done();
2300 }
2301 }
2302 }
2303
2304 let states = xdg_toplevel_states(&[
2305 xdg_toplevel::State::Activated,
2306 xdg_toplevel::State::Maximized,
2307 ]);
2308
2309 if output_changed {
2310 for (&sid, root_id) in &self.toplevel_surface_ids {
2314 let (lw, lh) = self.surface_sizes.get(&sid).copied().unwrap_or((w, h));
2315 if let Some(surf) = self.surfaces.get(root_id) {
2316 if let Some(ref tl) = surf.xdg_toplevel {
2317 tl.configure(lw, lh, states.clone());
2318 }
2319 if let Some(ref xs) = surf.xdg_surface {
2320 let serial = self.serial.wrapping_add(1);
2321 self.serial = serial;
2322 xs.configure(serial);
2323 }
2324 }
2325 }
2326 let all_sids: Vec<u16> = self.toplevel_surface_ids.keys().copied().collect();
2329 for sid in all_sids {
2330 self.fire_frame_callbacks_for_toplevel(sid);
2331 }
2332
2333 self.pointer_entered_id = None;
2336 self.pending_kb_reenter = true;
2337 } else {
2338 if let Some(root_id) = self.toplevel_surface_ids.get(&surface_id)
2343 && let Some(surf) = self.surfaces.get(root_id)
2344 {
2345 if let Some(ref tl) = surf.xdg_toplevel {
2346 tl.configure(w, h, states);
2347 }
2348 if let Some(ref xs) = surf.xdg_surface {
2349 let serial = self.serial.wrapping_add(1);
2350 self.serial = serial;
2351 xs.configure(serial);
2352 }
2353 }
2354 self.fire_frame_callbacks_for_toplevel(surface_id);
2355 }
2356 let _ = self.display_handle.flush_clients();
2357 }
2358 CompositorCommand::SurfaceFocus { surface_id } => {
2359 self.set_keyboard_focus(surface_id);
2360 let _ = self.display_handle.flush_clients();
2361 }
2362 CompositorCommand::SurfaceClose { surface_id } => {
2363 if let Some(root_id) = self.toplevel_surface_ids.get(&surface_id)
2364 && let Some(surf) = self.surfaces.get(root_id)
2365 && let Some(ref tl) = surf.xdg_toplevel
2366 {
2367 tl.close();
2368 }
2369 let _ = self.display_handle.flush_clients();
2370 }
2371 CompositorCommand::ClipboardOffer { mime_type, data } => {
2372 self.external_clipboard = Some(ExternalClipboard { mime_type, data });
2373 self.selection_source = None;
2375 self.offer_external_clipboard();
2376 }
2377 CompositorCommand::Capture {
2378 surface_id,
2379 scale_120,
2380 reply,
2381 } => {
2382 let cap_s120 = if scale_120 > 0 {
2385 scale_120
2386 } else {
2387 self.output_scale_120
2388 };
2389 let result = if let Some(root_id) = self.toplevel_surface_ids.get(&surface_id) {
2390 if let Some(ref mut vk) = self.vulkan_renderer {
2391 vk.render_tree_sized(
2392 root_id,
2393 &self.surfaces,
2394 &self.surface_meta,
2395 cap_s120,
2396 None,
2397 surface_id,
2398 )
2399 .map(|(_sid, w, h, pixels)| {
2400 let rgba = pixels.to_rgba(w, h);
2401 (w, h, rgba)
2402 })
2403 } else {
2404 None
2405 }
2406 } else {
2407 None
2408 };
2409 let _ = reply.send(result);
2410 }
2411 CompositorCommand::RequestFrame { surface_id } => {
2412 self.fire_frame_callbacks_for_toplevel(surface_id);
2413 }
2414 CompositorCommand::ReleaseKeys { keycodes } => {
2415 let time = elapsed_ms();
2416 let focused_wl = self
2417 .toplevel_surface_ids
2418 .get(&self.focused_surface_id)
2419 .and_then(|root_id| self.surfaces.get(root_id))
2420 .map(|s| s.wl_surface.clone());
2421 for keycode in &keycodes {
2422 let serial = self.next_serial();
2423 for kb in &self.keyboards {
2424 if let Some(ref wl) = focused_wl
2425 && same_client(kb, wl)
2426 {
2427 kb.key(serial, time, *keycode, wl_keyboard::KeyState::Released);
2428 }
2429 }
2430 }
2431 for keycode in &keycodes {
2433 self.update_and_send_modifiers(*keycode, false);
2434 }
2435 let _ = self.display_handle.flush_clients();
2436 }
2437 CompositorCommand::ClipboardListMimes { reply } => {
2438 let mimes = self.collect_clipboard_mime_types();
2439 let _ = reply.send(mimes);
2440 }
2441 CompositorCommand::ClipboardGet { mime_type, reply } => {
2442 let data = self.get_clipboard_content(&mime_type);
2443 let _ = reply.send(data);
2444 }
2445 CompositorCommand::SetExternalOutputBuffers {
2446 surface_id,
2447 buffers,
2448 } => {
2449 if let Some(ref mut vk) = self.vulkan_renderer {
2450 vk.set_external_output_buffers(surface_id, buffers);
2451 }
2452 }
2453 CompositorCommand::SetRefreshRate { mhz } => {
2454 let diff = (mhz as i64 - self.output_refresh_mhz as i64).unsigned_abs();
2458 if diff > 2000 && mhz > 0 {
2459 self.output_refresh_mhz = mhz;
2460 let s120 = self.output_scale_120 as i32;
2461 let mode_w = self.output_width * s120 / 120;
2462 let mode_h = self.output_height * s120 / 120;
2463 for output in &self.outputs {
2464 output.mode(
2465 wl_output::Mode::Current | wl_output::Mode::Preferred,
2466 mode_w,
2467 mode_h,
2468 mhz as i32,
2469 );
2470 if output.version() >= 2 {
2471 output.done();
2472 }
2473 }
2474 let _ = self.display_handle.flush_clients();
2475 }
2476 }
2477 CompositorCommand::SetVulkanEncoder {
2478 surface_id,
2479 codec,
2480 qp,
2481 width,
2482 height,
2483 } => {
2484 if let Some(ref mut vk) = self.vulkan_renderer {
2485 vk.create_vulkan_encoder(surface_id, codec, qp, width, height);
2486 }
2487 }
2488 CompositorCommand::RequestVulkanKeyframe { surface_id } => {
2489 if let Some(ref mut vk) = self.vulkan_renderer {
2490 vk.request_encoder_keyframe(surface_id);
2491 }
2492 }
2493 CompositorCommand::DestroyVulkanEncoder { surface_id } => {
2494 if let Some(ref mut vk) = self.vulkan_renderer {
2495 vk.destroy_vulkan_encoder(surface_id);
2496 }
2497 }
2498 CompositorCommand::Shutdown => {
2499 self.shutdown.store(true, Ordering::Relaxed);
2500 self.loop_signal.stop();
2501 }
2502 }
2503 }
2504
2505 fn send_dmabuf_feedback(&self, fb: &ZwpLinuxDmabufFeedbackV1) {
2509 use std::os::unix::fs::MetadataExt;
2510
2511 let modifiers: &[(u32, u64)] = self
2513 .vulkan_renderer
2514 .as_ref()
2515 .map(|vk| vk.supported_dmabuf_modifiers.as_slice())
2516 .unwrap_or(&[]);
2517
2518 let entry_size = 16usize;
2520 let table_size = modifiers.len() * entry_size;
2521 let mut table_data = vec![0u8; table_size];
2522 for (i, &(fmt, modifier)) in modifiers.iter().enumerate() {
2523 let off = i * entry_size;
2524 table_data[off..off + 4].copy_from_slice(&fmt.to_ne_bytes());
2525 table_data[off + 8..off + 16].copy_from_slice(&modifier.to_ne_bytes());
2527 }
2528
2529 let name = c"dmabuf-feedback-table";
2531 let raw_fd = unsafe { libc::memfd_create(name.as_ptr(), libc::MFD_CLOEXEC) };
2532 if raw_fd < 0 {
2533 eprintln!("[compositor] memfd_create for dmabuf feedback failed");
2534 fb.done();
2535 return;
2536 }
2537 let table_fd = unsafe { OwnedFd::from_raw_fd(raw_fd) };
2538 if !table_data.is_empty() {
2539 use std::io::Write;
2540 let mut file = std::fs::File::from(table_fd.try_clone().unwrap());
2541 if file.write_all(&table_data).is_err() {
2542 eprintln!("[compositor] failed to write dmabuf feedback table");
2543 fb.done();
2544 return;
2545 }
2546 }
2547 fb.format_table(table_fd.as_fd(), table_size as u32);
2548
2549 let dev = std::fs::metadata(&self.gpu_device)
2551 .map(|m| m.rdev())
2552 .unwrap_or(0);
2553 let dev_bytes = dev.to_ne_bytes().to_vec();
2554 fb.main_device(dev_bytes.clone());
2555
2556 fb.tranche_target_device(dev_bytes);
2558
2559 let indices: Vec<u8> = (0..modifiers.len() as u16)
2561 .flat_map(|i| i.to_ne_bytes())
2562 .collect();
2563 fb.tranche_formats(indices);
2564
2565 fb.tranche_flags(
2566 wayland_protocols::wp::linux_dmabuf::zv1::server::zwp_linux_dmabuf_feedback_v1::TrancheFlags::empty(),
2567 );
2568 fb.tranche_done();
2569 fb.done();
2570 }
2571}
2572
2573impl Compositor {
2574 fn collect_clipboard_mime_types(&self) -> Vec<String> {
2576 if let Some(ref src) = self.selection_source {
2578 let data = src.data::<DataSourceData>().unwrap();
2579 return data.mime_types.lock().unwrap().clone();
2580 }
2581 if let Some(ref cb) = self.external_clipboard
2583 && !cb.mime_type.is_empty()
2584 {
2585 let mut mimes = vec![cb.mime_type.clone()];
2586 if cb.mime_type.starts_with("text/plain") {
2588 if cb.mime_type != "text/plain" {
2589 mimes.push("text/plain".to_string());
2590 }
2591 if cb.mime_type != "text/plain;charset=utf-8" {
2592 mimes.push("text/plain;charset=utf-8".to_string());
2593 }
2594 mimes.push("UTF8_STRING".to_string());
2595 }
2596 return mimes;
2597 }
2598 Vec::new()
2599 }
2600
2601 fn get_clipboard_content(&mut self, mime_type: &str) -> Option<Vec<u8>> {
2603 if let Some(ref cb) = self.external_clipboard
2605 && self.selection_source.is_none()
2606 {
2607 let matches = cb.mime_type == mime_type
2609 || (cb.mime_type.starts_with("text/plain")
2610 && (mime_type == "text/plain"
2611 || mime_type == "text/plain;charset=utf-8"
2612 || mime_type == "UTF8_STRING"));
2613 if matches {
2614 return Some(cb.data.clone());
2615 }
2616 return None;
2617 }
2618 if let Some(src) = self.selection_source.clone() {
2620 return self.read_data_source_sync(&src, mime_type);
2621 }
2622 None
2623 }
2624
2625 fn read_data_source_sync(&mut self, source: &WlDataSource, mime_type: &str) -> Option<Vec<u8>> {
2627 let mut fds = [0i32; 2];
2628 if unsafe { libc::pipe(fds.as_mut_ptr()) } != 0 {
2629 return None;
2630 }
2631 let read_fd = unsafe { OwnedFd::from_raw_fd(fds[0]) };
2632 let write_fd = unsafe { OwnedFd::from_raw_fd(fds[1]) };
2633 source.send(mime_type.to_string(), write_fd.as_fd());
2634 let _ = self.display_handle.flush_clients();
2635 drop(write_fd); unsafe {
2638 libc::fcntl(read_fd.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
2639 }
2640 std::thread::sleep(std::time::Duration::from_millis(5));
2641 let mut buf = Vec::new();
2642 let mut tmp = [0u8; 8192];
2643 loop {
2644 let n = unsafe {
2645 libc::read(
2646 read_fd.as_raw_fd(),
2647 tmp.as_mut_ptr() as *mut libc::c_void,
2648 tmp.len(),
2649 )
2650 };
2651 if n <= 0 {
2652 break;
2653 }
2654 buf.extend_from_slice(&tmp[..n as usize]);
2655 if buf.len() > 1024 * 1024 {
2656 break; }
2658 }
2659 if buf.is_empty() { None } else { Some(buf) }
2660 }
2661}
2662
2663fn monotonic_timespec() -> (i64, i64) {
2669 let mut ts = libc::timespec {
2670 tv_sec: 0,
2671 tv_nsec: 0,
2672 };
2673 unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts) };
2675 (ts.tv_sec, ts.tv_nsec)
2676}
2677
2678fn elapsed_ms() -> u32 {
2679 let (sec, nsec) = monotonic_timespec();
2684 (sec as u32)
2685 .wrapping_mul(1000)
2686 .wrapping_add(nsec as u32 / 1_000_000)
2687}
2688
2689fn same_client<R1: Resource, R2: Resource>(a: &R1, b: &R2) -> bool {
2691 match (a.client(), b.client()) {
2692 (Some(ca), Some(cb)) => ca.id() == cb.id(),
2693 _ => false,
2694 }
2695}
2696
2697fn yuv420_to_rgb(y: u8, u: u8, v: u8) -> [u8; 3] {
2698 let y = (y as i32 - 16).max(0);
2699 let u = u as i32 - 128;
2700 let v = v as i32 - 128;
2701 let r = ((298 * y + 409 * v + 128) >> 8).clamp(0, 255) as u8;
2702 let g = ((298 * y - 100 * u - 208 * v + 128) >> 8).clamp(0, 255) as u8;
2703 let b = ((298 * y + 516 * u + 128) >> 8).clamp(0, 255) as u8;
2704 [r, g, b]
2705}
2706
2707fn xdg_toplevel_states(states: &[xdg_toplevel::State]) -> Vec<u8> {
2709 let mut bytes = Vec::with_capacity(states.len() * 4);
2710 for state in states {
2711 bytes.extend_from_slice(&(*state as u32).to_ne_bytes());
2712 }
2713 bytes
2714}
2715
2716fn create_keymap_fd(keymap_data: &[u8]) -> Option<OwnedFd> {
2717 use std::io::Write;
2718 let name = c"blit-keymap";
2719 let raw_fd = unsafe { libc::memfd_create(name.as_ptr(), libc::MFD_CLOEXEC) };
2720 if raw_fd < 0 {
2721 return None;
2722 }
2723 let fd = unsafe { OwnedFd::from_raw_fd(raw_fd) };
2724 let mut file = std::fs::File::from(fd);
2725 file.write_all(keymap_data).ok()?;
2726 Some(file.into())
2727}
2728
2729impl GlobalDispatch<WlCompositor, ()> for Compositor {
2736 fn bind(
2737 _state: &mut Self,
2738 _handle: &DisplayHandle,
2739 _client: &Client,
2740 resource: New<WlCompositor>,
2741 _data: &(),
2742 data_init: &mut DataInit<'_, Self>,
2743 ) {
2744 data_init.init(resource, ());
2745 }
2746}
2747
2748impl Dispatch<WlCompositor, ()> for Compositor {
2749 fn request(
2750 state: &mut Self,
2751 _client: &Client,
2752 _resource: &WlCompositor,
2753 request: <WlCompositor as Resource>::Request,
2754 _data: &(),
2755 _dh: &DisplayHandle,
2756 data_init: &mut DataInit<'_, Self>,
2757 ) {
2758 use wayland_server::protocol::wl_compositor::Request;
2759 match request {
2760 Request::CreateSurface { id } => {
2761 let surface = data_init.init(id, ());
2762 let proto_id = surface.id();
2763 state.surfaces.insert(
2764 proto_id,
2765 Surface {
2766 surface_id: 0,
2767 wl_surface: surface,
2768 pending_buffer: None,
2769 pending_buffer_scale: 1,
2770 pending_damage: false,
2771 pending_frame_callbacks: Vec::new(),
2772 pending_presentation_feedbacks: Vec::new(),
2773 pending_opaque: false,
2774 buffer_scale: 1,
2775 is_opaque: false,
2776 parent_surface_id: None,
2777 pending_subsurface_position: None,
2778 subsurface_position: (0, 0),
2779 children: Vec::new(),
2780 xdg_surface: None,
2781 xdg_toplevel: None,
2782 xdg_popup: None,
2783 xdg_geometry: None,
2784 title: String::new(),
2785 app_id: String::new(),
2786 pending_viewport_destination: None,
2787 viewport_destination: None,
2788 is_cursor: false,
2789 cursor_hotspot: (0, 0),
2790 },
2791 );
2792 }
2793 Request::CreateRegion { id } => {
2794 data_init.init(id, ());
2795 }
2796 _ => {}
2797 }
2798 }
2799}
2800
2801impl Dispatch<WlSurface, ()> for Compositor {
2804 fn request(
2805 state: &mut Self,
2806 _client: &Client,
2807 resource: &WlSurface,
2808 request: <WlSurface as Resource>::Request,
2809 _data: &(),
2810 _dh: &DisplayHandle,
2811 data_init: &mut DataInit<'_, Self>,
2812 ) {
2813 use wayland_server::protocol::wl_surface::Request;
2814 let sid = resource.id();
2815 match request {
2816 Request::Attach { buffer, x: _, y: _ } => {
2817 if let Some(surf) = state.surfaces.get_mut(&sid) {
2818 surf.pending_buffer = buffer;
2819 }
2820 }
2821 Request::Damage { .. } | Request::DamageBuffer { .. } => {
2822 if let Some(surf) = state.surfaces.get_mut(&sid) {
2823 surf.pending_damage = true;
2824 }
2825 }
2826 Request::Frame { callback } => {
2827 let cb = data_init.init(callback, ());
2828 if let Some(surf) = state.surfaces.get_mut(&sid) {
2829 surf.pending_frame_callbacks.push(cb);
2830 }
2831 }
2832 Request::SetBufferScale { scale } => {
2833 if let Some(surf) = state.surfaces.get_mut(&sid) {
2834 surf.pending_buffer_scale = scale;
2835 }
2836 }
2837 Request::SetOpaqueRegion { region: _ } => {
2838 if let Some(surf) = state.surfaces.get_mut(&sid) {
2839 surf.pending_opaque = true;
2840 }
2841 }
2842 Request::SetInputRegion { .. } => {}
2843 Request::Commit => {
2844 let is_cursor = state.surfaces.get(&sid).is_some_and(|s| s.is_cursor);
2845 if is_cursor {
2846 state.handle_cursor_commit(&sid);
2847 } else {
2848 state.handle_surface_commit(&sid);
2849 }
2850 }
2851 Request::SetBufferTransform { .. } => {}
2852 Request::Offset { .. } => {}
2853 Request::Destroy => {
2854 state.surface_meta.remove(&sid);
2855 state.cursor_rgba.remove(&sid);
2856 if let Some(ref mut vk) = state.vulkan_renderer {
2857 vk.remove_surface(&sid);
2858 }
2859 if let Some(held) = state.held_buffers.remove(&sid) {
2860 held.release();
2861 }
2862 if let Some(parent_id) = state
2863 .surfaces
2864 .get(&sid)
2865 .and_then(|s| s.parent_surface_id.clone())
2866 && let Some(parent) = state.surfaces.get_mut(&parent_id)
2867 {
2868 parent.children.retain(|c| *c != sid);
2869 }
2870 if let Some(surf) = state.surfaces.remove(&sid) {
2871 for fb in surf.pending_presentation_feedbacks {
2872 fb.discarded();
2873 }
2874 if surf.surface_id > 0 {
2875 state.toplevel_surface_ids.remove(&surf.surface_id);
2876 state.last_reported_size.remove(&surf.surface_id);
2877 state.surface_sizes.remove(&surf.surface_id);
2878 let _ = state.event_tx.send(CompositorEvent::SurfaceDestroyed {
2879 surface_id: surf.surface_id,
2880 });
2881 (state.event_notify)();
2882 }
2883 }
2884 }
2885 _ => {}
2886 }
2887 }
2888}
2889
2890impl Dispatch<WlCallback, ()> for Compositor {
2892 fn request(
2893 _: &mut Self,
2894 _: &Client,
2895 _: &WlCallback,
2896 _: <WlCallback as Resource>::Request,
2897 _: &(),
2898 _: &DisplayHandle,
2899 _: &mut DataInit<'_, Self>,
2900 ) {
2901 }
2902}
2903
2904impl GlobalDispatch<WpPresentation, ()> for Compositor {
2906 fn bind(
2907 _: &mut Self,
2908 _: &DisplayHandle,
2909 _: &Client,
2910 resource: New<WpPresentation>,
2911 _: &(),
2912 data_init: &mut DataInit<'_, Self>,
2913 ) {
2914 let pres = data_init.init(resource, ());
2915 pres.clock_id(libc::CLOCK_MONOTONIC as u32);
2917 }
2918}
2919
2920impl Dispatch<WpPresentation, ()> for Compositor {
2921 fn request(
2922 state: &mut Self,
2923 _: &Client,
2924 _: &WpPresentation,
2925 request: <WpPresentation as Resource>::Request,
2926 _: &(),
2927 _: &DisplayHandle,
2928 data_init: &mut DataInit<'_, Self>,
2929 ) {
2930 use wp_presentation::Request;
2931 match request {
2932 Request::Feedback { surface, callback } => {
2933 let fb = data_init.init(callback, ());
2934 let sid = surface.id();
2935 if let Some(surf) = state.surfaces.get_mut(&sid) {
2936 surf.pending_presentation_feedbacks.push(fb);
2937 }
2938 }
2939 Request::Destroy => {}
2940 _ => {}
2941 }
2942 }
2943}
2944
2945impl Dispatch<WpPresentationFeedback, ()> for Compositor {
2947 fn request(
2948 _: &mut Self,
2949 _: &Client,
2950 _: &WpPresentationFeedback,
2951 _: <WpPresentationFeedback as Resource>::Request,
2952 _: &(),
2953 _: &DisplayHandle,
2954 _: &mut DataInit<'_, Self>,
2955 ) {
2956 }
2957}
2958
2959impl Dispatch<WlRegion, ()> for Compositor {
2961 fn request(
2962 _: &mut Self,
2963 _: &Client,
2964 _: &WlRegion,
2965 _: <WlRegion as Resource>::Request,
2966 _: &(),
2967 _: &DisplayHandle,
2968 _: &mut DataInit<'_, Self>,
2969 ) {
2970 }
2971}
2972
2973impl GlobalDispatch<WlSubcompositor, ()> for Compositor {
2975 fn bind(
2976 _: &mut Self,
2977 _: &DisplayHandle,
2978 _: &Client,
2979 resource: New<WlSubcompositor>,
2980 _: &(),
2981 data_init: &mut DataInit<'_, Self>,
2982 ) {
2983 data_init.init(resource, ());
2984 }
2985}
2986
2987impl Dispatch<WlSubcompositor, ()> for Compositor {
2988 fn request(
2989 state: &mut Self,
2990 _: &Client,
2991 _: &WlSubcompositor,
2992 request: <WlSubcompositor as Resource>::Request,
2993 _: &(),
2994 _: &DisplayHandle,
2995 data_init: &mut DataInit<'_, Self>,
2996 ) {
2997 use wayland_server::protocol::wl_subcompositor::Request;
2998 match request {
2999 Request::GetSubsurface {
3000 id,
3001 surface,
3002 parent,
3003 } => {
3004 let child_id = surface.id();
3005 let parent_id = parent.id();
3006 data_init.init(
3007 id,
3008 SubsurfaceData {
3009 wl_surface_id: child_id.clone(),
3010 parent_surface_id: parent_id.clone(),
3011 },
3012 );
3013 if let Some(surf) = state.surfaces.get_mut(&child_id) {
3014 surf.parent_surface_id = Some(parent_id.clone());
3015 }
3016 if let Some(parent_surf) = state.surfaces.get_mut(&parent_id)
3017 && !parent_surf.children.contains(&child_id)
3018 {
3019 parent_surf.children.push(child_id);
3020 }
3021 }
3022 Request::Destroy => {}
3023 _ => {}
3024 }
3025 }
3026}
3027
3028impl Dispatch<WlSubsurface, SubsurfaceData> for Compositor {
3030 fn request(
3031 state: &mut Self,
3032 _: &Client,
3033 _: &WlSubsurface,
3034 request: <WlSubsurface as Resource>::Request,
3035 data: &SubsurfaceData,
3036 _: &DisplayHandle,
3037 _: &mut DataInit<'_, Self>,
3038 ) {
3039 use wayland_server::protocol::wl_subsurface::Request;
3040 match request {
3041 Request::SetPosition { x, y } => {
3042 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3043 surf.pending_subsurface_position = Some((x, y));
3044 }
3045 }
3046 Request::PlaceAbove { sibling } => {
3047 let sibling_id = sibling.id();
3048 if let Some(parent) = state.surfaces.get_mut(&data.parent_surface_id) {
3049 let child_id = &data.wl_surface_id;
3050 parent.children.retain(|c| c != child_id);
3051 let pos = parent
3052 .children
3053 .iter()
3054 .position(|c| *c == sibling_id)
3055 .map(|p| p + 1)
3056 .unwrap_or(parent.children.len());
3057 parent.children.insert(pos, child_id.clone());
3058 }
3059 }
3060 Request::PlaceBelow { sibling } => {
3061 let sibling_id = sibling.id();
3062 if let Some(parent) = state.surfaces.get_mut(&data.parent_surface_id) {
3063 let child_id = &data.wl_surface_id;
3064 parent.children.retain(|c| c != child_id);
3065 let pos = parent
3066 .children
3067 .iter()
3068 .position(|c| *c == sibling_id)
3069 .unwrap_or(0);
3070 parent.children.insert(pos, child_id.clone());
3071 }
3072 }
3073 Request::SetSync | Request::SetDesync => {}
3074 Request::Destroy => {
3075 let child_id = &data.wl_surface_id;
3076 if let Some(parent) = state.surfaces.get_mut(&data.parent_surface_id) {
3077 parent.children.retain(|c| c != child_id);
3078 }
3079 if let Some(surf) = state.surfaces.get_mut(child_id) {
3080 surf.parent_surface_id = None;
3081 }
3082 }
3083 _ => {}
3084 }
3085 }
3086}
3087
3088impl GlobalDispatch<XdgWmBase, ()> for Compositor {
3090 fn bind(
3091 _: &mut Self,
3092 _: &DisplayHandle,
3093 _: &Client,
3094 resource: New<XdgWmBase>,
3095 _: &(),
3096 data_init: &mut DataInit<'_, Self>,
3097 ) {
3098 data_init.init(resource, ());
3099 }
3100}
3101
3102impl Dispatch<XdgWmBase, ()> for Compositor {
3103 fn request(
3104 state: &mut Self,
3105 _: &Client,
3106 _: &XdgWmBase,
3107 request: <XdgWmBase as Resource>::Request,
3108 _: &(),
3109 _: &DisplayHandle,
3110 data_init: &mut DataInit<'_, Self>,
3111 ) {
3112 use xdg_wm_base::Request;
3113 match request {
3114 Request::GetXdgSurface { id, surface } => {
3115 let wl_surface_id = surface.id();
3116 let xdg_surface = data_init.init(
3117 id,
3118 XdgSurfaceData {
3119 wl_surface_id: wl_surface_id.clone(),
3120 },
3121 );
3122 if let Some(surf) = state.surfaces.get_mut(&wl_surface_id) {
3123 surf.xdg_surface = Some(xdg_surface);
3124 }
3125 }
3126 Request::CreatePositioner { id } => {
3127 let positioner = data_init.init(id, ());
3128 let pos_id = positioner.id();
3129 state.positioners.insert(
3130 pos_id,
3131 PositionerState {
3132 resource: positioner,
3133 geometry: PositionerGeometry {
3134 size: (0, 0),
3135 anchor_rect: (0, 0, 0, 0),
3136 anchor: 0,
3137 gravity: 0,
3138 constraint_adjustment: 0,
3139 offset: (0, 0),
3140 },
3141 },
3142 );
3143 }
3144 Request::Pong { .. } => {}
3145 Request::Destroy => {}
3146 _ => {}
3147 }
3148 }
3149}
3150
3151impl Dispatch<XdgSurface, XdgSurfaceData> for Compositor {
3153 fn request(
3154 state: &mut Self,
3155 _: &Client,
3156 resource: &XdgSurface,
3157 request: <XdgSurface as Resource>::Request,
3158 data: &XdgSurfaceData,
3159 _: &DisplayHandle,
3160 data_init: &mut DataInit<'_, Self>,
3161 ) {
3162 use xdg_surface::Request;
3163 match request {
3164 Request::GetToplevel { id } => {
3165 let toplevel = data_init.init(
3166 id,
3167 XdgToplevelData {
3168 wl_surface_id: data.wl_surface_id.clone(),
3169 },
3170 );
3171 let surface_id = state.allocate_surface_id();
3172 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3173 surf.xdg_toplevel = Some(toplevel.clone());
3174 surf.surface_id = surface_id;
3175 }
3176 state
3177 .toplevel_surface_ids
3178 .insert(surface_id, data.wl_surface_id.clone());
3179
3180 let (cw, ch) = state
3185 .surface_sizes
3186 .get(&surface_id)
3187 .copied()
3188 .unwrap_or((state.output_width, state.output_height));
3189 let states = xdg_toplevel_states(&[
3190 xdg_toplevel::State::Activated,
3191 xdg_toplevel::State::Maximized,
3192 ]);
3193 toplevel.configure(cw, ch, states);
3194 let serial = state.next_serial();
3195 resource.configure(serial);
3196
3197 state.set_keyboard_focus(surface_id);
3200 if let Some(surf) = state.surfaces.get(&data.wl_surface_id) {
3203 for output in &state.outputs {
3204 if same_client(output, &surf.wl_surface) {
3205 surf.wl_surface.enter(output);
3206 }
3207 }
3208 }
3209 let _ = state.display_handle.flush_clients();
3210
3211 let _ = state.event_tx.send(CompositorEvent::SurfaceCreated {
3212 surface_id,
3213 title: String::new(),
3214 app_id: String::new(),
3215 parent_id: 0,
3216 width: 0,
3217 height: 0,
3218 });
3219 (state.event_notify)();
3220 if state.verbose {
3221 eprintln!("[compositor] new_toplevel sid={surface_id}");
3222 }
3223 }
3224 Request::GetPopup {
3225 id,
3226 parent,
3227 positioner,
3228 } => {
3229 let popup = data_init.init(
3230 id,
3231 XdgPopupData {
3232 wl_surface_id: data.wl_surface_id.clone(),
3233 },
3234 );
3235
3236 let parent_wl_id: Option<ObjectId> = parent
3239 .as_ref()
3240 .and_then(|p| p.data::<XdgSurfaceData>())
3241 .map(|d| d.wl_surface_id.clone());
3242
3243 let parent_geom_offset = parent_wl_id
3248 .as_ref()
3249 .and_then(|pid| state.surfaces.get(pid))
3250 .and_then(|s| s.xdg_geometry)
3251 .map(|(gx, gy, _, _)| (gx, gy))
3252 .unwrap_or((0, 0));
3253
3254 let parent_abs = parent_wl_id
3259 .as_ref()
3260 .map(|pid| {
3261 let abs = state.surface_absolute_position(pid);
3262 (abs.0 + parent_geom_offset.0, abs.1 + parent_geom_offset.1)
3263 })
3264 .unwrap_or((0, 0));
3265 let (_, toplevel_root) = parent_wl_id
3268 .as_ref()
3269 .map(|pid| state.find_toplevel_root(pid))
3270 .unwrap_or_else(|| {
3271 (data.wl_surface_id.clone(), None)
3273 });
3274 let bounds = toplevel_root
3275 .and_then(|_| {
3276 let root_wl_id = parent_wl_id.as_ref().map(|pid| {
3277 let (rid, _) = state.find_toplevel_root(pid);
3278 rid
3279 })?;
3280 let surf = state.surfaces.get(&root_wl_id)?;
3281 if let Some((gx, gy, gw, gh)) = surf.xdg_geometry
3282 && gw > 0
3283 && gh > 0
3284 {
3285 return Some((gx, gy, gw, gh));
3286 }
3287
3288 let sm = state.surface_meta.get(&root_wl_id)?;
3291 let s = (sm.scale).max(1);
3292 let (lw, lh) = surf
3293 .viewport_destination
3294 .filter(|&(dw, dh)| dw > 0 && dh > 0)
3295 .unwrap_or((sm.width as i32 / s, sm.height as i32 / s));
3296 Some((0, 0, lw, lh))
3297 })
3298 .unwrap_or((0, 0, state.output_width, state.output_height));
3299
3300 eprintln!(
3301 "[popup] parent_abs={parent_abs:?} bounds={bounds:?} parent_wl={parent_wl_id:?} geom_off={parent_geom_offset:?}"
3302 );
3303 let pos_id = positioner.id();
3305 let (px, py, pw, ph) = state
3306 .positioners
3307 .get(&pos_id)
3308 .map(|p| p.geometry.compute_position(parent_abs, bounds))
3309 .unwrap_or((0, 0, 200, 200));
3310 eprintln!("[popup] result=({px},{py},{pw},{ph})");
3311
3312 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3313 surf.xdg_popup = Some(popup.clone());
3314 surf.parent_surface_id = parent_wl_id.clone();
3315 surf.subsurface_position =
3320 (parent_geom_offset.0 + px, parent_geom_offset.1 + py);
3321 }
3322 if let Some(ref parent_id) = parent_wl_id
3323 && let Some(parent_surf) = state.surfaces.get_mut(parent_id)
3324 && !parent_surf.children.contains(&data.wl_surface_id)
3325 {
3326 parent_surf.children.push(data.wl_surface_id.clone());
3327 }
3328
3329 popup.configure(px, py, pw, ph);
3330 let serial = state.next_serial();
3331 resource.configure(serial);
3332 let _ = state.display_handle.flush_clients();
3333 }
3334 Request::SetWindowGeometry {
3335 x,
3336 y,
3337 width,
3338 height,
3339 } => {
3340 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3341 if surf.xdg_popup.is_some() {
3349 let (old_gx, old_gy) = surf
3350 .xdg_geometry
3351 .map(|(gx, gy, _, _)| (gx, gy))
3352 .unwrap_or((0, 0));
3353 surf.subsurface_position.0 += old_gx - x;
3354 surf.subsurface_position.1 += old_gy - y;
3355 }
3356 surf.xdg_geometry = Some((x, y, width, height));
3357 }
3358 }
3359 Request::AckConfigure { .. } => {}
3360 Request::Destroy => {}
3361 _ => {}
3362 }
3363 }
3364}
3365
3366impl Dispatch<XdgToplevel, XdgToplevelData> for Compositor {
3368 fn request(
3369 state: &mut Self,
3370 _: &Client,
3371 _: &XdgToplevel,
3372 request: <XdgToplevel as Resource>::Request,
3373 data: &XdgToplevelData,
3374 _: &DisplayHandle,
3375 _: &mut DataInit<'_, Self>,
3376 ) {
3377 use xdg_toplevel::Request;
3378 match request {
3379 Request::SetTitle { title } => {
3380 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id)
3381 && surf.title != title
3382 {
3383 surf.title = title.clone();
3384 if surf.surface_id > 0 {
3385 let _ = state.event_tx.send(CompositorEvent::SurfaceTitle {
3386 surface_id: surf.surface_id,
3387 title,
3388 });
3389 (state.event_notify)();
3390 }
3391 }
3392 }
3393 Request::SetAppId { app_id } => {
3394 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id)
3395 && surf.app_id != app_id
3396 {
3397 surf.app_id = app_id.clone();
3398 if surf.surface_id > 0 {
3399 let _ = state.event_tx.send(CompositorEvent::SurfaceAppId {
3400 surface_id: surf.surface_id,
3401 app_id,
3402 });
3403 (state.event_notify)();
3404 }
3405 }
3406 }
3407 Request::Destroy => {
3408 let wl_surface_id = &data.wl_surface_id;
3409 state.surface_meta.remove(wl_surface_id);
3410 state.cursor_rgba.remove(wl_surface_id);
3411 if let Some(ref mut vk) = state.vulkan_renderer {
3412 vk.remove_surface(wl_surface_id);
3413 }
3414 if let Some(held) = state.held_buffers.remove(wl_surface_id) {
3415 held.release();
3416 }
3417 if let Some(surf) = state.surfaces.get_mut(wl_surface_id) {
3418 let sid = surf.surface_id;
3419 surf.xdg_toplevel = None;
3420 if sid > 0 {
3421 state.toplevel_surface_ids.remove(&sid);
3422 state.last_reported_size.remove(&sid);
3423 state.surface_sizes.remove(&sid);
3424 let _ = state
3425 .event_tx
3426 .send(CompositorEvent::SurfaceDestroyed { surface_id: sid });
3427 (state.event_notify)();
3428 surf.surface_id = 0;
3429 }
3430 }
3431 }
3432 _ => {}
3433 }
3434 }
3435}
3436
3437impl Dispatch<XdgPopup, XdgPopupData> for Compositor {
3439 fn request(
3440 state: &mut Self,
3441 _: &Client,
3442 _: &XdgPopup,
3443 request: <XdgPopup as Resource>::Request,
3444 data: &XdgPopupData,
3445 _: &DisplayHandle,
3446 _: &mut DataInit<'_, Self>,
3447 ) {
3448 use xdg_popup::Request;
3449 match request {
3450 Request::Grab { seat: _, serial: _ } => {
3451 state
3454 .popup_grab_stack
3455 .retain(|id| *id != data.wl_surface_id);
3456 state.popup_grab_stack.push(data.wl_surface_id.clone());
3457 }
3458 Request::Reposition { positioner, token } => {
3459 let pos_id = positioner.id();
3461 if let Some(surf) = state.surfaces.get(&data.wl_surface_id)
3462 && let Some(parent_id) = surf.parent_surface_id.clone()
3463 {
3464 let parent_geom_offset = state
3465 .surfaces
3466 .get(&parent_id)
3467 .and_then(|s| s.xdg_geometry)
3468 .map(|(gx, gy, _, _)| (gx, gy))
3469 .unwrap_or((0, 0));
3470 let parent_abs = {
3471 let abs = state.surface_absolute_position(&parent_id);
3472 (abs.0 + parent_geom_offset.0, abs.1 + parent_geom_offset.1)
3473 };
3474 let (root_id, toplevel_root) = state.find_toplevel_root(&parent_id);
3475 let bounds = toplevel_root
3476 .and_then(|_| {
3477 let surf = state.surfaces.get(&root_id)?;
3478 if let Some((gx, gy, gw, gh)) = surf.xdg_geometry
3479 && gw > 0
3480 && gh > 0
3481 {
3482 return Some((gx, gy, gw, gh));
3483 }
3484 let sm = state.surface_meta.get(&root_id)?;
3485 let s = (sm.scale).max(1);
3486 let (lw, lh) = surf
3487 .viewport_destination
3488 .filter(|&(dw, dh)| dw > 0 && dh > 0)
3489 .unwrap_or((sm.width as i32 / s, sm.height as i32 / s));
3490 Some((0, 0, lw, lh))
3491 })
3492 .unwrap_or((0, 0, state.output_width, state.output_height));
3493 let (px, py, pw, ph) = state
3494 .positioners
3495 .get(&pos_id)
3496 .map(|p| p.geometry.compute_position(parent_abs, bounds))
3497 .unwrap_or((0, 0, 200, 200));
3498 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3499 let old_gx = surf.xdg_geometry.map(|(gx, _, _, _)| gx).unwrap_or(0);
3502 let old_gy = surf.xdg_geometry.map(|(_, gy, _, _)| gy).unwrap_or(0);
3503 surf.subsurface_position = (
3504 parent_geom_offset.0 + px - old_gx,
3505 parent_geom_offset.1 + py - old_gy,
3506 );
3507 if let Some(ref popup) = surf.xdg_popup {
3508 popup.configure(px, py, pw, ph);
3509 popup.repositioned(token);
3510 }
3511 if let Some(ref xs) = surf.xdg_surface {
3512 let serial = state.serial.wrapping_add(1);
3513 state.serial = serial;
3514 xs.configure(serial);
3515 }
3516 }
3517 }
3518 }
3519 Request::Destroy => {
3520 state
3522 .popup_grab_stack
3523 .retain(|id| *id != data.wl_surface_id);
3524 if let Some(parent_id) = state
3526 .surfaces
3527 .get(&data.wl_surface_id)
3528 .and_then(|s| s.parent_surface_id.clone())
3529 && let Some(parent) = state.surfaces.get_mut(&parent_id)
3530 {
3531 parent.children.retain(|c| *c != data.wl_surface_id);
3532 }
3533 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3534 surf.xdg_popup = None;
3535 surf.parent_surface_id = None;
3536 }
3537 }
3538 _ => {}
3539 }
3540 }
3541}
3542
3543use wayland_protocols::xdg::shell::server::xdg_positioner;
3545impl Dispatch<XdgPositioner, ()> for Compositor {
3546 fn request(
3547 state: &mut Self,
3548 _: &Client,
3549 resource: &XdgPositioner,
3550 request: <XdgPositioner as Resource>::Request,
3551 _: &(),
3552 _: &DisplayHandle,
3553 _: &mut DataInit<'_, Self>,
3554 ) {
3555 use xdg_positioner::Request;
3556 let pos_id = resource.id();
3557 let Some(pos) = state.positioners.get_mut(&pos_id) else {
3558 return;
3559 };
3560 match request {
3561 Request::SetSize { width, height } => {
3562 pos.geometry.size = (width, height);
3563 }
3564 Request::SetAnchorRect {
3565 x,
3566 y,
3567 width,
3568 height,
3569 } => {
3570 pos.geometry.anchor_rect = (x, y, width, height);
3571 }
3572 Request::SetAnchor {
3573 anchor: wayland_server::WEnum::Value(v),
3574 } => {
3575 pos.geometry.anchor = v as u32;
3576 }
3577 Request::SetGravity {
3578 gravity: wayland_server::WEnum::Value(v),
3579 } => {
3580 pos.geometry.gravity = v as u32;
3581 }
3582 Request::SetOffset { x, y } => {
3583 pos.geometry.offset = (x, y);
3584 }
3585 Request::SetConstraintAdjustment {
3586 constraint_adjustment,
3587 } => {
3588 pos.geometry.constraint_adjustment = constraint_adjustment.into();
3589 }
3590 Request::Destroy => {
3591 state.positioners.remove(&pos_id);
3592 }
3593 _ => {}
3594 }
3595 }
3596}
3597
3598impl GlobalDispatch<ZxdgDecorationManagerV1, ()> for Compositor {
3600 fn bind(
3601 _: &mut Self,
3602 _: &DisplayHandle,
3603 _: &Client,
3604 resource: New<ZxdgDecorationManagerV1>,
3605 _: &(),
3606 data_init: &mut DataInit<'_, Self>,
3607 ) {
3608 data_init.init(resource, ());
3609 }
3610}
3611
3612impl Dispatch<ZxdgDecorationManagerV1, ()> for Compositor {
3613 fn request(
3614 _: &mut Self,
3615 _: &Client,
3616 _: &ZxdgDecorationManagerV1,
3617 request: <ZxdgDecorationManagerV1 as Resource>::Request,
3618 _: &(),
3619 _: &DisplayHandle,
3620 data_init: &mut DataInit<'_, Self>,
3621 ) {
3622 use zxdg_decoration_manager_v1::Request;
3623 match request {
3624 Request::GetToplevelDecoration { id, toplevel: _ } => {
3625 let decoration = data_init.init(id, ());
3626 decoration.configure(zxdg_toplevel_decoration_v1::Mode::ServerSide);
3628 }
3629 Request::Destroy => {}
3630 _ => {}
3631 }
3632 }
3633}
3634
3635impl Dispatch<ZxdgToplevelDecorationV1, ()> for Compositor {
3636 fn request(
3637 _: &mut Self,
3638 _: &Client,
3639 resource: &ZxdgToplevelDecorationV1,
3640 request: <ZxdgToplevelDecorationV1 as Resource>::Request,
3641 _: &(),
3642 _: &DisplayHandle,
3643 _: &mut DataInit<'_, Self>,
3644 ) {
3645 use zxdg_toplevel_decoration_v1::Request;
3646 match request {
3647 Request::SetMode { .. } | Request::UnsetMode => {
3648 resource.configure(zxdg_toplevel_decoration_v1::Mode::ServerSide);
3649 }
3650 Request::Destroy => {}
3651 _ => {}
3652 }
3653 }
3654}
3655
3656impl GlobalDispatch<WlShm, ()> for Compositor {
3658 fn bind(
3659 _: &mut Self,
3660 _: &DisplayHandle,
3661 _: &Client,
3662 resource: New<WlShm>,
3663 _: &(),
3664 data_init: &mut DataInit<'_, Self>,
3665 ) {
3666 let shm = data_init.init(resource, ());
3667 shm.format(wl_shm::Format::Argb8888);
3668 shm.format(wl_shm::Format::Xrgb8888);
3669 shm.format(wl_shm::Format::Abgr8888);
3670 shm.format(wl_shm::Format::Xbgr8888);
3671 }
3672}
3673
3674impl Dispatch<WlShm, ()> for Compositor {
3675 fn request(
3676 state: &mut Self,
3677 _: &Client,
3678 _: &WlShm,
3679 request: <WlShm as Resource>::Request,
3680 _: &(),
3681 _: &DisplayHandle,
3682 data_init: &mut DataInit<'_, Self>,
3683 ) {
3684 use wayland_server::protocol::wl_shm::Request;
3685 if let Request::CreatePool { id, fd, size } = request {
3686 let pool = data_init.init(id, ());
3687 let pool_id = pool.id();
3688 state
3689 .shm_pools
3690 .insert(pool_id, Arc::new(ShmPool::new(pool, fd, size)));
3691 }
3692 }
3693}
3694
3695impl Dispatch<WlShmPool, ()> for Compositor {
3697 fn request(
3698 state: &mut Self,
3699 _: &Client,
3700 resource: &WlShmPool,
3701 request: <WlShmPool as Resource>::Request,
3702 _: &(),
3703 _: &DisplayHandle,
3704 data_init: &mut DataInit<'_, Self>,
3705 ) {
3706 use wayland_server::protocol::wl_shm_pool::Request;
3707 let pool_id = resource.id();
3708 match request {
3709 Request::CreateBuffer {
3710 id,
3711 offset,
3712 width,
3713 height,
3714 stride,
3715 format,
3716 } => {
3717 let fmt = match format {
3719 wayland_server::WEnum::Value(f) => f,
3720 _ => wl_shm::Format::Argb8888, };
3722 let Some(pool) = state.shm_pools.get(&pool_id).cloned() else {
3723 return;
3724 };
3725 data_init.init(
3726 id,
3727 ShmBufferData {
3728 pool,
3729 offset,
3730 width,
3731 height,
3732 stride,
3733 format: fmt,
3734 },
3735 );
3736 }
3737 Request::Resize { size } => {
3738 if let Some(pool) = state.shm_pools.get(&pool_id) {
3739 pool.resize(size);
3740 }
3741 }
3742 Request::Destroy => {
3743 state.shm_pools.remove(&pool_id);
3746 }
3747 _ => {}
3748 }
3749 }
3750}
3751
3752impl Dispatch<WlBuffer, ShmBufferData> for Compositor {
3754 fn request(
3755 _: &mut Self,
3756 _: &Client,
3757 _: &WlBuffer,
3758 _: <WlBuffer as Resource>::Request,
3759 _: &ShmBufferData,
3760 _: &DisplayHandle,
3761 _: &mut DataInit<'_, Self>,
3762 ) {
3763 }
3764}
3765
3766impl Dispatch<WlBuffer, DmaBufBufferData> for Compositor {
3768 fn request(
3769 _: &mut Self,
3770 _: &Client,
3771 _: &WlBuffer,
3772 _: <WlBuffer as Resource>::Request,
3773 _: &DmaBufBufferData,
3774 _: &DisplayHandle,
3775 _: &mut DataInit<'_, Self>,
3776 ) {
3777 }
3778}
3779
3780impl GlobalDispatch<WlOutput, ()> for Compositor {
3782 fn bind(
3783 state: &mut Self,
3784 _: &DisplayHandle,
3785 _: &Client,
3786 resource: New<WlOutput>,
3787 _: &(),
3788 data_init: &mut DataInit<'_, Self>,
3789 ) {
3790 let output = data_init.init(resource, ());
3791 output.geometry(
3792 0,
3793 0,
3794 0,
3795 0,
3796 wl_output::Subpixel::Unknown,
3797 "Virtual".to_string(),
3798 "Headless".to_string(),
3799 wl_output::Transform::Normal,
3800 );
3801 let s120 = state.output_scale_120 as i32;
3802 let mode_w = state.output_width * s120 / 120;
3803 let mode_h = state.output_height * s120 / 120;
3804 output.mode(
3805 wl_output::Mode::Current | wl_output::Mode::Preferred,
3806 mode_w,
3807 mode_h,
3808 state.output_refresh_mhz as i32,
3809 );
3810 if output.version() >= 2 {
3811 output.scale(((state.output_scale_120 as i32) + 119) / 120);
3812 }
3813 if output.version() >= 2 {
3814 output.done();
3815 }
3816 state.outputs.push(output);
3817 }
3818}
3819
3820impl Dispatch<WlOutput, ()> for Compositor {
3821 fn request(
3822 state: &mut Self,
3823 _: &Client,
3824 resource: &WlOutput,
3825 request: <WlOutput as Resource>::Request,
3826 _: &(),
3827 _: &DisplayHandle,
3828 _: &mut DataInit<'_, Self>,
3829 ) {
3830 use wayland_server::protocol::wl_output::Request;
3831 if let Request::Release = request {
3832 state.outputs.retain(|o| o.id() != resource.id());
3833 }
3834 }
3835}
3836
3837impl GlobalDispatch<WlSeat, ()> for Compositor {
3839 fn bind(
3840 _: &mut Self,
3841 _: &DisplayHandle,
3842 _: &Client,
3843 resource: New<WlSeat>,
3844 _: &(),
3845 data_init: &mut DataInit<'_, Self>,
3846 ) {
3847 let seat = data_init.init(resource, ());
3848 seat.capabilities(wl_seat::Capability::Keyboard | wl_seat::Capability::Pointer);
3849 if seat.version() >= 2 {
3850 seat.name("headless".to_string());
3851 }
3852 }
3853}
3854
3855impl Dispatch<WlSeat, ()> for Compositor {
3856 fn request(
3857 state: &mut Self,
3858 _: &Client,
3859 _: &WlSeat,
3860 request: <WlSeat as Resource>::Request,
3861 _: &(),
3862 _: &DisplayHandle,
3863 data_init: &mut DataInit<'_, Self>,
3864 ) {
3865 use wayland_server::protocol::wl_seat::Request;
3866 match request {
3867 Request::GetKeyboard { id } => {
3868 let kb = data_init.init(id, ());
3869 if let Some(fd) = create_keymap_fd(&state.keyboard_keymap_data) {
3870 kb.keymap(
3871 wl_keyboard::KeymapFormat::XkbV1,
3872 fd.as_fd(),
3873 state.keyboard_keymap_data.len() as u32,
3874 );
3875 }
3876 if kb.version() >= 4 {
3877 kb.repeat_info(25, 200);
3878 }
3879 state.keyboards.push(kb);
3880 }
3881 Request::GetPointer { id } => {
3882 let ptr = data_init.init(id, ());
3883 state.pointers.push(ptr);
3884 }
3885 Request::GetTouch { id } => {
3886 data_init.init(id, ());
3887 }
3888 Request::Release => {}
3889 _ => {}
3890 }
3891 }
3892}
3893
3894impl Dispatch<WlKeyboard, ()> for Compositor {
3896 fn request(
3897 state: &mut Self,
3898 _: &Client,
3899 resource: &WlKeyboard,
3900 request: <WlKeyboard as Resource>::Request,
3901 _: &(),
3902 _: &DisplayHandle,
3903 _: &mut DataInit<'_, Self>,
3904 ) {
3905 if let wl_keyboard::Request::Release = request {
3906 state.keyboards.retain(|k| k.id() != resource.id());
3907 }
3908 }
3909}
3910
3911impl Dispatch<WlPointer, ()> for Compositor {
3913 fn request(
3914 state: &mut Self,
3915 _: &Client,
3916 resource: &WlPointer,
3917 request: <WlPointer as Resource>::Request,
3918 _: &(),
3919 _: &DisplayHandle,
3920 _: &mut DataInit<'_, Self>,
3921 ) {
3922 use wl_pointer::Request;
3923 match request {
3924 Request::SetCursor {
3925 serial: _,
3926 surface,
3927 hotspot_x,
3928 hotspot_y,
3929 } => {
3930 if let Some(surface) = surface {
3931 let sid = surface.id();
3932 if let Some(surf) = state.surfaces.get_mut(&sid) {
3933 surf.is_cursor = true;
3934 surf.cursor_hotspot = (hotspot_x, hotspot_y);
3935 }
3936 } else {
3937 let _ = state.event_tx.send(CompositorEvent::SurfaceCursor {
3938 surface_id: state.focused_surface_id,
3939 cursor: CursorImage::Hidden,
3940 });
3941 }
3942 }
3943 Request::Release => {
3944 state.pointers.retain(|p| p.id() != resource.id());
3945 }
3946 _ => {}
3947 }
3948 }
3949}
3950
3951impl Dispatch<wayland_server::protocol::wl_touch::WlTouch, ()> for Compositor {
3953 fn request(
3954 _: &mut Self,
3955 _: &Client,
3956 _: &wayland_server::protocol::wl_touch::WlTouch,
3957 _: <wayland_server::protocol::wl_touch::WlTouch as Resource>::Request,
3958 _: &(),
3959 _: &DisplayHandle,
3960 _: &mut DataInit<'_, Self>,
3961 ) {
3962 }
3963}
3964
3965impl GlobalDispatch<ZwpLinuxDmabufV1, ()> for Compositor {
3967 fn bind(
3968 state: &mut Self,
3969 _: &DisplayHandle,
3970 _: &Client,
3971 resource: New<ZwpLinuxDmabufV1>,
3972 _: &(),
3973 data_init: &mut DataInit<'_, Self>,
3974 ) {
3975 let dmabuf = data_init.init(resource, ());
3976 if dmabuf.version() >= 4 {
3979 return;
3980 }
3981 if dmabuf.version() >= 3 {
3982 if let Some(ref vk) = state.vulkan_renderer
3988 && !vk.supported_dmabuf_modifiers.is_empty()
3989 {
3990 for &(drm_fmt, modifier) in &vk.supported_dmabuf_modifiers {
3991 let mod_hi = (modifier >> 32) as u32;
3992 let mod_lo = (modifier & 0xFFFFFFFF) as u32;
3993 dmabuf.modifier(drm_fmt, mod_hi, mod_lo);
3994 }
3995 }
3996 } else if state
4000 .vulkan_renderer
4001 .as_ref()
4002 .is_some_and(|vk| vk.has_dmabuf())
4003 {
4004 dmabuf.format(drm_fourcc::ARGB8888);
4005 dmabuf.format(drm_fourcc::XRGB8888);
4006 dmabuf.format(drm_fourcc::ABGR8888);
4007 dmabuf.format(drm_fourcc::XBGR8888);
4008 }
4009 }
4010}
4011
4012impl Dispatch<ZwpLinuxDmabufV1, ()> for Compositor {
4013 fn request(
4014 state: &mut Self,
4015 _: &Client,
4016 _: &ZwpLinuxDmabufV1,
4017 request: <ZwpLinuxDmabufV1 as Resource>::Request,
4018 _: &(),
4019 _: &DisplayHandle,
4020 data_init: &mut DataInit<'_, Self>,
4021 ) {
4022 use zwp_linux_dmabuf_v1::Request;
4023 match request {
4024 Request::CreateParams { params_id } => {
4025 data_init.init(params_id, ());
4026 }
4027 Request::GetDefaultFeedback { id } => {
4028 let fb = data_init.init(id, ());
4029 state.send_dmabuf_feedback(&fb);
4030 }
4031 Request::GetSurfaceFeedback { id, .. } => {
4032 let fb = data_init.init(id, ());
4033 state.send_dmabuf_feedback(&fb);
4034 }
4035 Request::Destroy => {}
4036 _ => {}
4037 }
4038 }
4039}
4040
4041impl Dispatch<ZwpLinuxDmabufFeedbackV1, ()> for Compositor {
4042 fn request(
4043 _: &mut Self,
4044 _: &Client,
4045 _: &ZwpLinuxDmabufFeedbackV1,
4046 _request: <ZwpLinuxDmabufFeedbackV1 as Resource>::Request,
4047 _: &(),
4048 _: &DisplayHandle,
4049 _data_init: &mut DataInit<'_, Self>,
4050 ) {
4051 }
4053}
4054
4055impl Dispatch<ZwpLinuxBufferParamsV1, ()> for Compositor {
4057 fn request(
4058 state: &mut Self,
4059 client: &Client,
4060 resource: &ZwpLinuxBufferParamsV1,
4061 request: <ZwpLinuxBufferParamsV1 as Resource>::Request,
4062 _: &(),
4063 dh: &DisplayHandle,
4064 data_init: &mut DataInit<'_, Self>,
4065 ) {
4066 use zwp_linux_buffer_params_v1::Request;
4067 let params_id = resource.id();
4068 match request {
4069 Request::Add {
4070 fd,
4071 plane_idx: _,
4072 offset,
4073 stride,
4074 modifier_hi,
4075 modifier_lo,
4076 } => {
4077 let modifier = ((modifier_hi as u64) << 32) | (modifier_lo as u64);
4078 let entry = state
4079 .dmabuf_params
4080 .entry(params_id.clone())
4081 .or_insert_with(|| DmaBufParamsPending {
4082 resource: resource.clone(),
4083 planes: Vec::new(),
4084 modifier,
4085 });
4086 entry.modifier = modifier;
4087 entry.planes.push(DmaBufPlane { fd, offset, stride });
4088 }
4089 Request::Create {
4090 width,
4091 height,
4092 format,
4093 flags,
4094 } => {
4095 let pending = state.dmabuf_params.remove(¶ms_id);
4096 let (planes, modifier) = match pending {
4097 Some(p) => (p.planes, p.modifier),
4098 None => {
4099 resource.failed();
4100 return;
4101 }
4102 };
4103 let y_invert = flags
4104 .into_result()
4105 .ok()
4106 .is_some_and(|f| f.contains(zwp_linux_buffer_params_v1::Flags::YInvert));
4107 match client.create_resource::<WlBuffer, DmaBufBufferData, Compositor>(
4108 dh,
4109 1,
4110 DmaBufBufferData {
4111 width,
4112 height,
4113 fourcc: format,
4114 modifier,
4115 planes,
4116 y_invert,
4117 },
4118 ) {
4119 Ok(buffer) => resource.created(&buffer),
4120 Err(_) => resource.failed(),
4121 }
4122 }
4123 Request::CreateImmed {
4124 buffer_id,
4125 width,
4126 height,
4127 format,
4128 flags,
4129 } => {
4130 let (planes, modifier) = state
4131 .dmabuf_params
4132 .remove(¶ms_id)
4133 .map(|p| (p.planes, p.modifier))
4134 .unwrap_or_default();
4135 let y_invert = flags
4136 .into_result()
4137 .ok()
4138 .is_some_and(|f| f.contains(zwp_linux_buffer_params_v1::Flags::YInvert));
4139 data_init.init(
4140 buffer_id,
4141 DmaBufBufferData {
4142 width,
4143 height,
4144 fourcc: format,
4145 modifier,
4146 planes,
4147 y_invert,
4148 },
4149 );
4150 }
4151 Request::Destroy => {
4152 state.dmabuf_params.remove(¶ms_id);
4153 }
4154 _ => {}
4155 }
4156 }
4157}
4158
4159impl GlobalDispatch<WpFractionalScaleManagerV1, ()> for Compositor {
4161 fn bind(
4162 _: &mut Self,
4163 _: &DisplayHandle,
4164 _: &Client,
4165 resource: New<WpFractionalScaleManagerV1>,
4166 _: &(),
4167 data_init: &mut DataInit<'_, Self>,
4168 ) {
4169 data_init.init(resource, ());
4170 }
4171}
4172
4173impl Dispatch<WpFractionalScaleManagerV1, ()> for Compositor {
4174 fn request(
4175 state: &mut Self,
4176 _: &Client,
4177 _: &WpFractionalScaleManagerV1,
4178 request: <WpFractionalScaleManagerV1 as Resource>::Request,
4179 _: &(),
4180 _: &DisplayHandle,
4181 data_init: &mut DataInit<'_, Self>,
4182 ) {
4183 use wp_fractional_scale_manager_v1::Request;
4184 match request {
4185 Request::GetFractionalScale { id, surface: _ } => {
4186 let fs = data_init.init(id, ());
4187 fs.preferred_scale(state.output_scale_120 as u32);
4189 state.fractional_scales.push(fs);
4190 }
4191 Request::Destroy => {}
4192 _ => {}
4193 }
4194 }
4195}
4196
4197impl Dispatch<WpFractionalScaleV1, ()> for Compositor {
4199 fn request(
4200 state: &mut Self,
4201 _: &Client,
4202 resource: &WpFractionalScaleV1,
4203 _: <WpFractionalScaleV1 as Resource>::Request,
4204 _: &(),
4205 _: &DisplayHandle,
4206 _: &mut DataInit<'_, Self>,
4207 ) {
4208 state
4210 .fractional_scales
4211 .retain(|fs| fs.id() != resource.id());
4212 }
4213}
4214
4215impl GlobalDispatch<WpViewporter, ()> for Compositor {
4217 fn bind(
4218 _: &mut Self,
4219 _: &DisplayHandle,
4220 _: &Client,
4221 resource: New<WpViewporter>,
4222 _: &(),
4223 data_init: &mut DataInit<'_, Self>,
4224 ) {
4225 data_init.init(resource, ());
4226 }
4227}
4228
4229impl Dispatch<WpViewporter, ()> for Compositor {
4230 fn request(
4231 _: &mut Self,
4232 _: &Client,
4233 _: &WpViewporter,
4234 request: <WpViewporter as Resource>::Request,
4235 _: &(),
4236 _: &DisplayHandle,
4237 data_init: &mut DataInit<'_, Self>,
4238 ) {
4239 use wp_viewporter::Request;
4240 match request {
4241 Request::GetViewport { id, surface } => {
4242 let obj_id = surface.id();
4245 data_init.init(id, obj_id);
4246 }
4247 Request::Destroy => {}
4248 _ => {}
4249 }
4250 }
4251}
4252
4253impl Dispatch<WpViewport, ObjectId> for Compositor {
4255 fn request(
4256 state: &mut Self,
4257 _: &Client,
4258 _: &WpViewport,
4259 request: <WpViewport as Resource>::Request,
4260 surface_obj_id: &ObjectId,
4261 _: &DisplayHandle,
4262 _: &mut DataInit<'_, Self>,
4263 ) {
4264 use wayland_protocols::wp::viewporter::server::wp_viewport::Request;
4265 match request {
4266 Request::SetDestination { width, height } => {
4267 if let Some(surf) = state.surfaces.get_mut(surface_obj_id) {
4268 if width > 0 && height > 0 {
4270 surf.pending_viewport_destination = Some((width, height));
4271 } else {
4272 surf.pending_viewport_destination = None;
4273 }
4274 }
4275 }
4276 Request::SetSource { .. } => {
4277 }
4279 Request::Destroy => {}
4280 _ => {}
4281 }
4282 }
4283}
4284
4285impl GlobalDispatch<WlDataDeviceManager, ()> for Compositor {
4292 fn bind(
4293 _: &mut Self,
4294 _: &DisplayHandle,
4295 _: &Client,
4296 resource: New<WlDataDeviceManager>,
4297 _: &(),
4298 data_init: &mut DataInit<'_, Self>,
4299 ) {
4300 data_init.init(resource, ());
4301 }
4302}
4303
4304impl Dispatch<WlDataDeviceManager, ()> for Compositor {
4305 fn request(
4306 state: &mut Self,
4307 _: &Client,
4308 _: &WlDataDeviceManager,
4309 request: <WlDataDeviceManager as Resource>::Request,
4310 _: &(),
4311 _: &DisplayHandle,
4312 data_init: &mut DataInit<'_, Self>,
4313 ) {
4314 use wl_data_device_manager::Request;
4315 match request {
4316 Request::CreateDataSource { id } => {
4317 data_init.init(
4318 id,
4319 DataSourceData {
4320 mime_types: std::sync::Mutex::new(Vec::new()),
4321 },
4322 );
4323 }
4324 Request::GetDataDevice { id, seat: _ } => {
4325 let dd = data_init.init(id, ());
4326 state.data_devices.push(dd);
4327 }
4328 _ => {}
4329 }
4330 }
4331}
4332
4333impl Dispatch<WlDataSource, DataSourceData> for Compositor {
4334 fn request(
4335 _: &mut Self,
4336 _: &Client,
4337 _: &WlDataSource,
4338 request: <WlDataSource as Resource>::Request,
4339 data: &DataSourceData,
4340 _: &DisplayHandle,
4341 _: &mut DataInit<'_, Self>,
4342 ) {
4343 use wl_data_source::Request;
4344 match request {
4345 Request::Offer { mime_type } => {
4346 data.mime_types.lock().unwrap().push(mime_type);
4347 }
4348 Request::Destroy => {}
4349 _ => {} }
4351 }
4352
4353 fn destroyed(
4354 state: &mut Self,
4355 _: wayland_server::backend::ClientId,
4356 resource: &WlDataSource,
4357 _: &DataSourceData,
4358 ) {
4359 if state
4360 .selection_source
4361 .as_ref()
4362 .is_some_and(|s| s.id() == resource.id())
4363 {
4364 state.selection_source = None;
4365 }
4366 }
4367}
4368
4369impl Dispatch<WlDataDevice, ()> for Compositor {
4370 fn request(
4371 state: &mut Self,
4372 _: &Client,
4373 _: &WlDataDevice,
4374 request: <WlDataDevice as Resource>::Request,
4375 _: &(),
4376 _: &DisplayHandle,
4377 _: &mut DataInit<'_, Self>,
4378 ) {
4379 use wl_data_device::Request;
4380 match request {
4381 Request::SetSelection { source, serial: _ } => {
4382 state.selection_source = source.clone();
4383 if let Some(ref src) = source {
4385 let data = src.data::<DataSourceData>().unwrap();
4386 let mimes = data.mime_types.lock().unwrap();
4387 let text_mime = mimes
4388 .iter()
4389 .find(|m| {
4390 m.as_str() == "text/plain;charset=utf-8"
4391 || m.as_str() == "text/plain"
4392 || m.as_str() == "UTF8_STRING"
4393 })
4394 .cloned();
4395 drop(mimes);
4396 if let Some(mime) = text_mime {
4397 state.read_data_source_and_emit(src, &mime);
4398 }
4399 }
4400 }
4401 Request::Release => {}
4402 _ => {} }
4404 }
4405
4406 fn destroyed(
4407 state: &mut Self,
4408 _: wayland_server::backend::ClientId,
4409 resource: &WlDataDevice,
4410 _: &(),
4411 ) {
4412 state.data_devices.retain(|d| d.id() != resource.id());
4413 }
4414}
4415
4416impl Dispatch<WlDataOffer, DataOfferData> for Compositor {
4417 fn request(
4418 state: &mut Self,
4419 _: &Client,
4420 _: &WlDataOffer,
4421 request: <WlDataOffer as Resource>::Request,
4422 data: &DataOfferData,
4423 _: &DisplayHandle,
4424 _: &mut DataInit<'_, Self>,
4425 ) {
4426 use wl_data_offer::Request;
4427 match request {
4428 Request::Receive { mime_type, fd } => {
4429 if data.external {
4430 if let Some(ref cb) = state.external_clipboard
4432 && (cb.mime_type == mime_type
4433 || mime_type == "text/plain"
4434 || mime_type == "text/plain;charset=utf-8"
4435 || mime_type == "UTF8_STRING")
4436 {
4437 use std::io::Write;
4438 let mut f = std::fs::File::from(fd);
4439 let _ = f.write_all(&cb.data);
4440 }
4441 } else if let Some(ref src) = state.selection_source {
4442 src.send(mime_type, fd.as_fd());
4444 }
4445 }
4446 Request::Destroy => {}
4447 _ => {} }
4449 }
4450}
4451
4452impl Compositor {
4453 fn read_data_source_and_emit(&mut self, source: &WlDataSource, mime_type: &str) {
4456 let mut fds = [0i32; 2];
4457 if unsafe { libc::pipe(fds.as_mut_ptr()) } != 0 {
4458 return;
4459 }
4460 let read_fd = unsafe { OwnedFd::from_raw_fd(fds[0]) };
4461 let write_fd = unsafe { OwnedFd::from_raw_fd(fds[1]) };
4462 source.send(mime_type.to_string(), write_fd.as_fd());
4463 let _ = self.display_handle.flush_clients();
4464 unsafe {
4466 libc::fcntl(read_fd.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
4467 }
4468 std::thread::sleep(std::time::Duration::from_millis(5));
4470 let mut buf = Vec::new();
4471 let mut tmp = [0u8; 8192];
4472 loop {
4473 let n = unsafe {
4474 libc::read(
4475 read_fd.as_raw_fd(),
4476 tmp.as_mut_ptr() as *mut libc::c_void,
4477 tmp.len(),
4478 )
4479 };
4480 if n <= 0 {
4481 break;
4482 }
4483 buf.extend_from_slice(&tmp[..n as usize]);
4484 if buf.len() > 1024 * 1024 {
4485 break; }
4487 }
4488 if !buf.is_empty() {
4489 let _ = self.event_tx.send(CompositorEvent::ClipboardContent {
4490 mime_type: mime_type.to_string(),
4491 data: buf,
4492 });
4493 (self.event_notify)();
4494 }
4495 }
4496
4497 fn offer_external_clipboard(&mut self) {
4499 let Some(ref cb) = self.external_clipboard else {
4500 return;
4501 };
4502 let mime = cb.mime_type.clone();
4503 for dd in &self.data_devices {
4504 if let Some(client) = dd.client() {
4505 let offer = client
4506 .create_resource::<WlDataOffer, DataOfferData, Compositor>(
4507 &self.display_handle,
4508 dd.version(),
4509 DataOfferData { external: true },
4510 )
4511 .unwrap();
4512 dd.data_offer(&offer);
4513 offer.offer(mime.clone());
4514 if mime.starts_with("text/plain") {
4516 if mime != "text/plain" {
4517 offer.offer("text/plain".to_string());
4518 }
4519 if mime != "text/plain;charset=utf-8" {
4520 offer.offer("text/plain;charset=utf-8".to_string());
4521 }
4522 offer.offer("UTF8_STRING".to_string());
4523 }
4524 dd.selection(Some(&offer));
4525 }
4526 }
4527 let _ = self.display_handle.flush_clients();
4528 }
4529}
4530
4531impl GlobalDispatch<ZwpPrimarySelectionDeviceManagerV1, ()> for Compositor {
4534 fn bind(
4535 _: &mut Self,
4536 _: &DisplayHandle,
4537 _: &Client,
4538 resource: New<ZwpPrimarySelectionDeviceManagerV1>,
4539 _: &(),
4540 data_init: &mut DataInit<'_, Self>,
4541 ) {
4542 data_init.init(resource, ());
4543 }
4544}
4545
4546impl Dispatch<ZwpPrimarySelectionDeviceManagerV1, ()> for Compositor {
4547 fn request(
4548 state: &mut Self,
4549 _: &Client,
4550 _: &ZwpPrimarySelectionDeviceManagerV1,
4551 request: <ZwpPrimarySelectionDeviceManagerV1 as Resource>::Request,
4552 _: &(),
4553 _: &DisplayHandle,
4554 data_init: &mut DataInit<'_, Self>,
4555 ) {
4556 use zwp_primary_selection_device_manager_v1::Request;
4557 match request {
4558 Request::CreateSource { id } => {
4559 data_init.init(
4560 id,
4561 PrimarySourceData {
4562 mime_types: std::sync::Mutex::new(Vec::new()),
4563 },
4564 );
4565 }
4566 Request::GetDevice { id, seat: _ } => {
4567 let pd = data_init.init(id, ());
4568 state.primary_devices.push(pd);
4569 }
4570 Request::Destroy => {}
4571 _ => {}
4572 }
4573 }
4574}
4575
4576impl Dispatch<ZwpPrimarySelectionSourceV1, PrimarySourceData> for Compositor {
4577 fn request(
4578 _: &mut Self,
4579 _: &Client,
4580 _: &ZwpPrimarySelectionSourceV1,
4581 request: <ZwpPrimarySelectionSourceV1 as Resource>::Request,
4582 data: &PrimarySourceData,
4583 _: &DisplayHandle,
4584 _: &mut DataInit<'_, Self>,
4585 ) {
4586 use zwp_primary_selection_source_v1::Request;
4587 match request {
4588 Request::Offer { mime_type } => {
4589 data.mime_types.lock().unwrap().push(mime_type);
4590 }
4591 Request::Destroy => {}
4592 _ => {}
4593 }
4594 }
4595
4596 fn destroyed(
4597 state: &mut Self,
4598 _: wayland_server::backend::ClientId,
4599 resource: &ZwpPrimarySelectionSourceV1,
4600 _: &PrimarySourceData,
4601 ) {
4602 if state
4603 .primary_source
4604 .as_ref()
4605 .is_some_and(|s| s.id() == resource.id())
4606 {
4607 state.primary_source = None;
4608 }
4609 }
4610}
4611
4612impl Dispatch<ZwpPrimarySelectionDeviceV1, ()> for Compositor {
4613 fn request(
4614 state: &mut Self,
4615 _: &Client,
4616 _: &ZwpPrimarySelectionDeviceV1,
4617 request: <ZwpPrimarySelectionDeviceV1 as Resource>::Request,
4618 _: &(),
4619 _: &DisplayHandle,
4620 _: &mut DataInit<'_, Self>,
4621 ) {
4622 use zwp_primary_selection_device_v1::Request;
4623 match request {
4624 Request::SetSelection { source, serial: _ } => {
4625 state.primary_source = source;
4626 }
4627 Request::Destroy => {}
4628 _ => {}
4629 }
4630 }
4631
4632 fn destroyed(
4633 state: &mut Self,
4634 _: wayland_server::backend::ClientId,
4635 resource: &ZwpPrimarySelectionDeviceV1,
4636 _: &(),
4637 ) {
4638 state.primary_devices.retain(|d| d.id() != resource.id());
4639 }
4640}
4641
4642impl Dispatch<ZwpPrimarySelectionOfferV1, PrimaryOfferData> for Compositor {
4643 fn request(
4644 state: &mut Self,
4645 _: &Client,
4646 _: &ZwpPrimarySelectionOfferV1,
4647 request: <ZwpPrimarySelectionOfferV1 as Resource>::Request,
4648 data: &PrimaryOfferData,
4649 _: &DisplayHandle,
4650 _: &mut DataInit<'_, Self>,
4651 ) {
4652 use zwp_primary_selection_offer_v1::Request;
4653 match request {
4654 Request::Receive { mime_type, fd } => {
4655 if data.external {
4656 if let Some(ref cb) = state.external_primary {
4657 use std::io::Write;
4658 let mut f = std::fs::File::from(fd);
4659 let _ = f.write_all(&cb.data);
4660 let _ = mime_type; }
4662 } else if let Some(ref src) = state.primary_source {
4663 src.send(mime_type, fd.as_fd());
4664 }
4665 }
4666 Request::Destroy => {}
4667 _ => {}
4668 }
4669 }
4670}
4671
4672impl GlobalDispatch<ZwpPointerConstraintsV1, ()> for Compositor {
4675 fn bind(
4676 _: &mut Self,
4677 _: &DisplayHandle,
4678 _: &Client,
4679 resource: New<ZwpPointerConstraintsV1>,
4680 _: &(),
4681 data_init: &mut DataInit<'_, Self>,
4682 ) {
4683 data_init.init(resource, ());
4684 }
4685}
4686
4687impl Dispatch<ZwpPointerConstraintsV1, ()> for Compositor {
4688 fn request(
4689 _: &mut Self,
4690 _: &Client,
4691 _: &ZwpPointerConstraintsV1,
4692 request: <ZwpPointerConstraintsV1 as Resource>::Request,
4693 _: &(),
4694 _: &DisplayHandle,
4695 data_init: &mut DataInit<'_, Self>,
4696 ) {
4697 use zwp_pointer_constraints_v1::Request;
4698 match request {
4699 Request::LockPointer {
4700 id,
4701 surface: _,
4702 pointer: _,
4703 region: _,
4704 lifetime: _,
4705 } => {
4706 let lp = data_init.init(id, ());
4707 lp.locked();
4709 }
4710 Request::ConfinePointer {
4711 id,
4712 surface: _,
4713 pointer: _,
4714 region: _,
4715 lifetime: _,
4716 } => {
4717 let cp = data_init.init(id, ());
4718 cp.confined();
4719 }
4720 Request::Destroy => {}
4721 _ => {}
4722 }
4723 }
4724}
4725
4726impl Dispatch<ZwpLockedPointerV1, ()> for Compositor {
4727 fn request(
4728 _: &mut Self,
4729 _: &Client,
4730 _: &ZwpLockedPointerV1,
4731 _: <ZwpLockedPointerV1 as Resource>::Request,
4732 _: &(),
4733 _: &DisplayHandle,
4734 _: &mut DataInit<'_, Self>,
4735 ) {
4736 }
4738}
4739
4740impl Dispatch<ZwpConfinedPointerV1, ()> for Compositor {
4741 fn request(
4742 _: &mut Self,
4743 _: &Client,
4744 _: &ZwpConfinedPointerV1,
4745 _: <ZwpConfinedPointerV1 as Resource>::Request,
4746 _: &(),
4747 _: &DisplayHandle,
4748 _: &mut DataInit<'_, Self>,
4749 ) {
4750 }
4752}
4753
4754impl GlobalDispatch<ZwpRelativePointerManagerV1, ()> for Compositor {
4757 fn bind(
4758 _: &mut Self,
4759 _: &DisplayHandle,
4760 _: &Client,
4761 resource: New<ZwpRelativePointerManagerV1>,
4762 _: &(),
4763 data_init: &mut DataInit<'_, Self>,
4764 ) {
4765 data_init.init(resource, ());
4766 }
4767}
4768
4769impl Dispatch<ZwpRelativePointerManagerV1, ()> for Compositor {
4770 fn request(
4771 state: &mut Self,
4772 _: &Client,
4773 _: &ZwpRelativePointerManagerV1,
4774 request: <ZwpRelativePointerManagerV1 as Resource>::Request,
4775 _: &(),
4776 _: &DisplayHandle,
4777 data_init: &mut DataInit<'_, Self>,
4778 ) {
4779 use zwp_relative_pointer_manager_v1::Request;
4780 match request {
4781 Request::GetRelativePointer { id, pointer: _ } => {
4782 let rp = data_init.init(id, ());
4783 state.relative_pointers.push(rp);
4784 }
4785 Request::Destroy => {}
4786 _ => {}
4787 }
4788 }
4789}
4790
4791impl Dispatch<ZwpRelativePointerV1, ()> for Compositor {
4792 fn request(
4793 state: &mut Self,
4794 _: &Client,
4795 resource: &ZwpRelativePointerV1,
4796 _: <ZwpRelativePointerV1 as Resource>::Request,
4797 _: &(),
4798 _: &DisplayHandle,
4799 _: &mut DataInit<'_, Self>,
4800 ) {
4801 state
4803 .relative_pointers
4804 .retain(|rp| rp.id() != resource.id());
4805 }
4806}
4807
4808impl GlobalDispatch<ZwpTextInputManagerV3, ()> for Compositor {
4811 fn bind(
4812 _: &mut Self,
4813 _: &DisplayHandle,
4814 _: &Client,
4815 resource: New<ZwpTextInputManagerV3>,
4816 _: &(),
4817 data_init: &mut DataInit<'_, Self>,
4818 ) {
4819 data_init.init(resource, ());
4820 }
4821}
4822
4823impl Dispatch<ZwpTextInputManagerV3, ()> for Compositor {
4824 fn request(
4825 state: &mut Self,
4826 _: &Client,
4827 _: &ZwpTextInputManagerV3,
4828 request: <ZwpTextInputManagerV3 as Resource>::Request,
4829 _: &(),
4830 _: &DisplayHandle,
4831 data_init: &mut DataInit<'_, Self>,
4832 ) {
4833 use zwp_text_input_manager_v3::Request;
4834 match request {
4835 Request::GetTextInput { id, seat: _ } => {
4836 let ti = data_init.init(id, ());
4837 state.text_inputs.push(TextInputState {
4838 resource: ti,
4839 enabled: false,
4840 });
4841 }
4842 Request::Destroy => {}
4843 _ => {}
4844 }
4845 }
4846}
4847
4848impl Dispatch<ZwpTextInputV3, ()> for Compositor {
4849 fn request(
4850 state: &mut Self,
4851 _: &Client,
4852 resource: &ZwpTextInputV3,
4853 request: <ZwpTextInputV3 as Resource>::Request,
4854 _: &(),
4855 _: &DisplayHandle,
4856 _: &mut DataInit<'_, Self>,
4857 ) {
4858 use zwp_text_input_v3::Request;
4859 match request {
4860 Request::Enable => {
4861 if let Some(ti) = state
4862 .text_inputs
4863 .iter_mut()
4864 .find(|t| t.resource.id() == resource.id())
4865 {
4866 ti.enabled = true;
4867 }
4868 }
4869 Request::Disable => {
4870 if let Some(ti) = state
4871 .text_inputs
4872 .iter_mut()
4873 .find(|t| t.resource.id() == resource.id())
4874 {
4875 ti.enabled = false;
4876 }
4877 }
4878 Request::Commit => {
4879 }
4881 Request::Destroy => {
4882 state
4883 .text_inputs
4884 .retain(|t| t.resource.id() != resource.id());
4885 }
4886 _ => {}
4889 }
4890 }
4891}
4892
4893impl GlobalDispatch<XdgActivationV1, ()> for Compositor {
4896 fn bind(
4897 _: &mut Self,
4898 _: &DisplayHandle,
4899 _: &Client,
4900 resource: New<XdgActivationV1>,
4901 _: &(),
4902 data_init: &mut DataInit<'_, Self>,
4903 ) {
4904 data_init.init(resource, ());
4905 }
4906}
4907
4908impl Dispatch<XdgActivationV1, ()> for Compositor {
4909 fn request(
4910 state: &mut Self,
4911 _: &Client,
4912 _: &XdgActivationV1,
4913 request: <XdgActivationV1 as Resource>::Request,
4914 _: &(),
4915 _: &DisplayHandle,
4916 data_init: &mut DataInit<'_, Self>,
4917 ) {
4918 use xdg_activation_v1::Request;
4919 match request {
4920 Request::GetActivationToken { id } => {
4921 let serial = state.next_activation_token;
4922 state.next_activation_token = serial.wrapping_add(1);
4923 data_init.init(id, ActivationTokenData { serial });
4924 }
4925 Request::Activate {
4926 token: _,
4927 surface: _,
4928 } => {
4929 }
4932 Request::Destroy => {}
4933 _ => {}
4934 }
4935 }
4936}
4937
4938impl Dispatch<XdgActivationTokenV1, ActivationTokenData> for Compositor {
4939 fn request(
4940 _: &mut Self,
4941 _: &Client,
4942 resource: &XdgActivationTokenV1,
4943 request: <XdgActivationTokenV1 as Resource>::Request,
4944 data: &ActivationTokenData,
4945 _: &DisplayHandle,
4946 _: &mut DataInit<'_, Self>,
4947 ) {
4948 use xdg_activation_token_v1::Request;
4949 match request {
4950 Request::Commit => {
4951 resource.done(format!("blit-token-{}", data.serial));
4954 }
4955 Request::SetSerial { .. } | Request::SetAppId { .. } | Request::SetSurface { .. } => {}
4956 Request::Destroy => {}
4957 _ => {}
4958 }
4959 }
4960}
4961
4962impl GlobalDispatch<WpCursorShapeManagerV1, ()> for Compositor {
4965 fn bind(
4966 _: &mut Self,
4967 _: &DisplayHandle,
4968 _: &Client,
4969 resource: New<WpCursorShapeManagerV1>,
4970 _: &(),
4971 data_init: &mut DataInit<'_, Self>,
4972 ) {
4973 data_init.init(resource, ());
4974 }
4975}
4976
4977impl Dispatch<WpCursorShapeManagerV1, ()> for Compositor {
4978 fn request(
4979 _: &mut Self,
4980 _: &Client,
4981 _: &WpCursorShapeManagerV1,
4982 request: <WpCursorShapeManagerV1 as Resource>::Request,
4983 _: &(),
4984 _: &DisplayHandle,
4985 data_init: &mut DataInit<'_, Self>,
4986 ) {
4987 use wp_cursor_shape_manager_v1::Request;
4988 match request {
4989 Request::GetPointer {
4990 cursor_shape_device,
4991 pointer: _,
4992 } => {
4993 data_init.init(cursor_shape_device, ());
4994 }
4995 Request::GetTabletToolV2 {
4996 cursor_shape_device,
4997 tablet_tool: _,
4998 } => {
4999 data_init.init(cursor_shape_device, ());
5000 }
5001 Request::Destroy => {}
5002 _ => {}
5003 }
5004 }
5005}
5006
5007impl Dispatch<WpCursorShapeDeviceV1, ()> for Compositor {
5008 fn request(
5009 state: &mut Self,
5010 _: &Client,
5011 _: &WpCursorShapeDeviceV1,
5012 request: <WpCursorShapeDeviceV1 as Resource>::Request,
5013 _: &(),
5014 _: &DisplayHandle,
5015 _: &mut DataInit<'_, Self>,
5016 ) {
5017 use wp_cursor_shape_device_v1::Request;
5018 match request {
5019 Request::SetShape { serial: _, shape } => {
5020 use wayland_server::WEnum;
5021 use wp_cursor_shape_device_v1::Shape;
5022 let name = match shape {
5023 WEnum::Value(Shape::Default) => "default",
5024 WEnum::Value(Shape::ContextMenu) => "context-menu",
5025 WEnum::Value(Shape::Help) => "help",
5026 WEnum::Value(Shape::Pointer) => "pointer",
5027 WEnum::Value(Shape::Progress) => "progress",
5028 WEnum::Value(Shape::Wait) => "wait",
5029 WEnum::Value(Shape::Cell) => "cell",
5030 WEnum::Value(Shape::Crosshair) => "crosshair",
5031 WEnum::Value(Shape::Text) => "text",
5032 WEnum::Value(Shape::VerticalText) => "vertical-text",
5033 WEnum::Value(Shape::Alias) => "alias",
5034 WEnum::Value(Shape::Copy) => "copy",
5035 WEnum::Value(Shape::Move) => "move",
5036 WEnum::Value(Shape::NoDrop) => "no-drop",
5037 WEnum::Value(Shape::NotAllowed) => "not-allowed",
5038 WEnum::Value(Shape::Grab) => "grab",
5039 WEnum::Value(Shape::Grabbing) => "grabbing",
5040 WEnum::Value(Shape::EResize) => "e-resize",
5041 WEnum::Value(Shape::NResize) => "n-resize",
5042 WEnum::Value(Shape::NeResize) => "ne-resize",
5043 WEnum::Value(Shape::NwResize) => "nw-resize",
5044 WEnum::Value(Shape::SResize) => "s-resize",
5045 WEnum::Value(Shape::SeResize) => "se-resize",
5046 WEnum::Value(Shape::SwResize) => "sw-resize",
5047 WEnum::Value(Shape::WResize) => "w-resize",
5048 WEnum::Value(Shape::EwResize) => "ew-resize",
5049 WEnum::Value(Shape::NsResize) => "ns-resize",
5050 WEnum::Value(Shape::NeswResize) => "nesw-resize",
5051 WEnum::Value(Shape::NwseResize) => "nwse-resize",
5052 WEnum::Value(Shape::ColResize) => "col-resize",
5053 WEnum::Value(Shape::RowResize) => "row-resize",
5054 WEnum::Value(Shape::AllScroll) => "all-scroll",
5055 WEnum::Value(Shape::ZoomIn) => "zoom-in",
5056 WEnum::Value(Shape::ZoomOut) => "zoom-out",
5057 _ => "default",
5058 };
5059 let _ = state.event_tx.send(CompositorEvent::SurfaceCursor {
5060 surface_id: state.focused_surface_id,
5061 cursor: CursorImage::Named(name.to_string()),
5062 });
5063 (state.event_notify)();
5064 }
5065 Request::Destroy => {}
5066 _ => {}
5067 }
5068 }
5069}
5070
5071impl wayland_server::backend::ClientData for ClientState {
5073 fn initialized(&self, _: wayland_server::backend::ClientId) {}
5074 fn disconnected(
5075 &self,
5076 _: wayland_server::backend::ClientId,
5077 _: wayland_server::backend::DisconnectReason,
5078 ) {
5079 }
5080}
5081
5082pub struct CompositorHandle {
5087 pub event_rx: mpsc::Receiver<CompositorEvent>,
5088 pub command_tx: mpsc::Sender<CompositorCommand>,
5089 pub socket_name: String,
5090 pub thread: std::thread::JoinHandle<()>,
5091 pub shutdown: Arc<AtomicBool>,
5092 pub vulkan_video_encode: bool,
5094 pub vulkan_video_encode_av1: bool,
5096 loop_signal: LoopSignal,
5097}
5098
5099impl CompositorHandle {
5100 pub fn wake(&self) {
5101 self.loop_signal.wakeup();
5102 }
5103}
5104
5105pub fn spawn_compositor(
5106 verbose: bool,
5107 event_notify: Arc<dyn Fn() + Send + Sync>,
5108 gpu_device: &str,
5109) -> CompositorHandle {
5110 let _gpu_device = gpu_device.to_string();
5111 let (event_tx, event_rx) = mpsc::channel();
5112 let (command_tx, command_rx) = mpsc::channel();
5113 let (socket_tx, socket_rx) = mpsc::sync_channel(1);
5114 let (signal_tx, signal_rx) = mpsc::sync_channel::<LoopSignal>(1);
5115 let (caps_tx, caps_rx) = mpsc::sync_channel::<(bool, bool)>(1);
5116 let shutdown = Arc::new(AtomicBool::new(false));
5117 let shutdown_clone = shutdown.clone();
5118
5119 let runtime_dir = std::env::var_os("XDG_RUNTIME_DIR")
5120 .map(std::path::PathBuf::from)
5121 .filter(|p| {
5122 let probe = p.join(".blit-probe");
5123 if std::fs::write(&probe, b"").is_ok() {
5124 let _ = std::fs::remove_file(&probe);
5125 true
5126 } else {
5127 false
5128 }
5129 })
5130 .unwrap_or_else(std::env::temp_dir);
5131
5132 let runtime_dir_clone = runtime_dir.clone();
5133 let thread = std::thread::Builder::new()
5134 .name("compositor".into())
5135 .spawn(move || {
5136 unsafe { std::env::set_var("XDG_RUNTIME_DIR", &runtime_dir_clone) };
5137 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
5138 run_compositor(
5139 event_tx,
5140 command_rx,
5141 socket_tx,
5142 signal_tx,
5143 caps_tx,
5144 event_notify,
5145 shutdown_clone,
5146 verbose,
5147 _gpu_device,
5148 );
5149 }));
5150 if let Err(e) = result {
5151 let msg = if let Some(s) = e.downcast_ref::<&str>() {
5152 s.to_string()
5153 } else if let Some(s) = e.downcast_ref::<String>() {
5154 s.clone()
5155 } else {
5156 "unknown panic".to_string()
5157 };
5158 eprintln!("[compositor] PANIC: {msg}");
5159 }
5160 })
5161 .expect("failed to spawn compositor thread");
5162
5163 let socket_name = socket_rx.recv().expect("compositor failed to start");
5164 let socket_name = runtime_dir
5165 .join(&socket_name)
5166 .to_string_lossy()
5167 .into_owned();
5168 let loop_signal = signal_rx
5169 .recv()
5170 .expect("compositor failed to send loop signal");
5171 let (vulkan_video_encode, vulkan_video_encode_av1) = caps_rx.recv().unwrap_or((false, false));
5172
5173 CompositorHandle {
5174 event_rx,
5175 command_tx,
5176 socket_name,
5177 thread,
5178 shutdown,
5179 vulkan_video_encode,
5180 vulkan_video_encode_av1,
5181 loop_signal,
5182 }
5183}
5184
5185#[allow(clippy::too_many_arguments)]
5186fn run_compositor(
5187 event_tx: mpsc::Sender<CompositorEvent>,
5188 command_rx: mpsc::Receiver<CompositorCommand>,
5189 socket_tx: mpsc::SyncSender<String>,
5190 signal_tx: mpsc::SyncSender<LoopSignal>,
5191 caps_tx: mpsc::SyncSender<(bool, bool)>,
5192 event_notify: Arc<dyn Fn() + Send + Sync>,
5193 shutdown: Arc<AtomicBool>,
5194 verbose: bool,
5195 gpu_device: String,
5196) {
5197 let mut event_loop: EventLoop<Compositor> =
5198 EventLoop::try_new().expect("failed to create event loop");
5199 let loop_signal = event_loop.get_signal();
5200
5201 let display: Display<Compositor> = Display::new().expect("failed to create display");
5202 let dh = display.handle();
5203
5204 eprintln!("[compositor] trying Vulkan renderer for {gpu_device}");
5207 let vulkan_renderer = super::vulkan_render::VulkanRenderer::try_new(&gpu_device);
5208 let has_dmabuf = vulkan_renderer.as_ref().is_some_and(|vk| vk.has_dmabuf());
5209 eprintln!(
5210 "[compositor] Vulkan renderer: {} (dmabuf={})",
5211 vulkan_renderer.is_some(),
5212 has_dmabuf,
5213 );
5214
5215 dh.create_global::<Compositor, WlCompositor, ()>(6, ());
5217 dh.create_global::<Compositor, WlSubcompositor, ()>(1, ());
5218 dh.create_global::<Compositor, XdgWmBase, ()>(6, ());
5219 dh.create_global::<Compositor, WlShm, ()>(1, ());
5220 dh.create_global::<Compositor, WlOutput, ()>(4, ());
5221 dh.create_global::<Compositor, WlSeat, ()>(9, ());
5222 if has_dmabuf {
5227 dh.create_global::<Compositor, ZwpLinuxDmabufV1, ()>(4, ());
5228 }
5229 dh.create_global::<Compositor, WpViewporter, ()>(1, ());
5230 dh.create_global::<Compositor, WpFractionalScaleManagerV1, ()>(1, ());
5231 dh.create_global::<Compositor, ZxdgDecorationManagerV1, ()>(1, ());
5232 dh.create_global::<Compositor, WlDataDeviceManager, ()>(3, ());
5233 dh.create_global::<Compositor, ZwpPointerConstraintsV1, ()>(1, ());
5234 dh.create_global::<Compositor, ZwpRelativePointerManagerV1, ()>(1, ());
5235 dh.create_global::<Compositor, XdgActivationV1, ()>(1, ());
5236 dh.create_global::<Compositor, WpCursorShapeManagerV1, ()>(1, ());
5237 dh.create_global::<Compositor, ZwpPrimarySelectionDeviceManagerV1, ()>(1, ());
5238 dh.create_global::<Compositor, WpPresentation, ()>(1, ());
5239 dh.create_global::<Compositor, ZwpTextInputManagerV3, ()>(1, ());
5240
5241 let keymap_string = include_str!("../data/us-qwerty.xkb");
5243 let mut keymap_data = keymap_string.as_bytes().to_vec();
5244 keymap_data.push(0); let listening_socket = wayland_server::ListeningSocket::bind_auto("wayland", 0..33)
5248 .unwrap_or_else(|e| {
5249 let dir = std::env::var("XDG_RUNTIME_DIR").unwrap_or_else(|_| "(unset)".into());
5250 panic!("failed to create wayland socket in XDG_RUNTIME_DIR={dir}: {e}\nhint: ensure the directory exists and is writable by the current user");
5251 });
5252 let socket_name = listening_socket
5253 .socket_name()
5254 .unwrap()
5255 .to_string_lossy()
5256 .into_owned();
5257 socket_tx.send(socket_name).unwrap();
5258 let _ = signal_tx.send(loop_signal.clone());
5259
5260 let mut compositor = Compositor {
5261 display_handle: dh,
5262 surfaces: HashMap::new(),
5263 toplevel_surface_ids: HashMap::new(),
5264 next_surface_id: 1,
5265 shm_pools: HashMap::new(),
5266 surface_meta: HashMap::new(),
5267 dmabuf_params: HashMap::new(),
5268 vulkan_renderer,
5269 output_width: 1920,
5270 output_height: 1080,
5271 output_refresh_mhz: 60_000,
5272 output_scale_120: 120,
5273 outputs: Vec::new(),
5274 keyboards: Vec::new(),
5275 pointers: Vec::new(),
5276 keyboard_keymap_data: keymap_data,
5277 mods_depressed: 0,
5278 mods_locked: 0,
5279 serial: 0,
5280 event_tx,
5281 event_notify,
5282 loop_signal: loop_signal.clone(),
5283 pending_commits: HashMap::new(),
5284 focused_surface_id: 0,
5285 pointer_entered_id: None,
5286 pending_kb_reenter: false,
5287 gpu_device,
5288 verbose,
5289 shutdown: shutdown.clone(),
5290 last_reported_size: HashMap::new(),
5291 surface_sizes: HashMap::new(),
5292 positioners: HashMap::new(),
5293 fractional_scales: Vec::new(),
5294 data_devices: Vec::new(),
5295 selection_source: None,
5296 external_clipboard: None,
5297 primary_devices: Vec::new(),
5298 primary_source: None,
5299 external_primary: None,
5300 relative_pointers: Vec::new(),
5301 text_inputs: Vec::new(),
5302 text_input_serial: 0,
5303 next_activation_token: 1,
5304 popup_grab_stack: Vec::new(),
5305 held_buffers: HashMap::new(),
5306 cursor_rgba: HashMap::new(),
5307 };
5308
5309 {
5311 let (vve, vve_av1) = compositor
5312 .vulkan_renderer
5313 .as_ref()
5314 .map(|vk| (vk.has_video_encode(), vk.has_video_encode_av1()))
5315 .unwrap_or((false, false));
5316 let _ = caps_tx.send((vve, vve_av1));
5317 }
5318
5319 let handle = event_loop.handle();
5320
5321 let display_source = Generic::new(display, Interest::READ, calloop::Mode::Level);
5323 handle
5324 .insert_source(display_source, |_, display, state| {
5325 let d = unsafe { display.get_mut() };
5326 if let Err(e) = d.dispatch_clients(state)
5327 && state.verbose
5328 {
5329 eprintln!("[compositor] dispatch_clients error: {e}");
5330 }
5331 state.cleanup_dead_surfaces();
5332 if let Err(e) = d.flush_clients()
5333 && state.verbose
5334 {
5335 eprintln!("[compositor] flush_clients error: {e}");
5336 }
5337 Ok(PostAction::Continue)
5338 })
5339 .expect("failed to insert display source");
5340
5341 let socket_source = Generic::new(listening_socket, Interest::READ, calloop::Mode::Level);
5343 handle
5344 .insert_source(socket_source, |_, socket, state| {
5345 let ls = unsafe { socket.get_mut() };
5346 if let Some(client_stream) = ls.accept().ok().flatten()
5347 && let Err(e) = state
5348 .display_handle
5349 .insert_client(client_stream, Arc::new(ClientState))
5350 && state.verbose
5351 {
5352 eprintln!("[compositor] insert_client error: {e}");
5353 }
5354 Ok(PostAction::Continue)
5355 })
5356 .expect("failed to insert listening socket");
5357
5358 if verbose {
5359 eprintln!("[compositor] entering event loop");
5360 }
5361
5362 while !shutdown.load(Ordering::Relaxed) {
5363 while let Ok(cmd) = command_rx.try_recv() {
5365 match cmd {
5366 CompositorCommand::Shutdown => {
5367 shutdown.store(true, Ordering::Relaxed);
5368 return;
5369 }
5370 other => compositor.handle_command(other),
5371 }
5372 }
5373
5374 let poll_timeout = if compositor
5377 .vulkan_renderer
5378 .as_ref()
5379 .is_some_and(|vk| vk.has_pending())
5380 {
5381 std::time::Duration::from_millis(1)
5382 } else {
5383 std::time::Duration::from_secs(1)
5384 };
5385
5386 if let Err(e) = event_loop.dispatch(Some(poll_timeout), &mut compositor)
5387 && verbose
5388 {
5389 eprintln!("[compositor] event loop error: {e}");
5390 }
5391
5392 if let Some(ref mut vk) = compositor.vulkan_renderer
5396 && let Some((sid, w, h, pixels)) = vk.try_retire_pending()
5397 {
5398 let s120_u32 = (compositor.output_scale_120 as u32).max(120);
5399 let log_w = (w * 120).div_ceil(s120_u32);
5400 let log_h = (h * 120).div_ceil(s120_u32);
5401 compositor
5402 .pending_commits
5403 .insert(sid, (w, h, log_w, log_h, pixels));
5404 }
5405
5406 if !compositor.pending_commits.is_empty() {
5407 compositor.flush_pending_commits();
5408 }
5409
5410 if let Err(e) = compositor.display_handle.flush_clients()
5411 && verbose
5412 {
5413 eprintln!("[compositor] flush error: {e}");
5414 }
5415 }
5416
5417 if verbose {
5418 eprintln!("[compositor] event loop exited");
5419 }
5420}