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 with_mmap<F, R>(&self, f: F) -> Option<R>
734 where
735 F: FnOnce(&[u8]) -> R,
736 {
737 let inner = self.inner.lock().unwrap();
738 if inner.mmap_ptr.is_null() {
739 return None;
740 }
741 let slice = unsafe { std::slice::from_raw_parts(inner.mmap_ptr, inner.size) };
742 Some(f(slice))
743 }
744
745 fn read_buffer(
746 &self,
747 offset: i32,
748 width: i32,
749 height: i32,
750 stride: i32,
751 format: wl_shm::Format,
752 ) -> Option<(u32, u32, PixelData)> {
753 let inner = self.inner.lock().unwrap();
754 if inner.mmap_ptr.is_null() {
755 return None;
756 }
757 let w = width as u32;
758 let h = height as u32;
759 let s = stride as usize;
760 let off = offset as usize;
761 let row_bytes = w as usize * 4;
762 let needed = off + s * (h as usize).saturating_sub(1) + row_bytes;
763 if needed > inner.size {
764 return None;
765 }
766 let mut bgra = if s == row_bytes && off == 0 {
767 let total = row_bytes * h as usize;
768 unsafe { std::slice::from_raw_parts(inner.mmap_ptr, total) }.to_vec()
769 } else {
770 let mut packed = Vec::with_capacity(row_bytes * h as usize);
771 for row in 0..h as usize {
772 let src = unsafe {
773 std::slice::from_raw_parts(inner.mmap_ptr.add(off + row * s), row_bytes)
774 };
775 packed.extend_from_slice(src);
776 }
777 packed
778 };
779 if matches!(format, wl_shm::Format::Xrgb8888 | wl_shm::Format::Xbgr8888) {
780 for px in bgra.chunks_exact_mut(4) {
781 px[3] = 255;
782 }
783 }
784 if matches!(format, wl_shm::Format::Abgr8888 | wl_shm::Format::Xbgr8888) {
785 Some((w, h, PixelData::Rgba(Arc::new(bgra))))
786 } else {
787 Some((w, h, PixelData::Bgra(Arc::new(bgra))))
788 }
789 }
790}
791
792impl Drop for ShmPool {
793 fn drop(&mut self) {
794 let inner = self.inner.get_mut().unwrap();
795 if !inner.mmap_ptr.is_null() {
796 unsafe {
797 libc::munmap(inner.mmap_ptr as *mut _, inner.size);
798 }
799 }
800 }
801}
802
803unsafe impl Send for ShmPool {}
804
805struct ShmBufferData {
806 pool: Arc<ShmPool>,
812 offset: i32,
813 width: i32,
814 height: i32,
815 stride: i32,
816 format: wl_shm::Format,
817}
818
819struct DmaBufBufferData {
820 width: i32,
821 height: i32,
822 fourcc: u32,
823 modifier: u64,
824 planes: Vec<DmaBufPlane>,
825 y_invert: bool,
826}
827
828struct DmaBufPlane {
829 fd: OwnedFd,
830 offset: u32,
831 stride: u32,
832}
833
834struct DmaBufParamsPending {
835 resource: ZwpLinuxBufferParamsV1,
836 planes: Vec<DmaBufPlane>,
837 modifier: u64,
838}
839
840struct ClientState;
841struct XdgSurfaceData {
842 wl_surface_id: ObjectId,
843}
844struct XdgToplevelData {
845 wl_surface_id: ObjectId,
846}
847struct XdgPopupData {
848 wl_surface_id: ObjectId,
849}
850struct SubsurfaceData {
851 wl_surface_id: ObjectId,
852 parent_surface_id: ObjectId,
853}
854
855struct DataSourceData {
858 mime_types: std::sync::Mutex<Vec<String>>,
859}
860
861struct DataOfferData {
862 external: bool,
866}
867
868struct ExternalClipboard {
870 mime_type: String,
871 data: Vec<u8>,
872}
873
874struct PrimarySourceData {
875 mime_types: std::sync::Mutex<Vec<String>>,
876}
877struct PrimaryOfferData {
878 external: bool,
879}
880
881struct ActivationTokenData {
883 serial: u32,
884}
885
886struct PositionerState {
887 resource: XdgPositioner,
888 geometry: PositionerGeometry,
889}
890
891fn char_to_keycode(ch: char) -> Option<(u32, bool)> {
899 const KEY_1: u32 = 2;
900 const KEY_2: u32 = 3;
901 const KEY_3: u32 = 4;
902 const KEY_4: u32 = 5;
903 const KEY_5: u32 = 6;
904 const KEY_6: u32 = 7;
905 const KEY_7: u32 = 8;
906 const KEY_8: u32 = 9;
907 const KEY_9: u32 = 10;
908 const KEY_0: u32 = 11;
909 const KEY_MINUS: u32 = 12;
910 const KEY_EQUAL: u32 = 13;
911 const KEY_TAB: u32 = 15;
912 const KEY_Q: u32 = 16;
913 const KEY_W: u32 = 17;
914 const KEY_E: u32 = 18;
915 const KEY_R: u32 = 19;
916 const KEY_T: u32 = 20;
917 const KEY_Y: u32 = 21;
918 const KEY_U: u32 = 22;
919 const KEY_I: u32 = 23;
920 const KEY_O: u32 = 24;
921 const KEY_P: u32 = 25;
922 const KEY_LEFTBRACE: u32 = 26;
923 const KEY_RIGHTBRACE: u32 = 27;
924 const KEY_ENTER: u32 = 28;
925 const KEY_A: u32 = 30;
926 const KEY_S: u32 = 31;
927 const KEY_D: u32 = 32;
928 const KEY_F: u32 = 33;
929 const KEY_G: u32 = 34;
930 const KEY_H: u32 = 35;
931 const KEY_J: u32 = 36;
932 const KEY_K: u32 = 37;
933 const KEY_L: u32 = 38;
934 const KEY_SEMICOLON: u32 = 39;
935 const KEY_APOSTROPHE: u32 = 40;
936 const KEY_GRAVE: u32 = 41;
937 const KEY_BACKSLASH: u32 = 43;
938 const KEY_Z: u32 = 44;
939 const KEY_X: u32 = 45;
940 const KEY_C: u32 = 46;
941 const KEY_V: u32 = 47;
942 const KEY_B: u32 = 48;
943 const KEY_N: u32 = 49;
944 const KEY_M: u32 = 50;
945 const KEY_COMMA: u32 = 51;
946 const KEY_DOT: u32 = 52;
947 const KEY_SLASH: u32 = 53;
948 const KEY_SPACE: u32 = 57;
949
950 fn letter_kc(ch: char) -> u32 {
951 match ch {
952 'a' => KEY_A,
953 'b' => KEY_B,
954 'c' => KEY_C,
955 'd' => KEY_D,
956 'e' => KEY_E,
957 'f' => KEY_F,
958 'g' => KEY_G,
959 'h' => KEY_H,
960 'i' => KEY_I,
961 'j' => KEY_J,
962 'k' => KEY_K,
963 'l' => KEY_L,
964 'm' => KEY_M,
965 'n' => KEY_N,
966 'o' => KEY_O,
967 'p' => KEY_P,
968 'q' => KEY_Q,
969 'r' => KEY_R,
970 's' => KEY_S,
971 't' => KEY_T,
972 'u' => KEY_U,
973 'v' => KEY_V,
974 'w' => KEY_W,
975 'x' => KEY_X,
976 'y' => KEY_Y,
977 'z' => KEY_Z,
978 _ => KEY_SPACE,
979 }
980 }
981
982 let (kc, shift) = match ch {
983 'a'..='z' => (letter_kc(ch), false),
984 'A'..='Z' => (letter_kc(ch.to_ascii_lowercase()), true),
985 '0' => (KEY_0, false),
986 '1'..='9' => (KEY_1 + (ch as u32 - '1' as u32), false),
987 ' ' => (KEY_SPACE, false),
988 '-' => (KEY_MINUS, false),
989 '=' => (KEY_EQUAL, false),
990 '[' => (KEY_LEFTBRACE, false),
991 ']' => (KEY_RIGHTBRACE, false),
992 ';' => (KEY_SEMICOLON, false),
993 '\'' => (KEY_APOSTROPHE, false),
994 ',' => (KEY_COMMA, false),
995 '.' => (KEY_DOT, false),
996 '/' => (KEY_SLASH, false),
997 '\\' => (KEY_BACKSLASH, false),
998 '`' => (KEY_GRAVE, false),
999 '\t' => (KEY_TAB, false),
1000 '\n' => (KEY_ENTER, false),
1001 '!' => (KEY_1, true),
1002 '@' => (KEY_2, true),
1003 '#' => (KEY_3, true),
1004 '$' => (KEY_4, true),
1005 '%' => (KEY_5, true),
1006 '^' => (KEY_6, true),
1007 '&' => (KEY_7, true),
1008 '*' => (KEY_8, true),
1009 '(' => (KEY_9, true),
1010 ')' => (KEY_0, true),
1011 '_' => (KEY_MINUS, true),
1012 '+' => (KEY_EQUAL, true),
1013 '{' => (KEY_LEFTBRACE, true),
1014 '}' => (KEY_RIGHTBRACE, true),
1015 ':' => (KEY_SEMICOLON, true),
1016 '"' => (KEY_APOSTROPHE, true),
1017 '<' => (KEY_COMMA, true),
1018 '>' => (KEY_DOT, true),
1019 '?' => (KEY_SLASH, true),
1020 '|' => (KEY_BACKSLASH, true),
1021 '~' => (KEY_GRAVE, true),
1022 _ => return None,
1023 };
1024 Some((kc, shift))
1025}
1026
1027const MOD_SHIFT: u32 = 1 << 0;
1033const MOD_LOCK: u32 = 1 << 1;
1034const MOD_CONTROL: u32 = 1 << 2;
1035const MOD_MOD1: u32 = 1 << 3; const MOD_MOD4: u32 = 1 << 6; fn keycode_to_mod(keycode: u32) -> u32 {
1041 match keycode {
1042 42 | 54 => MOD_SHIFT, 58 => MOD_LOCK, 29 | 97 => MOD_CONTROL, 56 | 100 => MOD_MOD1, 125 | 126 => MOD_MOD4, _ => 0,
1048 }
1049}
1050
1051struct TextInputState {
1053 resource: ZwpTextInputV3,
1054 enabled: bool,
1056}
1057
1058struct Compositor {
1060 display_handle: DisplayHandle,
1061 surfaces: HashMap<ObjectId, Surface>,
1062 toplevel_surface_ids: HashMap<u16, ObjectId>,
1063 next_surface_id: u16,
1064 shm_pools: HashMap<ObjectId, Arc<ShmPool>>,
1065 surface_meta: HashMap<ObjectId, super::render::SurfaceMeta>,
1069 dmabuf_params: HashMap<ObjectId, DmaBufParamsPending>,
1070 vulkan_renderer: Option<super::vulkan_render::VulkanRenderer>,
1071 output_width: i32,
1072 output_height: i32,
1073 output_refresh_mhz: u32,
1076 output_scale_120: u16,
1080 outputs: Vec<WlOutput>,
1081 keyboards: Vec<WlKeyboard>,
1082 pointers: Vec<WlPointer>,
1083 keyboard_keymap_data: Vec<u8>,
1084 mods_depressed: u32,
1086 mods_locked: u32,
1088 serial: u32,
1089 event_tx: mpsc::Sender<CompositorEvent>,
1090 event_notify: Arc<dyn Fn() + Send + Sync>,
1091 loop_signal: LoopSignal,
1092 pending_commits: HashMap<u16, (u32, u32, u32, u32, PixelData)>,
1094 focused_surface_id: u16,
1095 pointer_entered_id: Option<ObjectId>,
1097 pending_kb_reenter: bool,
1101
1102 gpu_device: String,
1103 verbose: bool,
1104 shutdown: Arc<AtomicBool>,
1105 last_reported_size: HashMap<u16, (u32, u32, u32, u32)>,
1109 surface_sizes: HashMap<u16, (i32, i32)>,
1113 positioners: HashMap<ObjectId, PositionerState>,
1115 fractional_scales: Vec<WpFractionalScaleV1>,
1118
1119 data_devices: Vec<WlDataDevice>,
1122 selection_source: Option<WlDataSource>,
1125 external_clipboard: Option<ExternalClipboard>,
1127
1128 primary_devices: Vec<ZwpPrimarySelectionDeviceV1>,
1130 primary_source: Option<ZwpPrimarySelectionSourceV1>,
1131 external_primary: Option<ExternalClipboard>,
1132
1133 relative_pointers: Vec<ZwpRelativePointerV1>,
1135
1136 text_inputs: Vec<TextInputState>,
1141 #[expect(dead_code)]
1144 text_input_serial: u32,
1145
1146 next_activation_token: u32,
1148
1149 popup_grab_stack: Vec<ObjectId>,
1154
1155 held_buffers: HashMap<ObjectId, WlBuffer>,
1162
1163 cursor_rgba: HashMap<ObjectId, (u32, u32, Vec<u8>)>,
1168}
1169
1170impl Compositor {
1171 fn next_serial(&mut self) -> u32 {
1172 self.serial = self.serial.wrapping_add(1);
1173 self.serial
1174 }
1175
1176 fn update_and_send_modifiers(&mut self, keycode: u32, pressed: bool) {
1181 let m = keycode_to_mod(keycode);
1182 if m == 0 {
1183 return;
1184 }
1185 if keycode == 58 {
1186 if pressed {
1188 self.mods_locked ^= MOD_LOCK;
1189 }
1190 } else if pressed {
1191 self.mods_depressed |= m;
1192 } else {
1193 self.mods_depressed &= !m;
1194 }
1195 let serial = self.next_serial();
1196 let focused_wl = self
1197 .toplevel_surface_ids
1198 .get(&self.focused_surface_id)
1199 .and_then(|root_id| self.surfaces.get(root_id))
1200 .map(|s| s.wl_surface.clone());
1201 for kb in &self.keyboards {
1202 if let Some(ref wl) = focused_wl
1203 && same_client(kb, wl)
1204 {
1205 kb.modifiers(serial, self.mods_depressed, 0, self.mods_locked, 0);
1206 }
1207 }
1208 }
1209
1210 fn set_keyboard_focus(&mut self, new_surface_id: u16) {
1215 let old_id = self.focused_surface_id;
1216 if old_id == new_surface_id {
1217 self.focused_surface_id = new_surface_id;
1220 if let Some(root_id) = self.toplevel_surface_ids.get(&new_surface_id)
1221 && let Some(wl_surface) = self.surfaces.get(root_id).map(|s| s.wl_surface.clone())
1222 {
1223 let serial = self.next_serial();
1224 for kb in &self.keyboards {
1225 if same_client(kb, &wl_surface) {
1226 kb.enter(serial, &wl_surface, vec![]);
1227 }
1228 }
1229 for ti in &self.text_inputs {
1230 if same_client(&ti.resource, &wl_surface) {
1231 ti.resource.enter(&wl_surface);
1232 }
1233 }
1234 }
1235 return;
1236 }
1237
1238 if old_id != 0
1240 && let Some(old_root) = self.toplevel_surface_ids.get(&old_id)
1241 && let Some(old_wl) = self.surfaces.get(old_root).map(|s| s.wl_surface.clone())
1242 {
1243 let serial = self.next_serial();
1244 for kb in &self.keyboards {
1245 if same_client(kb, &old_wl) {
1246 kb.leave(serial, &old_wl);
1247 }
1248 }
1249 for ti in &self.text_inputs {
1250 if same_client(&ti.resource, &old_wl) {
1251 ti.resource.leave(&old_wl);
1252 }
1253 }
1254 }
1255
1256 self.focused_surface_id = new_surface_id;
1257
1258 if let Some(root_id) = self.toplevel_surface_ids.get(&new_surface_id)
1260 && let Some(wl_surface) = self.surfaces.get(root_id).map(|s| s.wl_surface.clone())
1261 {
1262 let serial = self.next_serial();
1263 for kb in &self.keyboards {
1264 if same_client(kb, &wl_surface) {
1265 kb.enter(serial, &wl_surface, vec![]);
1266 }
1267 }
1268 for ti in &self.text_inputs {
1269 if same_client(&ti.resource, &wl_surface) {
1270 ti.resource.enter(&wl_surface);
1271 }
1272 }
1273 }
1274 }
1275
1276 fn allocate_surface_id(&mut self) -> u16 {
1277 let mut id = self.next_surface_id;
1278 let start = id;
1279 loop {
1280 if !self.toplevel_surface_ids.contains_key(&id) {
1281 break;
1282 }
1283 id = id.wrapping_add(1);
1284 if id == 0 {
1285 id = 1;
1286 }
1287 if id == start {
1288 break;
1289 }
1290 }
1291 self.next_surface_id = id.wrapping_add(1);
1292 if self.next_surface_id == 0 {
1293 self.next_surface_id = 1;
1294 }
1295 id
1296 }
1297
1298 fn flush_pending_commits(&mut self) {
1299 for (surface_id, (width, height, log_w, log_h, pixels)) in self.pending_commits.drain() {
1300 let prev = self.last_reported_size.get(&surface_id).copied();
1301 if prev.is_none() || prev.map(|(pw, ph, _, _)| (pw, ph)) != Some((width, height)) {
1302 self.last_reported_size
1303 .insert(surface_id, (width, height, log_w, log_h));
1304 let _ = self.event_tx.send(CompositorEvent::SurfaceResized {
1305 surface_id,
1306 width: width as u16,
1307 height: height as u16,
1308 });
1309 }
1310 let _ = self.event_tx.send(CompositorEvent::SurfaceCommit {
1311 surface_id,
1312 width,
1313 height,
1314 pixels,
1315 timestamp_ms: elapsed_ms(),
1316 });
1317 }
1318 (self.event_notify)();
1319 }
1320
1321 fn read_shm_buffer(&self, buffer: &WlBuffer) -> Option<(u32, u32, PixelData)> {
1322 let data = buffer.data::<ShmBufferData>()?;
1323 let r = data.pool.read_buffer(
1324 data.offset,
1325 data.width,
1326 data.height,
1327 data.stride,
1328 data.format,
1329 );
1330 if r.is_none() {
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_shm_buffer #{n}] pool.read_buffer=None off={} {}x{} stride={} fmt={:?}",
1336 data.offset, data.width, data.height, data.stride, data.format,
1337 );
1338 }
1339 }
1340 r
1341 }
1342
1343 fn read_dmabuf_buffer(&self, buffer: &WlBuffer) -> Option<(u32, u32, PixelData)> {
1344 let data = buffer.data::<DmaBufBufferData>()?;
1345 let width = data.width as u32;
1346 let height = data.height as u32;
1347 if width == 0 || height == 0 || data.planes.is_empty() {
1348 static N: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1349 let n = N.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1350 if n < 10 || n.is_multiple_of(100) {
1351 eprintln!(
1352 "[read_dmabuf_buffer #{n}] empty: {}x{} planes={}",
1353 width,
1354 height,
1355 data.planes.len()
1356 );
1357 }
1358 return None;
1359 }
1360 let plane = &data.planes[0];
1361 if matches!(
1362 data.fourcc,
1363 drm_fourcc::ARGB8888
1364 | drm_fourcc::XRGB8888
1365 | drm_fourcc::ABGR8888
1366 | drm_fourcc::XBGR8888
1367 ) {
1368 use std::os::fd::AsRawFd;
1371 let raw_fd = plane.fd.as_raw_fd();
1372 let _is_drm = {
1373 let mut link_buf = [0u8; 256];
1374 let path = format!("/proc/self/fd/{raw_fd}\0");
1375 let n = unsafe {
1376 libc::readlink(
1377 path.as_ptr() as *const _,
1378 link_buf.as_mut_ptr() as *mut _,
1379 255,
1380 )
1381 };
1382 n > 0 && link_buf[..n as usize].starts_with(b"/dev/dri/")
1383 };
1384
1385 let owned = plane.fd.try_clone().ok()?;
1389 return Some((
1390 width,
1391 height,
1392 PixelData::DmaBuf {
1393 fd: Arc::new(owned),
1394 fourcc: data.fourcc,
1395 modifier: data.modifier,
1396 stride: plane.stride,
1397 offset: plane.offset,
1398 y_invert: data.y_invert,
1399 },
1400 ));
1401 }
1402 static N: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1403 let n = N.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1404 if n < 10 || n.is_multiple_of(100) {
1405 eprintln!(
1406 "[read_dmabuf_buffer #{n}] unsupported fourcc=0x{:08x} ({}x{}) modifier=0x{:x}",
1407 data.fourcc, width, height, data.modifier,
1408 );
1409 }
1410 None
1411 }
1412
1413 fn read_buffer(&self, buffer: &WlBuffer) -> Option<(u32, u32, PixelData)> {
1414 if buffer.data::<ShmBufferData>().is_some() {
1418 return self.read_shm_buffer(buffer);
1419 }
1420 if buffer.data::<DmaBufBufferData>().is_some() {
1421 return self.read_dmabuf_buffer(buffer);
1422 }
1423 static N: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1424 let n = N.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1425 if n < 10 || n.is_multiple_of(100) {
1426 eprintln!(
1427 "[read_buffer #{n}] buffer has unknown role (neither Shm nor DmaBuf data attached)",
1428 );
1429 }
1430 None
1431 }
1432
1433 fn handle_surface_commit(&mut self, surface_id: &ObjectId) {
1434 let (root_id, toplevel_sid) = self.find_toplevel_root(surface_id);
1435
1436 let had_buffer = self
1441 .surfaces
1442 .get(surface_id)
1443 .is_some_and(|s| s.pending_buffer.is_some());
1444 {
1445 static N: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1446 let n = N.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1447 if n < 40 || n.is_multiple_of(200) {
1448 let children = self
1449 .surfaces
1450 .get(surface_id)
1451 .map(|s| s.children.len())
1452 .unwrap_or(0);
1453 eprintln!(
1454 "[commit-in #{n}] sid={surface_id:?} toplevel={toplevel_sid:?} root={root_id:?} had_buffer={had_buffer} children={children}",
1455 );
1456 }
1457 }
1458 self.apply_pending_state(surface_id);
1459
1460 let toplevel_sid = match toplevel_sid {
1461 Some(sid) => sid,
1462 None => {
1463 if let Some(held) = self.held_buffers.remove(surface_id) {
1466 held.release();
1467 }
1468 self.fire_surface_frame_callbacks(surface_id);
1471 let _ = self.display_handle.flush_clients();
1472 return;
1473 }
1474 };
1475
1476 let s120 = self.output_scale_120;
1480 let target_phys = self.surface_sizes.get(&toplevel_sid).map(|&(lw, lh)| {
1481 let pw = super::render::to_physical(lw as u32, s120 as u32);
1482 let ph = super::render::to_physical(lh as u32, s120 as u32);
1483 (pw, ph)
1484 });
1485 let composited = if let Some(ref mut vk) = self.vulkan_renderer {
1486 vk.render_tree_sized(
1487 &root_id,
1488 &self.surfaces,
1489 &self.surface_meta,
1490 s120,
1491 target_phys,
1492 toplevel_sid,
1493 )
1494 } else {
1495 None
1496 };
1497
1498 if let Some((result_sid, w, h, ref pixels)) = composited
1499 && !pixels.is_empty()
1500 {
1501 let kind = match pixels {
1502 PixelData::Bgra(_) => "bgra",
1503 PixelData::Rgba(_) => "rgba",
1504 PixelData::Nv12 { .. } => "nv12",
1505 PixelData::VaSurface { .. } => "va-surface",
1506 PixelData::Nv12DmaBuf { .. } => "nv12-dmabuf",
1507 PixelData::Encoded { .. } => "vulkan-encoded",
1508 PixelData::DmaBuf { fd, .. } => {
1509 use std::os::fd::AsRawFd;
1510 let raw = fd.as_raw_fd();
1511 let mut lb = [0u8; 128];
1512 let p = format!("/proc/self/fd/{raw}\0");
1513 let n = unsafe {
1514 libc::readlink(p.as_ptr() as *const _, lb.as_mut_ptr() as *mut _, 127)
1515 };
1516 if n > 0 && lb[..n as usize].starts_with(b"/dev/dri/") {
1517 "dmabuf-drm"
1518 } else {
1519 "dmabuf-anon"
1520 }
1521 }
1522 };
1523 if self.verbose {
1524 static LC: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1525 let lc = LC.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1526 if lc < 3 || lc.is_multiple_of(1000) {
1527 eprintln!("[pending #{lc}] {w}x{h} kind={kind}");
1528 }
1529 }
1530 let s120_u32 = (s120 as u32).max(120);
1545 let log_w = (w * 120).div_ceil(s120_u32);
1546 let log_h = (h * 120).div_ceil(s120_u32);
1547 self.pending_commits
1548 .insert(result_sid, (w, h, log_w, log_h, composited.unwrap().3));
1549 }
1550
1551 if let Some(held) = self.held_buffers.remove(surface_id) {
1556 held.release();
1557 }
1558
1559 self.fire_frame_callbacks_for_toplevel(toplevel_sid);
1564
1565 if self.pending_kb_reenter {
1570 self.pending_kb_reenter = false;
1571 let root_ids: Vec<ObjectId> = self.toplevel_surface_ids.values().cloned().collect();
1572 for root_id in root_ids {
1573 let wl = self.surfaces.get(&root_id).map(|s| s.wl_surface.clone());
1574 if let Some(wl) = wl {
1575 let serial = self.next_serial();
1576 for kb in &self.keyboards {
1577 if same_client(kb, &wl) {
1578 kb.leave(serial, &wl);
1579 }
1580 }
1581 let serial = self.next_serial();
1582 for kb in &self.keyboards {
1583 if same_client(kb, &wl) {
1584 kb.enter(serial, &wl, vec![]);
1585 }
1586 }
1587 }
1588 }
1589 let _ = self.display_handle.flush_clients();
1590 }
1591
1592 if self.verbose {
1593 let cache_entries = self.surface_meta.len();
1594 let has_pending = self.pending_commits.contains_key(&toplevel_sid);
1595 static COMMIT_COUNT: std::sync::atomic::AtomicU64 =
1596 std::sync::atomic::AtomicU64::new(0);
1597 let n = COMMIT_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1598 if n < 5 || n.is_multiple_of(1000) {
1599 eprintln!(
1600 "[commit #{n}] sid={surface_id:?} root={root_id:?} cache={cache_entries} pending={has_pending} buf={had_buffer}",
1601 );
1602 }
1603 }
1604 }
1605
1606 fn surface_absolute_position(&self, surface_id: &ObjectId) -> (i32, i32) {
1610 let mut x = 0i32;
1611 let mut y = 0i32;
1612 let mut current = surface_id.clone();
1613 while let Some(surf) = self.surfaces.get(¤t) {
1614 x += surf.subsurface_position.0;
1615 y += surf.subsurface_position.1;
1616 match surf.parent_surface_id {
1617 Some(ref parent) => current = parent.clone(),
1618 None => break,
1619 }
1620 }
1621 (x, y)
1622 }
1623
1624 fn find_toplevel_root(&self, surface_id: &ObjectId) -> (ObjectId, Option<u16>) {
1625 let mut current = surface_id.clone();
1626 loop {
1627 match self.surfaces.get(¤t) {
1628 Some(surf) => {
1629 if let Some(ref parent) = surf.parent_surface_id {
1630 current = parent.clone();
1631 } else {
1632 return (
1633 current,
1634 if surf.surface_id > 0 {
1635 Some(surf.surface_id)
1636 } else {
1637 None
1638 },
1639 );
1640 }
1641 }
1642 None => return (current, None),
1643 }
1644 }
1645 }
1646
1647 fn collect_surface_tree(&self, root_id: &ObjectId) -> Vec<ObjectId> {
1648 let mut result = Vec::new();
1649 self.collect_tree_recursive(root_id, &mut result);
1650 result
1651 }
1652
1653 fn collect_tree_recursive(&self, surface_id: &ObjectId, result: &mut Vec<ObjectId>) {
1654 result.push(surface_id.clone());
1655 if let Some(surf) = self.surfaces.get(surface_id) {
1656 for child_id in &surf.children {
1657 self.collect_tree_recursive(child_id, result);
1658 }
1659 }
1660 }
1661
1662 fn hit_test_surface_at(
1667 &self,
1668 root_id: &ObjectId,
1669 x: f64,
1670 y: f64,
1671 ) -> Option<(WlSurface, f64, f64)> {
1672 self.hit_test_recursive(root_id, x, y, 0, 0).or_else(|| {
1673 self.surfaces
1675 .get(root_id)
1676 .map(|s| (s.wl_surface.clone(), x, y))
1677 })
1678 }
1679
1680 fn hit_test_recursive(
1681 &self,
1682 surface_id: &ObjectId,
1683 x: f64,
1684 y: f64,
1685 offset_x: i32,
1686 offset_y: i32,
1687 ) -> Option<(WlSurface, f64, f64)> {
1688 let surf = self.surfaces.get(surface_id)?;
1689 let sx = offset_x + surf.subsurface_position.0;
1690 let sy = offset_y + surf.subsurface_position.1;
1691
1692 for child_id in surf.children.iter().rev() {
1694 if let Some(hit) = self.hit_test_recursive(child_id, x, y, sx, sy) {
1695 return Some(hit);
1696 }
1697 }
1698
1699 if let Some(sm) = self.surface_meta.get(surface_id) {
1701 let s = sm.scale.max(1) as f64;
1702 let (w, h) = (sm.width, sm.height);
1703 let (lw, lh) = surf
1706 .viewport_destination
1707 .filter(|&(dw, dh)| dw > 0 && dh > 0)
1708 .map(|(dw, dh)| (dw as f64, dh as f64))
1709 .unwrap_or((w as f64 / s, h as f64 / s));
1710 let lx = x - sx as f64;
1711 let ly = y - sy as f64;
1712 if lx >= 0.0 && ly >= 0.0 && lx < lw && ly < lh {
1713 return Some((surf.wl_surface.clone(), lx, ly));
1714 }
1715 }
1716 None
1717 }
1718
1719 fn apply_pending_state(&mut self, surface_id: &ObjectId) {
1732 let (buffer, scale, is_cursor) = {
1733 let Some(surf) = self.surfaces.get_mut(surface_id) else {
1734 return;
1735 };
1736 let buffer = surf.pending_buffer.take();
1737 let scale = surf.pending_buffer_scale;
1738 surf.buffer_scale = scale;
1739 surf.viewport_destination = surf.pending_viewport_destination;
1740 surf.is_opaque = surf.pending_opaque;
1741 surf.pending_damage = false;
1742 if let Some(pos) = surf.pending_subsurface_position.take() {
1743 surf.subsurface_position = pos;
1744 }
1745 (buffer, scale, surf.is_cursor)
1746 };
1747 let Some(buf) = buffer else { return };
1748
1749 if let Some(old) = self.held_buffers.remove(surface_id) {
1752 old.release();
1753 }
1754
1755 if !is_cursor && let Some(shm) = buf.data::<ShmBufferData>() {
1761 let w = shm.width as u32;
1762 let h = shm.height as u32;
1763 let stride = shm.stride as usize;
1764 let offset = shm.offset as usize;
1765 let format = shm.format;
1766 if w > 0
1767 && h > 0
1768 && let Some(ref mut vk) = self.vulkan_renderer
1769 {
1770 let swap_rb =
1771 !matches!(format, wl_shm::Format::Abgr8888 | wl_shm::Format::Xbgr8888);
1772 let force_opaque =
1773 matches!(format, wl_shm::Format::Xrgb8888 | wl_shm::Format::Xbgr8888);
1774 let row_bytes = w as usize * 4;
1775 let uploaded = shm
1776 .pool
1777 .with_mmap(|slice| {
1778 if offset + stride * (h as usize - 1) + row_bytes > slice.len() {
1779 return false;
1780 }
1781 vk.upload_surface_shm_mmap(
1782 surface_id,
1783 slice,
1784 offset,
1785 stride,
1786 w,
1787 h,
1788 swap_rb,
1789 force_opaque,
1790 )
1791 })
1792 .unwrap_or(false);
1793 if uploaded {
1794 self.surface_meta.insert(
1795 surface_id.clone(),
1796 super::render::SurfaceMeta {
1797 width: w,
1798 height: h,
1799 scale,
1800 y_invert: false,
1801 },
1802 );
1803 buf.release();
1804 return;
1805 }
1806 }
1807 }
1808
1809 if let Some((w, h, pixels)) = self.read_buffer(&buf) {
1810 let y_invert = matches!(pixels, PixelData::DmaBuf { y_invert: true, .. });
1811
1812 if let Some(ref mut vk) = self.vulkan_renderer {
1814 vk.upload_surface(surface_id, &pixels, w, h);
1815 }
1816
1817 self.surface_meta.insert(
1819 surface_id.clone(),
1820 super::render::SurfaceMeta {
1821 width: w,
1822 height: h,
1823 scale,
1824 y_invert,
1825 },
1826 );
1827
1828 if is_cursor {
1831 let rgba = pixels.to_rgba(w, h);
1832 if !rgba.is_empty() {
1833 self.cursor_rgba.insert(surface_id.clone(), (w, h, rgba));
1834 }
1835 }
1836
1837 if pixels.is_dmabuf() {
1838 self.held_buffers.insert(surface_id.clone(), buf);
1841 } else {
1842 buf.release();
1845 }
1846 } else {
1847 buf.release();
1848 }
1849 }
1850
1851 fn fire_surface_frame_callbacks(&mut self, surface_id: &ObjectId) {
1852 let (callbacks, feedbacks) = {
1853 let Some(surf) = self.surfaces.get_mut(surface_id) else {
1854 return;
1855 };
1856 (
1857 std::mem::take(&mut surf.pending_frame_callbacks),
1858 std::mem::take(&mut surf.pending_presentation_feedbacks),
1859 )
1860 };
1861 let time = elapsed_ms();
1862 for cb in callbacks {
1863 cb.done(time);
1864 }
1865 if !feedbacks.is_empty() {
1866 let (sec, nsec) = monotonic_timespec();
1867 for fb in feedbacks {
1870 for output in &self.outputs {
1871 if same_client(&fb, output) {
1872 fb.sync_output(output);
1873 }
1874 }
1875 let refresh_ns = if self.output_refresh_mhz > 0 {
1877 (1_000_000_000_000u64 / self.output_refresh_mhz as u64) as u32
1878 } else {
1879 0
1880 };
1881 fb.presented(
1882 (sec >> 32) as u32,
1883 sec as u32,
1884 nsec as u32,
1885 refresh_ns,
1886 0, 0, WpPresentationFeedbackKind::empty(),
1889 );
1890 }
1891 }
1892 }
1893
1894 fn cleanup_dead_surfaces(&mut self) {
1899 self.fractional_scales.retain(|fs| fs.is_alive());
1901 self.outputs.retain(|o| o.is_alive());
1902 self.keyboards.retain(|k| k.is_alive());
1903 self.pointers.retain(|p| p.is_alive());
1904 self.data_devices.retain(|d| d.is_alive());
1905 self.primary_devices.retain(|d| d.is_alive());
1906 self.relative_pointers.retain(|p| p.is_alive());
1907 self.text_inputs.retain(|ti| ti.resource.is_alive());
1908 self.shm_pools.retain(|_, p| p.resource.is_alive());
1909 self.dmabuf_params.retain(|_, p| p.resource.is_alive());
1910 self.positioners.retain(|_, p| p.resource.is_alive());
1911
1912 let dead: Vec<ObjectId> = self
1913 .surfaces
1914 .iter()
1915 .filter(|(_, surf)| !surf.wl_surface.is_alive())
1916 .map(|(id, _)| id.clone())
1917 .collect();
1918
1919 for proto_id in &dead {
1920 self.surface_meta.remove(proto_id);
1921 if let Some(ref mut vk) = self.vulkan_renderer {
1922 vk.remove_surface(proto_id);
1923 }
1924 if let Some(held) = self.held_buffers.remove(proto_id) {
1925 held.release();
1926 }
1927 if let Some(surf) = self.surfaces.remove(proto_id) {
1928 for fb in surf.pending_presentation_feedbacks {
1931 fb.discarded();
1932 }
1933 if let Some(ref parent_id) = surf.parent_surface_id
1934 && let Some(parent) = self.surfaces.get_mut(parent_id)
1935 {
1936 parent.children.retain(|c| c != proto_id);
1937 }
1938 if surf.surface_id > 0 {
1939 self.toplevel_surface_ids.remove(&surf.surface_id);
1940 self.last_reported_size.remove(&surf.surface_id);
1941 self.surface_sizes.remove(&surf.surface_id);
1942 let _ = self.event_tx.send(CompositorEvent::SurfaceDestroyed {
1943 surface_id: surf.surface_id,
1944 });
1945 (self.event_notify)();
1946 }
1947 }
1948 }
1949 }
1950
1951 fn fire_frame_callbacks_for_toplevel(&mut self, toplevel_sid: u16) {
1952 let Some(root_id) = self.toplevel_surface_ids.get(&toplevel_sid).cloned() else {
1953 return;
1954 };
1955 let tree = self.collect_surface_tree(&root_id);
1956 for sid in &tree {
1957 self.fire_surface_frame_callbacks(sid);
1958 }
1959 let _ = self.display_handle.flush_clients();
1960 }
1961
1962 fn handle_cursor_commit(&mut self, surface_id: &ObjectId) {
1963 self.apply_pending_state(surface_id);
1964 let hotspot = self
1965 .surfaces
1966 .get(surface_id)
1967 .map(|s| s.cursor_hotspot)
1968 .unwrap_or((0, 0));
1969 if let Some((w, h, rgba)) = self.cursor_rgba.get(surface_id)
1970 && !rgba.is_empty()
1971 {
1972 let _ = self.event_tx.send(CompositorEvent::SurfaceCursor {
1973 surface_id: self.focused_surface_id,
1974 cursor: CursorImage::Custom {
1975 hotspot_x: hotspot.0 as u16,
1976 hotspot_y: hotspot.1 as u16,
1977 width: *w as u16,
1978 height: *h as u16,
1979 rgba: rgba.clone(),
1980 },
1981 });
1982 }
1983 self.fire_surface_frame_callbacks(surface_id);
1984 let _ = self.display_handle.flush_clients();
1985 }
1986
1987 fn handle_command(&mut self, cmd: CompositorCommand) {
1988 match cmd {
1989 CompositorCommand::KeyInput {
1990 surface_id: _,
1991 keycode,
1992 pressed,
1993 } => {
1994 let serial = self.next_serial();
1995 let time = elapsed_ms();
1996 let state = if pressed {
1997 wl_keyboard::KeyState::Pressed
1998 } else {
1999 wl_keyboard::KeyState::Released
2000 };
2001 let focused_wl = self
2002 .toplevel_surface_ids
2003 .get(&self.focused_surface_id)
2004 .and_then(|root_id| self.surfaces.get(root_id))
2005 .map(|s| s.wl_surface.clone());
2006 for kb in &self.keyboards {
2007 if let Some(ref wl) = focused_wl
2008 && same_client(kb, wl)
2009 {
2010 kb.key(serial, time, keycode, state);
2011 }
2012 }
2013 self.update_and_send_modifiers(keycode, pressed);
2018 let _ = self.display_handle.flush_clients();
2019 }
2020 CompositorCommand::TextInput { text } => {
2021 let focused_wl = self
2022 .toplevel_surface_ids
2023 .get(&self.focused_surface_id)
2024 .and_then(|root_id| self.surfaces.get(root_id))
2025 .map(|s| s.wl_surface.clone());
2026 let Some(focused_wl) = focused_wl else { return };
2027
2028 const KEY_LEFTSHIFT: u32 = 42;
2044 let saved_mods_depressed = self.mods_depressed;
2045 for ch in text.chars() {
2046 if let Some((kc, need_shift)) = char_to_keycode(ch) {
2047 let time = elapsed_ms();
2048 if need_shift {
2049 let serial = self.next_serial();
2050 for kb in &self.keyboards {
2051 if same_client(kb, &focused_wl) {
2052 kb.key(
2053 serial,
2054 time,
2055 KEY_LEFTSHIFT,
2056 wl_keyboard::KeyState::Pressed,
2057 );
2058 }
2059 }
2060 self.update_and_send_modifiers(KEY_LEFTSHIFT, true);
2061 }
2062 let serial = self.next_serial();
2063 for kb in &self.keyboards {
2064 if same_client(kb, &focused_wl) {
2065 kb.key(serial, time, kc, wl_keyboard::KeyState::Pressed);
2066 }
2067 }
2068 let serial = self.next_serial();
2069 for kb in &self.keyboards {
2070 if same_client(kb, &focused_wl) {
2071 kb.key(serial, time, kc, wl_keyboard::KeyState::Released);
2072 }
2073 }
2074 if need_shift {
2075 let serial = self.next_serial();
2076 for kb in &self.keyboards {
2077 if same_client(kb, &focused_wl) {
2078 kb.key(
2079 serial,
2080 time,
2081 KEY_LEFTSHIFT,
2082 wl_keyboard::KeyState::Released,
2083 );
2084 }
2085 }
2086 self.update_and_send_modifiers(KEY_LEFTSHIFT, false);
2087 }
2088 }
2089 }
2092 if self.mods_depressed != saved_mods_depressed {
2096 self.mods_depressed = saved_mods_depressed;
2097 let serial = self.next_serial();
2098 for kb in &self.keyboards {
2099 if same_client(kb, &focused_wl) {
2100 kb.modifiers(serial, self.mods_depressed, 0, self.mods_locked, 0);
2101 }
2102 }
2103 }
2104 let _ = self.display_handle.flush_clients();
2105 }
2106 CompositorCommand::PointerMotion { surface_id, x, y } => {
2107 let time = elapsed_ms();
2108 let (mut x, mut y) =
2113 if let Some(&(cw, ch, lw, lh)) = self.last_reported_size.get(&surface_id) {
2114 let sx = if cw > 0 { lw as f64 / cw as f64 } else { 1.0 };
2115 let sy = if ch > 0 { lh as f64 / ch as f64 } else { 1.0 };
2116 (x * sx, y * sy)
2117 } else {
2118 (x, y)
2119 };
2120 if let Some((gx, gy, _, _)) = self
2124 .toplevel_surface_ids
2125 .get(&surface_id)
2126 .and_then(|rid| self.surfaces.get(rid))
2127 .and_then(|s| s.xdg_geometry)
2128 {
2129 x += gx as f64;
2130 y += gy as f64;
2131 }
2132 let target_wl = self
2135 .toplevel_surface_ids
2136 .get(&surface_id)
2137 .and_then(|root_id| self.hit_test_surface_at(root_id, x, y))
2138 .map(|(wl_surface, lx, ly)| (wl_surface.id(), wl_surface, lx, ly));
2139
2140 static PTR_DBG: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
2141 let pn = PTR_DBG.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2142 if pn < 5 || pn.is_multiple_of(500) {
2143 let root = self.toplevel_surface_ids.get(&surface_id).cloned();
2144 let lrs = self.last_reported_size.get(&surface_id).copied();
2145 eprintln!(
2146 "[pointer #{pn}] sid={surface_id} logical=({x:.1},{y:.1}) lrs={lrs:?} root={root:?} hit={:?}",
2147 target_wl.as_ref().map(|(pid, _, lx, ly)| format!(
2148 "proto={pid:?} local=({lx:.1},{ly:.1})"
2149 ))
2150 );
2151 }
2152 if let Some((proto_id, wl_surface, lx, ly)) = target_wl {
2153 if self.pointer_entered_id.as_ref() != Some(&proto_id) {
2154 let serial = self.next_serial();
2155 let matching_ptrs = self
2156 .pointers
2157 .iter()
2158 .filter(|p| same_client(*p, &wl_surface))
2159 .count();
2160 eprintln!(
2161 "[pointer-enter] proto={proto_id:?} matching_ptrs={matching_ptrs} total_ptrs={}",
2162 self.pointers.len()
2163 );
2164 if self.pointer_entered_id.is_some() {
2166 let old_wl = self
2167 .surfaces
2168 .values()
2169 .find(|s| Some(s.wl_surface.id()) == self.pointer_entered_id)
2170 .map(|s| s.wl_surface.clone());
2171 if let Some(old_wl) = old_wl {
2172 for ptr in &self.pointers {
2173 if same_client(ptr, &old_wl) {
2174 ptr.leave(serial, &old_wl);
2175 ptr.frame();
2176 }
2177 }
2178 }
2179 }
2180 for ptr in &self.pointers {
2181 if same_client(ptr, &wl_surface) {
2182 ptr.enter(serial, &wl_surface, lx, ly);
2183 }
2184 }
2185 self.pointer_entered_id = Some(proto_id);
2186 }
2187 for ptr in &self.pointers {
2188 if same_client(ptr, &wl_surface) {
2189 ptr.motion(time, lx, ly);
2190 ptr.frame();
2191 }
2192 }
2193 }
2194 let _ = self.display_handle.flush_clients();
2197 }
2198 CompositorCommand::PointerButton {
2199 surface_id: _,
2200 button,
2201 pressed,
2202 } => {
2203 let serial = self.next_serial();
2204 let time = elapsed_ms();
2205 let state = if pressed {
2206 wl_pointer::ButtonState::Pressed
2207 } else {
2208 wl_pointer::ButtonState::Released
2209 };
2210
2211 if pressed && !self.popup_grab_stack.is_empty() {
2214 let click_on_grabbed = self.pointer_entered_id.as_ref().is_some_and(|eid| {
2215 self.popup_grab_stack.iter().any(|gid| {
2216 self.surfaces
2217 .get(gid)
2218 .is_some_and(|s| s.wl_surface.id() == *eid)
2219 })
2220 });
2221 if !click_on_grabbed {
2222 while let Some(grab_wl_id) = self.popup_grab_stack.pop() {
2224 if let Some(surf) = self.surfaces.get(&grab_wl_id)
2225 && let Some(ref popup) = surf.xdg_popup
2226 {
2227 popup.popup_done();
2228 }
2229 }
2230 let _ = self.display_handle.flush_clients();
2231 }
2232 }
2233
2234 let focused_wl = self
2235 .surfaces
2236 .values()
2237 .find(|s| Some(s.wl_surface.id()) == self.pointer_entered_id)
2238 .map(|s| s.wl_surface.clone());
2239 for ptr in &self.pointers {
2240 if let Some(ref wl) = focused_wl
2241 && same_client(ptr, wl)
2242 {
2243 ptr.button(serial, time, button, state);
2244 ptr.frame();
2245 }
2246 }
2247 let _ = self.display_handle.flush_clients();
2248 }
2249 CompositorCommand::PointerAxis {
2250 surface_id: _,
2251 axis,
2252 value,
2253 } => {
2254 let time = elapsed_ms();
2255 let wl_axis = if axis == 0 {
2256 wl_pointer::Axis::VerticalScroll
2257 } else {
2258 wl_pointer::Axis::HorizontalScroll
2259 };
2260 let focused_wl = self
2261 .surfaces
2262 .values()
2263 .find(|s| Some(s.wl_surface.id()) == self.pointer_entered_id)
2264 .map(|s| s.wl_surface.clone());
2265 for ptr in &self.pointers {
2266 if let Some(ref wl) = focused_wl
2267 && same_client(ptr, wl)
2268 {
2269 ptr.axis(time, wl_axis, value);
2270 ptr.frame();
2271 }
2272 }
2273 let _ = self.display_handle.flush_clients();
2274 }
2275 CompositorCommand::SurfaceResize {
2276 surface_id,
2277 width,
2278 height,
2279 scale_120,
2280 } => {
2281 let s_in = (scale_120 as i32).max(120);
2284 let w = (width as i32) * 120 / s_in;
2285 let h = (height as i32) * 120 / s_in;
2286 self.surface_sizes.insert(surface_id, (w, h));
2287
2288 let mut output_changed = false;
2291
2292 if scale_120 > 0 && scale_120 != self.output_scale_120 {
2294 self.output_scale_120 = scale_120;
2295 output_changed = true;
2296 }
2297
2298 let s120 = self.output_scale_120 as i32;
2299
2300 let (max_w, max_h) = self
2305 .surface_sizes
2306 .values()
2307 .fold((0i32, 0i32), |(mw, mh), &(sw, sh)| (mw.max(sw), mh.max(sh)));
2308 let max_w = max_w.max(1);
2310 let max_h = max_h.max(1);
2311 if max_w != self.output_width || max_h != self.output_height {
2312 self.output_width = max_w;
2313 self.output_height = max_h;
2314 output_changed = true;
2315 }
2316
2317 if output_changed {
2321 let int_scale = ((s120) + 119) / 120;
2322 for output in &self.outputs {
2323 output.geometry(
2324 0,
2325 0,
2326 0,
2327 0,
2328 wl_output::Subpixel::None,
2329 "blit".to_string(),
2330 "virtual".to_string(),
2331 wl_output::Transform::Normal,
2332 );
2333 let mode_w = self.output_width * s120 / 120;
2335 let mode_h = self.output_height * s120 / 120;
2336 output.mode(
2337 wl_output::Mode::Current | wl_output::Mode::Preferred,
2338 mode_w,
2339 mode_h,
2340 self.output_refresh_mhz as i32,
2341 );
2342 if output.version() >= 2 {
2343 output.scale(int_scale);
2344 }
2345 }
2346 for fs in &self.fractional_scales {
2347 fs.preferred_scale(s120 as u32);
2348 }
2349 }
2350
2351 if output_changed {
2354 for output in &self.outputs {
2355 if output.version() >= 2 {
2356 output.done();
2357 }
2358 }
2359 }
2360
2361 let states = xdg_toplevel_states(&[
2362 xdg_toplevel::State::Activated,
2363 xdg_toplevel::State::Maximized,
2364 ]);
2365
2366 if output_changed {
2367 for (&sid, root_id) in &self.toplevel_surface_ids {
2371 let (lw, lh) = self.surface_sizes.get(&sid).copied().unwrap_or((w, h));
2372 if let Some(surf) = self.surfaces.get(root_id) {
2373 if let Some(ref tl) = surf.xdg_toplevel {
2374 tl.configure(lw, lh, states.clone());
2375 }
2376 if let Some(ref xs) = surf.xdg_surface {
2377 let serial = self.serial.wrapping_add(1);
2378 self.serial = serial;
2379 xs.configure(serial);
2380 }
2381 }
2382 }
2383 let all_sids: Vec<u16> = self.toplevel_surface_ids.keys().copied().collect();
2386 for sid in all_sids {
2387 self.fire_frame_callbacks_for_toplevel(sid);
2388 }
2389
2390 self.pointer_entered_id = None;
2393 self.pending_kb_reenter = true;
2394 } else {
2395 if let Some(root_id) = self.toplevel_surface_ids.get(&surface_id)
2400 && let Some(surf) = self.surfaces.get(root_id)
2401 {
2402 if let Some(ref tl) = surf.xdg_toplevel {
2403 tl.configure(w, h, states);
2404 }
2405 if let Some(ref xs) = surf.xdg_surface {
2406 let serial = self.serial.wrapping_add(1);
2407 self.serial = serial;
2408 xs.configure(serial);
2409 }
2410 }
2411 self.fire_frame_callbacks_for_toplevel(surface_id);
2412 }
2413 let _ = self.display_handle.flush_clients();
2414 }
2415 CompositorCommand::SurfaceFocus { surface_id } => {
2416 self.set_keyboard_focus(surface_id);
2417 let _ = self.display_handle.flush_clients();
2418 }
2419 CompositorCommand::SurfaceClose { surface_id } => {
2420 if let Some(root_id) = self.toplevel_surface_ids.get(&surface_id)
2421 && let Some(surf) = self.surfaces.get(root_id)
2422 && let Some(ref tl) = surf.xdg_toplevel
2423 {
2424 tl.close();
2425 }
2426 let _ = self.display_handle.flush_clients();
2427 }
2428 CompositorCommand::ClipboardOffer { mime_type, data } => {
2429 self.external_clipboard = Some(ExternalClipboard { mime_type, data });
2430 if let Some(src) = self.selection_source.take() {
2436 src.cancelled();
2437 }
2438 self.offer_external_clipboard();
2439 }
2440 CompositorCommand::Capture {
2441 surface_id,
2442 scale_120,
2443 reply,
2444 } => {
2445 let cap_s120 = if scale_120 > 0 {
2448 scale_120
2449 } else {
2450 self.output_scale_120
2451 };
2452 let result = if let Some(root_id) = self.toplevel_surface_ids.get(&surface_id) {
2453 if let Some(ref mut vk) = self.vulkan_renderer {
2454 vk.render_tree_sized(
2455 root_id,
2456 &self.surfaces,
2457 &self.surface_meta,
2458 cap_s120,
2459 None,
2460 surface_id,
2461 )
2462 .map(|(_sid, w, h, pixels)| {
2463 let rgba = pixels.to_rgba(w, h);
2464 (w, h, rgba)
2465 })
2466 } else {
2467 None
2468 }
2469 } else {
2470 None
2471 };
2472 let _ = reply.send(result);
2473 }
2474 CompositorCommand::RequestFrame { surface_id } => {
2475 self.fire_frame_callbacks_for_toplevel(surface_id);
2476 }
2477 CompositorCommand::ReleaseKeys { keycodes } => {
2478 let time = elapsed_ms();
2479 let focused_wl = self
2480 .toplevel_surface_ids
2481 .get(&self.focused_surface_id)
2482 .and_then(|root_id| self.surfaces.get(root_id))
2483 .map(|s| s.wl_surface.clone());
2484 for keycode in &keycodes {
2485 let serial = self.next_serial();
2486 for kb in &self.keyboards {
2487 if let Some(ref wl) = focused_wl
2488 && same_client(kb, wl)
2489 {
2490 kb.key(serial, time, *keycode, wl_keyboard::KeyState::Released);
2491 }
2492 }
2493 }
2494 for keycode in &keycodes {
2496 self.update_and_send_modifiers(*keycode, false);
2497 }
2498 let _ = self.display_handle.flush_clients();
2499 }
2500 CompositorCommand::ClipboardListMimes { reply } => {
2501 let mimes = self.collect_clipboard_mime_types();
2502 let _ = reply.send(mimes);
2503 }
2504 CompositorCommand::ClipboardGet { mime_type, reply } => {
2505 let data = self.get_clipboard_content(&mime_type);
2506 let _ = reply.send(data);
2507 }
2508 CompositorCommand::SetExternalOutputBuffers {
2509 surface_id,
2510 buffers,
2511 } => {
2512 if let Some(ref mut vk) = self.vulkan_renderer {
2513 vk.set_external_output_buffers(surface_id, buffers);
2514 }
2515 }
2516 CompositorCommand::SetRefreshRate { mhz } => {
2517 let diff = (mhz as i64 - self.output_refresh_mhz as i64).unsigned_abs();
2521 if diff > 2000 && mhz > 0 {
2522 self.output_refresh_mhz = mhz;
2523 let s120 = self.output_scale_120 as i32;
2524 let mode_w = self.output_width * s120 / 120;
2525 let mode_h = self.output_height * s120 / 120;
2526 for output in &self.outputs {
2527 output.mode(
2528 wl_output::Mode::Current | wl_output::Mode::Preferred,
2529 mode_w,
2530 mode_h,
2531 mhz as i32,
2532 );
2533 if output.version() >= 2 {
2534 output.done();
2535 }
2536 }
2537 let _ = self.display_handle.flush_clients();
2538 }
2539 }
2540 CompositorCommand::SetVulkanEncoder {
2541 surface_id,
2542 codec,
2543 qp,
2544 width,
2545 height,
2546 } => {
2547 if let Some(ref mut vk) = self.vulkan_renderer {
2548 vk.create_vulkan_encoder(surface_id, codec, qp, width, height);
2549 }
2550 }
2551 CompositorCommand::RequestVulkanKeyframe { surface_id } => {
2552 if let Some(ref mut vk) = self.vulkan_renderer {
2553 vk.request_encoder_keyframe(surface_id);
2554 }
2555 }
2556 CompositorCommand::DestroyVulkanEncoder { surface_id } => {
2557 if let Some(ref mut vk) = self.vulkan_renderer {
2558 vk.destroy_vulkan_encoder(surface_id);
2559 }
2560 }
2561 CompositorCommand::Shutdown => {
2562 self.shutdown.store(true, Ordering::Relaxed);
2563 self.loop_signal.stop();
2564 }
2565 }
2566 }
2567
2568 fn send_dmabuf_feedback(&self, fb: &ZwpLinuxDmabufFeedbackV1) {
2572 use std::os::unix::fs::MetadataExt;
2573
2574 let modifiers: &[(u32, u64)] = self
2576 .vulkan_renderer
2577 .as_ref()
2578 .map(|vk| vk.supported_dmabuf_modifiers.as_slice())
2579 .unwrap_or(&[]);
2580
2581 let entry_size = 16usize;
2583 let table_size = modifiers.len() * entry_size;
2584 let mut table_data = vec![0u8; table_size];
2585 for (i, &(fmt, modifier)) in modifiers.iter().enumerate() {
2586 let off = i * entry_size;
2587 table_data[off..off + 4].copy_from_slice(&fmt.to_ne_bytes());
2588 table_data[off + 8..off + 16].copy_from_slice(&modifier.to_ne_bytes());
2590 }
2591
2592 let name = c"dmabuf-feedback-table";
2594 let raw_fd = unsafe { libc::memfd_create(name.as_ptr(), libc::MFD_CLOEXEC) };
2595 if raw_fd < 0 {
2596 eprintln!("[compositor] memfd_create for dmabuf feedback failed");
2597 fb.done();
2598 return;
2599 }
2600 let table_fd = unsafe { OwnedFd::from_raw_fd(raw_fd) };
2601 if !table_data.is_empty() {
2602 use std::io::Write;
2603 let mut file = std::fs::File::from(table_fd.try_clone().unwrap());
2604 if file.write_all(&table_data).is_err() {
2605 eprintln!("[compositor] failed to write dmabuf feedback table");
2606 fb.done();
2607 return;
2608 }
2609 }
2610 fb.format_table(table_fd.as_fd(), table_size as u32);
2611
2612 let dev = std::fs::metadata(&self.gpu_device)
2614 .map(|m| m.rdev())
2615 .unwrap_or(0);
2616 let dev_bytes = dev.to_ne_bytes().to_vec();
2617 fb.main_device(dev_bytes.clone());
2618
2619 fb.tranche_target_device(dev_bytes);
2621
2622 let indices: Vec<u8> = (0..modifiers.len() as u16)
2624 .flat_map(|i| i.to_ne_bytes())
2625 .collect();
2626 fb.tranche_formats(indices);
2627
2628 fb.tranche_flags(
2629 wayland_protocols::wp::linux_dmabuf::zv1::server::zwp_linux_dmabuf_feedback_v1::TrancheFlags::empty(),
2630 );
2631 fb.tranche_done();
2632 fb.done();
2633 }
2634}
2635
2636impl Compositor {
2637 fn collect_clipboard_mime_types(&self) -> Vec<String> {
2639 if let Some(ref src) = self.selection_source {
2641 let data = src.data::<DataSourceData>().unwrap();
2642 return data.mime_types.lock().unwrap().clone();
2643 }
2644 if let Some(ref cb) = self.external_clipboard
2646 && !cb.mime_type.is_empty()
2647 {
2648 let mut mimes = vec![cb.mime_type.clone()];
2649 if cb.mime_type.starts_with("text/plain") {
2651 if cb.mime_type != "text/plain" {
2652 mimes.push("text/plain".to_string());
2653 }
2654 if cb.mime_type != "text/plain;charset=utf-8" {
2655 mimes.push("text/plain;charset=utf-8".to_string());
2656 }
2657 mimes.push("UTF8_STRING".to_string());
2658 }
2659 return mimes;
2660 }
2661 Vec::new()
2662 }
2663
2664 fn get_clipboard_content(&mut self, mime_type: &str) -> Option<Vec<u8>> {
2666 if let Some(ref cb) = self.external_clipboard
2668 && self.selection_source.is_none()
2669 {
2670 let matches = cb.mime_type == mime_type
2672 || (cb.mime_type.starts_with("text/plain")
2673 && (mime_type == "text/plain"
2674 || mime_type == "text/plain;charset=utf-8"
2675 || mime_type == "UTF8_STRING"));
2676 if matches {
2677 return Some(cb.data.clone());
2678 }
2679 return None;
2680 }
2681 if let Some(src) = self.selection_source.clone() {
2683 return self.read_data_source_sync(&src, mime_type);
2684 }
2685 None
2686 }
2687
2688 fn read_data_source_sync(&mut self, source: &WlDataSource, mime_type: &str) -> Option<Vec<u8>> {
2690 let mut fds = [0i32; 2];
2691 if unsafe { libc::pipe(fds.as_mut_ptr()) } != 0 {
2692 return None;
2693 }
2694 let read_fd = unsafe { OwnedFd::from_raw_fd(fds[0]) };
2695 let write_fd = unsafe { OwnedFd::from_raw_fd(fds[1]) };
2696 source.send(mime_type.to_string(), write_fd.as_fd());
2697 let _ = self.display_handle.flush_clients();
2698 drop(write_fd); unsafe {
2701 libc::fcntl(read_fd.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
2702 }
2703 std::thread::sleep(std::time::Duration::from_millis(5));
2704 let mut buf = Vec::new();
2705 let mut tmp = [0u8; 8192];
2706 loop {
2707 let n = unsafe {
2708 libc::read(
2709 read_fd.as_raw_fd(),
2710 tmp.as_mut_ptr() as *mut libc::c_void,
2711 tmp.len(),
2712 )
2713 };
2714 if n <= 0 {
2715 break;
2716 }
2717 buf.extend_from_slice(&tmp[..n as usize]);
2718 if buf.len() > 1024 * 1024 {
2719 break; }
2721 }
2722 if buf.is_empty() { None } else { Some(buf) }
2723 }
2724}
2725
2726fn monotonic_timespec() -> (i64, i64) {
2732 let mut ts = libc::timespec {
2733 tv_sec: 0,
2734 tv_nsec: 0,
2735 };
2736 unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts) };
2738 (ts.tv_sec, ts.tv_nsec)
2739}
2740
2741fn elapsed_ms() -> u32 {
2742 let (sec, nsec) = monotonic_timespec();
2747 (sec as u32)
2748 .wrapping_mul(1000)
2749 .wrapping_add(nsec as u32 / 1_000_000)
2750}
2751
2752fn same_client<R1: Resource, R2: Resource>(a: &R1, b: &R2) -> bool {
2754 match (a.client(), b.client()) {
2755 (Some(ca), Some(cb)) => ca.id() == cb.id(),
2756 _ => false,
2757 }
2758}
2759
2760fn yuv420_to_rgb(y: u8, u: u8, v: u8) -> [u8; 3] {
2761 let y = (y as i32 - 16).max(0);
2762 let u = u as i32 - 128;
2763 let v = v as i32 - 128;
2764 let r = ((298 * y + 409 * v + 128) >> 8).clamp(0, 255) as u8;
2765 let g = ((298 * y - 100 * u - 208 * v + 128) >> 8).clamp(0, 255) as u8;
2766 let b = ((298 * y + 516 * u + 128) >> 8).clamp(0, 255) as u8;
2767 [r, g, b]
2768}
2769
2770fn xdg_toplevel_states(states: &[xdg_toplevel::State]) -> Vec<u8> {
2772 let mut bytes = Vec::with_capacity(states.len() * 4);
2773 for state in states {
2774 bytes.extend_from_slice(&(*state as u32).to_ne_bytes());
2775 }
2776 bytes
2777}
2778
2779fn create_keymap_fd(keymap_data: &[u8]) -> Option<OwnedFd> {
2780 use std::io::Write;
2781 let name = c"blit-keymap";
2782 let raw_fd = unsafe { libc::memfd_create(name.as_ptr(), libc::MFD_CLOEXEC) };
2783 if raw_fd < 0 {
2784 return None;
2785 }
2786 let fd = unsafe { OwnedFd::from_raw_fd(raw_fd) };
2787 let mut file = std::fs::File::from(fd);
2788 file.write_all(keymap_data).ok()?;
2789 Some(file.into())
2790}
2791
2792impl GlobalDispatch<WlCompositor, ()> for Compositor {
2799 fn bind(
2800 _state: &mut Self,
2801 _handle: &DisplayHandle,
2802 _client: &Client,
2803 resource: New<WlCompositor>,
2804 _data: &(),
2805 data_init: &mut DataInit<'_, Self>,
2806 ) {
2807 data_init.init(resource, ());
2808 }
2809}
2810
2811impl Dispatch<WlCompositor, ()> for Compositor {
2812 fn request(
2813 state: &mut Self,
2814 _client: &Client,
2815 _resource: &WlCompositor,
2816 request: <WlCompositor as Resource>::Request,
2817 _data: &(),
2818 _dh: &DisplayHandle,
2819 data_init: &mut DataInit<'_, Self>,
2820 ) {
2821 use wayland_server::protocol::wl_compositor::Request;
2822 match request {
2823 Request::CreateSurface { id } => {
2824 let surface = data_init.init(id, ());
2825 let proto_id = surface.id();
2826 state.surfaces.insert(
2827 proto_id,
2828 Surface {
2829 surface_id: 0,
2830 wl_surface: surface,
2831 pending_buffer: None,
2832 pending_buffer_scale: 1,
2833 pending_damage: false,
2834 pending_frame_callbacks: Vec::new(),
2835 pending_presentation_feedbacks: Vec::new(),
2836 pending_opaque: false,
2837 buffer_scale: 1,
2838 is_opaque: false,
2839 parent_surface_id: None,
2840 pending_subsurface_position: None,
2841 subsurface_position: (0, 0),
2842 children: Vec::new(),
2843 xdg_surface: None,
2844 xdg_toplevel: None,
2845 xdg_popup: None,
2846 xdg_geometry: None,
2847 title: String::new(),
2848 app_id: String::new(),
2849 pending_viewport_destination: None,
2850 viewport_destination: None,
2851 is_cursor: false,
2852 cursor_hotspot: (0, 0),
2853 },
2854 );
2855 }
2856 Request::CreateRegion { id } => {
2857 data_init.init(id, ());
2858 }
2859 _ => {}
2860 }
2861 }
2862}
2863
2864impl Dispatch<WlSurface, ()> for Compositor {
2867 fn request(
2868 state: &mut Self,
2869 _client: &Client,
2870 resource: &WlSurface,
2871 request: <WlSurface as Resource>::Request,
2872 _data: &(),
2873 _dh: &DisplayHandle,
2874 data_init: &mut DataInit<'_, Self>,
2875 ) {
2876 use wayland_server::protocol::wl_surface::Request;
2877 let sid = resource.id();
2878 match request {
2879 Request::Attach { buffer, x: _, y: _ } => {
2880 if let Some(surf) = state.surfaces.get_mut(&sid) {
2881 surf.pending_buffer = buffer;
2882 }
2883 }
2884 Request::Damage { .. } | Request::DamageBuffer { .. } => {
2885 if let Some(surf) = state.surfaces.get_mut(&sid) {
2886 surf.pending_damage = true;
2887 }
2888 }
2889 Request::Frame { callback } => {
2890 let cb = data_init.init(callback, ());
2891 if let Some(surf) = state.surfaces.get_mut(&sid) {
2892 surf.pending_frame_callbacks.push(cb);
2893 }
2894 }
2895 Request::SetBufferScale { scale } => {
2896 if let Some(surf) = state.surfaces.get_mut(&sid) {
2897 surf.pending_buffer_scale = scale;
2898 }
2899 }
2900 Request::SetOpaqueRegion { region: _ } => {
2901 if let Some(surf) = state.surfaces.get_mut(&sid) {
2902 surf.pending_opaque = true;
2903 }
2904 }
2905 Request::SetInputRegion { .. } => {}
2906 Request::Commit => {
2907 let is_cursor = state.surfaces.get(&sid).is_some_and(|s| s.is_cursor);
2908 if is_cursor {
2909 state.handle_cursor_commit(&sid);
2910 } else {
2911 state.handle_surface_commit(&sid);
2912 }
2913 }
2914 Request::SetBufferTransform { .. } => {}
2915 Request::Offset { .. } => {}
2916 Request::Destroy => {
2917 state.surface_meta.remove(&sid);
2918 state.cursor_rgba.remove(&sid);
2919 if let Some(ref mut vk) = state.vulkan_renderer {
2920 vk.remove_surface(&sid);
2921 }
2922 if let Some(held) = state.held_buffers.remove(&sid) {
2923 held.release();
2924 }
2925 if let Some(parent_id) = state
2926 .surfaces
2927 .get(&sid)
2928 .and_then(|s| s.parent_surface_id.clone())
2929 && let Some(parent) = state.surfaces.get_mut(&parent_id)
2930 {
2931 parent.children.retain(|c| *c != sid);
2932 }
2933 if let Some(surf) = state.surfaces.remove(&sid) {
2934 for fb in surf.pending_presentation_feedbacks {
2935 fb.discarded();
2936 }
2937 if surf.surface_id > 0 {
2938 state.toplevel_surface_ids.remove(&surf.surface_id);
2939 state.last_reported_size.remove(&surf.surface_id);
2940 state.surface_sizes.remove(&surf.surface_id);
2941 let _ = state.event_tx.send(CompositorEvent::SurfaceDestroyed {
2942 surface_id: surf.surface_id,
2943 });
2944 (state.event_notify)();
2945 }
2946 }
2947 }
2948 _ => {}
2949 }
2950 }
2951}
2952
2953impl Dispatch<WlCallback, ()> for Compositor {
2955 fn request(
2956 _: &mut Self,
2957 _: &Client,
2958 _: &WlCallback,
2959 _: <WlCallback as Resource>::Request,
2960 _: &(),
2961 _: &DisplayHandle,
2962 _: &mut DataInit<'_, Self>,
2963 ) {
2964 }
2965}
2966
2967impl GlobalDispatch<WpPresentation, ()> for Compositor {
2969 fn bind(
2970 _: &mut Self,
2971 _: &DisplayHandle,
2972 _: &Client,
2973 resource: New<WpPresentation>,
2974 _: &(),
2975 data_init: &mut DataInit<'_, Self>,
2976 ) {
2977 let pres = data_init.init(resource, ());
2978 pres.clock_id(libc::CLOCK_MONOTONIC as u32);
2980 }
2981}
2982
2983impl Dispatch<WpPresentation, ()> for Compositor {
2984 fn request(
2985 state: &mut Self,
2986 _: &Client,
2987 _: &WpPresentation,
2988 request: <WpPresentation as Resource>::Request,
2989 _: &(),
2990 _: &DisplayHandle,
2991 data_init: &mut DataInit<'_, Self>,
2992 ) {
2993 use wp_presentation::Request;
2994 match request {
2995 Request::Feedback { surface, callback } => {
2996 let fb = data_init.init(callback, ());
2997 let sid = surface.id();
2998 if let Some(surf) = state.surfaces.get_mut(&sid) {
2999 surf.pending_presentation_feedbacks.push(fb);
3000 }
3001 }
3002 Request::Destroy => {}
3003 _ => {}
3004 }
3005 }
3006}
3007
3008impl Dispatch<WpPresentationFeedback, ()> for Compositor {
3010 fn request(
3011 _: &mut Self,
3012 _: &Client,
3013 _: &WpPresentationFeedback,
3014 _: <WpPresentationFeedback as Resource>::Request,
3015 _: &(),
3016 _: &DisplayHandle,
3017 _: &mut DataInit<'_, Self>,
3018 ) {
3019 }
3020}
3021
3022impl Dispatch<WlRegion, ()> for Compositor {
3024 fn request(
3025 _: &mut Self,
3026 _: &Client,
3027 _: &WlRegion,
3028 _: <WlRegion as Resource>::Request,
3029 _: &(),
3030 _: &DisplayHandle,
3031 _: &mut DataInit<'_, Self>,
3032 ) {
3033 }
3034}
3035
3036impl GlobalDispatch<WlSubcompositor, ()> for Compositor {
3038 fn bind(
3039 _: &mut Self,
3040 _: &DisplayHandle,
3041 _: &Client,
3042 resource: New<WlSubcompositor>,
3043 _: &(),
3044 data_init: &mut DataInit<'_, Self>,
3045 ) {
3046 data_init.init(resource, ());
3047 }
3048}
3049
3050impl Dispatch<WlSubcompositor, ()> for Compositor {
3051 fn request(
3052 state: &mut Self,
3053 _: &Client,
3054 _: &WlSubcompositor,
3055 request: <WlSubcompositor as Resource>::Request,
3056 _: &(),
3057 _: &DisplayHandle,
3058 data_init: &mut DataInit<'_, Self>,
3059 ) {
3060 use wayland_server::protocol::wl_subcompositor::Request;
3061 match request {
3062 Request::GetSubsurface {
3063 id,
3064 surface,
3065 parent,
3066 } => {
3067 let child_id = surface.id();
3068 let parent_id = parent.id();
3069 data_init.init(
3070 id,
3071 SubsurfaceData {
3072 wl_surface_id: child_id.clone(),
3073 parent_surface_id: parent_id.clone(),
3074 },
3075 );
3076 if let Some(surf) = state.surfaces.get_mut(&child_id) {
3077 surf.parent_surface_id = Some(parent_id.clone());
3078 }
3079 if let Some(parent_surf) = state.surfaces.get_mut(&parent_id)
3080 && !parent_surf.children.contains(&child_id)
3081 {
3082 parent_surf.children.push(child_id);
3083 }
3084 }
3085 Request::Destroy => {}
3086 _ => {}
3087 }
3088 }
3089}
3090
3091impl Dispatch<WlSubsurface, SubsurfaceData> for Compositor {
3093 fn request(
3094 state: &mut Self,
3095 _: &Client,
3096 _: &WlSubsurface,
3097 request: <WlSubsurface as Resource>::Request,
3098 data: &SubsurfaceData,
3099 _: &DisplayHandle,
3100 _: &mut DataInit<'_, Self>,
3101 ) {
3102 use wayland_server::protocol::wl_subsurface::Request;
3103 match request {
3104 Request::SetPosition { x, y } => {
3105 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3106 surf.pending_subsurface_position = Some((x, y));
3107 }
3108 }
3109 Request::PlaceAbove { sibling } => {
3110 let sibling_id = sibling.id();
3111 if let Some(parent) = state.surfaces.get_mut(&data.parent_surface_id) {
3112 let child_id = &data.wl_surface_id;
3113 parent.children.retain(|c| c != child_id);
3114 let pos = parent
3115 .children
3116 .iter()
3117 .position(|c| *c == sibling_id)
3118 .map(|p| p + 1)
3119 .unwrap_or(parent.children.len());
3120 parent.children.insert(pos, child_id.clone());
3121 }
3122 }
3123 Request::PlaceBelow { sibling } => {
3124 let sibling_id = sibling.id();
3125 if let Some(parent) = state.surfaces.get_mut(&data.parent_surface_id) {
3126 let child_id = &data.wl_surface_id;
3127 parent.children.retain(|c| c != child_id);
3128 let pos = parent
3129 .children
3130 .iter()
3131 .position(|c| *c == sibling_id)
3132 .unwrap_or(0);
3133 parent.children.insert(pos, child_id.clone());
3134 }
3135 }
3136 Request::SetSync | Request::SetDesync => {}
3137 Request::Destroy => {
3138 let child_id = &data.wl_surface_id;
3139 if let Some(parent) = state.surfaces.get_mut(&data.parent_surface_id) {
3140 parent.children.retain(|c| c != child_id);
3141 }
3142 if let Some(surf) = state.surfaces.get_mut(child_id) {
3143 surf.parent_surface_id = None;
3144 }
3145 }
3146 _ => {}
3147 }
3148 }
3149}
3150
3151impl GlobalDispatch<XdgWmBase, ()> for Compositor {
3153 fn bind(
3154 _: &mut Self,
3155 _: &DisplayHandle,
3156 _: &Client,
3157 resource: New<XdgWmBase>,
3158 _: &(),
3159 data_init: &mut DataInit<'_, Self>,
3160 ) {
3161 data_init.init(resource, ());
3162 }
3163}
3164
3165impl Dispatch<XdgWmBase, ()> for Compositor {
3166 fn request(
3167 state: &mut Self,
3168 _: &Client,
3169 _: &XdgWmBase,
3170 request: <XdgWmBase as Resource>::Request,
3171 _: &(),
3172 _: &DisplayHandle,
3173 data_init: &mut DataInit<'_, Self>,
3174 ) {
3175 use xdg_wm_base::Request;
3176 match request {
3177 Request::GetXdgSurface { id, surface } => {
3178 let wl_surface_id = surface.id();
3179 let xdg_surface = data_init.init(
3180 id,
3181 XdgSurfaceData {
3182 wl_surface_id: wl_surface_id.clone(),
3183 },
3184 );
3185 if let Some(surf) = state.surfaces.get_mut(&wl_surface_id) {
3186 surf.xdg_surface = Some(xdg_surface);
3187 }
3188 }
3189 Request::CreatePositioner { id } => {
3190 let positioner = data_init.init(id, ());
3191 let pos_id = positioner.id();
3192 state.positioners.insert(
3193 pos_id,
3194 PositionerState {
3195 resource: positioner,
3196 geometry: PositionerGeometry {
3197 size: (0, 0),
3198 anchor_rect: (0, 0, 0, 0),
3199 anchor: 0,
3200 gravity: 0,
3201 constraint_adjustment: 0,
3202 offset: (0, 0),
3203 },
3204 },
3205 );
3206 }
3207 Request::Pong { .. } => {}
3208 Request::Destroy => {}
3209 _ => {}
3210 }
3211 }
3212}
3213
3214impl Dispatch<XdgSurface, XdgSurfaceData> for Compositor {
3216 fn request(
3217 state: &mut Self,
3218 _: &Client,
3219 resource: &XdgSurface,
3220 request: <XdgSurface as Resource>::Request,
3221 data: &XdgSurfaceData,
3222 _: &DisplayHandle,
3223 data_init: &mut DataInit<'_, Self>,
3224 ) {
3225 use xdg_surface::Request;
3226 match request {
3227 Request::GetToplevel { id } => {
3228 let toplevel = data_init.init(
3229 id,
3230 XdgToplevelData {
3231 wl_surface_id: data.wl_surface_id.clone(),
3232 },
3233 );
3234 let surface_id = state.allocate_surface_id();
3235 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3236 surf.xdg_toplevel = Some(toplevel.clone());
3237 surf.surface_id = surface_id;
3238 }
3239 state
3240 .toplevel_surface_ids
3241 .insert(surface_id, data.wl_surface_id.clone());
3242
3243 let (cw, ch) = state
3248 .surface_sizes
3249 .get(&surface_id)
3250 .copied()
3251 .unwrap_or((state.output_width, state.output_height));
3252 let states = xdg_toplevel_states(&[
3253 xdg_toplevel::State::Activated,
3254 xdg_toplevel::State::Maximized,
3255 ]);
3256 toplevel.configure(cw, ch, states);
3257 let serial = state.next_serial();
3258 resource.configure(serial);
3259
3260 state.set_keyboard_focus(surface_id);
3263 if let Some(surf) = state.surfaces.get(&data.wl_surface_id) {
3266 for output in &state.outputs {
3267 if same_client(output, &surf.wl_surface) {
3268 surf.wl_surface.enter(output);
3269 }
3270 }
3271 }
3272 let _ = state.display_handle.flush_clients();
3273
3274 let _ = state.event_tx.send(CompositorEvent::SurfaceCreated {
3275 surface_id,
3276 title: String::new(),
3277 app_id: String::new(),
3278 parent_id: 0,
3279 width: 0,
3280 height: 0,
3281 });
3282 (state.event_notify)();
3283 if state.verbose {
3284 eprintln!("[compositor] new_toplevel sid={surface_id}");
3285 }
3286 }
3287 Request::GetPopup {
3288 id,
3289 parent,
3290 positioner,
3291 } => {
3292 let popup = data_init.init(
3293 id,
3294 XdgPopupData {
3295 wl_surface_id: data.wl_surface_id.clone(),
3296 },
3297 );
3298
3299 let parent_wl_id: Option<ObjectId> = parent
3302 .as_ref()
3303 .and_then(|p| p.data::<XdgSurfaceData>())
3304 .map(|d| d.wl_surface_id.clone());
3305
3306 let parent_geom_offset = parent_wl_id
3311 .as_ref()
3312 .and_then(|pid| state.surfaces.get(pid))
3313 .and_then(|s| s.xdg_geometry)
3314 .map(|(gx, gy, _, _)| (gx, gy))
3315 .unwrap_or((0, 0));
3316
3317 let parent_abs = parent_wl_id
3322 .as_ref()
3323 .map(|pid| {
3324 let abs = state.surface_absolute_position(pid);
3325 (abs.0 + parent_geom_offset.0, abs.1 + parent_geom_offset.1)
3326 })
3327 .unwrap_or((0, 0));
3328 let (_, toplevel_root) = parent_wl_id
3331 .as_ref()
3332 .map(|pid| state.find_toplevel_root(pid))
3333 .unwrap_or_else(|| {
3334 (data.wl_surface_id.clone(), None)
3336 });
3337 let bounds = toplevel_root
3338 .and_then(|_| {
3339 let root_wl_id = parent_wl_id.as_ref().map(|pid| {
3340 let (rid, _) = state.find_toplevel_root(pid);
3341 rid
3342 })?;
3343 let surf = state.surfaces.get(&root_wl_id)?;
3344 if let Some((gx, gy, gw, gh)) = surf.xdg_geometry
3345 && gw > 0
3346 && gh > 0
3347 {
3348 return Some((gx, gy, gw, gh));
3349 }
3350
3351 let sm = state.surface_meta.get(&root_wl_id)?;
3354 let s = (sm.scale).max(1);
3355 let (lw, lh) = surf
3356 .viewport_destination
3357 .filter(|&(dw, dh)| dw > 0 && dh > 0)
3358 .unwrap_or((sm.width as i32 / s, sm.height as i32 / s));
3359 Some((0, 0, lw, lh))
3360 })
3361 .unwrap_or((0, 0, state.output_width, state.output_height));
3362
3363 eprintln!(
3364 "[popup] parent_abs={parent_abs:?} bounds={bounds:?} parent_wl={parent_wl_id:?} geom_off={parent_geom_offset:?}"
3365 );
3366 let pos_id = positioner.id();
3368 let (px, py, pw, ph) = state
3369 .positioners
3370 .get(&pos_id)
3371 .map(|p| p.geometry.compute_position(parent_abs, bounds))
3372 .unwrap_or((0, 0, 200, 200));
3373 eprintln!("[popup] result=({px},{py},{pw},{ph})");
3374
3375 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3376 surf.xdg_popup = Some(popup.clone());
3377 surf.parent_surface_id = parent_wl_id.clone();
3378 surf.subsurface_position =
3383 (parent_geom_offset.0 + px, parent_geom_offset.1 + py);
3384 }
3385 if let Some(ref parent_id) = parent_wl_id
3386 && let Some(parent_surf) = state.surfaces.get_mut(parent_id)
3387 && !parent_surf.children.contains(&data.wl_surface_id)
3388 {
3389 parent_surf.children.push(data.wl_surface_id.clone());
3390 }
3391
3392 popup.configure(px, py, pw, ph);
3393 let serial = state.next_serial();
3394 resource.configure(serial);
3395 let _ = state.display_handle.flush_clients();
3396 }
3397 Request::SetWindowGeometry {
3398 x,
3399 y,
3400 width,
3401 height,
3402 } => {
3403 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3404 if surf.xdg_popup.is_some() {
3412 let (old_gx, old_gy) = surf
3413 .xdg_geometry
3414 .map(|(gx, gy, _, _)| (gx, gy))
3415 .unwrap_or((0, 0));
3416 surf.subsurface_position.0 += old_gx - x;
3417 surf.subsurface_position.1 += old_gy - y;
3418 }
3419 surf.xdg_geometry = Some((x, y, width, height));
3420 }
3421 }
3422 Request::AckConfigure { .. } => {}
3423 Request::Destroy => {}
3424 _ => {}
3425 }
3426 }
3427}
3428
3429impl Dispatch<XdgToplevel, XdgToplevelData> for Compositor {
3431 fn request(
3432 state: &mut Self,
3433 _: &Client,
3434 _: &XdgToplevel,
3435 request: <XdgToplevel as Resource>::Request,
3436 data: &XdgToplevelData,
3437 _: &DisplayHandle,
3438 _: &mut DataInit<'_, Self>,
3439 ) {
3440 use xdg_toplevel::Request;
3441 match request {
3442 Request::SetTitle { title } => {
3443 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id)
3444 && surf.title != title
3445 {
3446 surf.title = title.clone();
3447 if surf.surface_id > 0 {
3448 let _ = state.event_tx.send(CompositorEvent::SurfaceTitle {
3449 surface_id: surf.surface_id,
3450 title,
3451 });
3452 (state.event_notify)();
3453 }
3454 }
3455 }
3456 Request::SetAppId { app_id } => {
3457 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id)
3458 && surf.app_id != app_id
3459 {
3460 surf.app_id = app_id.clone();
3461 if surf.surface_id > 0 {
3462 let _ = state.event_tx.send(CompositorEvent::SurfaceAppId {
3463 surface_id: surf.surface_id,
3464 app_id,
3465 });
3466 (state.event_notify)();
3467 }
3468 }
3469 }
3470 Request::Destroy => {
3471 let wl_surface_id = &data.wl_surface_id;
3472 state.surface_meta.remove(wl_surface_id);
3473 state.cursor_rgba.remove(wl_surface_id);
3474 if let Some(ref mut vk) = state.vulkan_renderer {
3475 vk.remove_surface(wl_surface_id);
3476 }
3477 if let Some(held) = state.held_buffers.remove(wl_surface_id) {
3478 held.release();
3479 }
3480 if let Some(surf) = state.surfaces.get_mut(wl_surface_id) {
3481 let sid = surf.surface_id;
3482 surf.xdg_toplevel = None;
3483 if sid > 0 {
3484 state.toplevel_surface_ids.remove(&sid);
3485 state.last_reported_size.remove(&sid);
3486 state.surface_sizes.remove(&sid);
3487 let _ = state
3488 .event_tx
3489 .send(CompositorEvent::SurfaceDestroyed { surface_id: sid });
3490 (state.event_notify)();
3491 surf.surface_id = 0;
3492 }
3493 }
3494 }
3495 _ => {}
3496 }
3497 }
3498}
3499
3500impl Dispatch<XdgPopup, XdgPopupData> for Compositor {
3502 fn request(
3503 state: &mut Self,
3504 _: &Client,
3505 _: &XdgPopup,
3506 request: <XdgPopup as Resource>::Request,
3507 data: &XdgPopupData,
3508 _: &DisplayHandle,
3509 _: &mut DataInit<'_, Self>,
3510 ) {
3511 use xdg_popup::Request;
3512 match request {
3513 Request::Grab { seat: _, serial: _ } => {
3514 state
3517 .popup_grab_stack
3518 .retain(|id| *id != data.wl_surface_id);
3519 state.popup_grab_stack.push(data.wl_surface_id.clone());
3520 }
3521 Request::Reposition { positioner, token } => {
3522 let pos_id = positioner.id();
3524 if let Some(surf) = state.surfaces.get(&data.wl_surface_id)
3525 && let Some(parent_id) = surf.parent_surface_id.clone()
3526 {
3527 let parent_geom_offset = state
3528 .surfaces
3529 .get(&parent_id)
3530 .and_then(|s| s.xdg_geometry)
3531 .map(|(gx, gy, _, _)| (gx, gy))
3532 .unwrap_or((0, 0));
3533 let parent_abs = {
3534 let abs = state.surface_absolute_position(&parent_id);
3535 (abs.0 + parent_geom_offset.0, abs.1 + parent_geom_offset.1)
3536 };
3537 let (root_id, toplevel_root) = state.find_toplevel_root(&parent_id);
3538 let bounds = toplevel_root
3539 .and_then(|_| {
3540 let surf = state.surfaces.get(&root_id)?;
3541 if let Some((gx, gy, gw, gh)) = surf.xdg_geometry
3542 && gw > 0
3543 && gh > 0
3544 {
3545 return Some((gx, gy, gw, gh));
3546 }
3547 let sm = state.surface_meta.get(&root_id)?;
3548 let s = (sm.scale).max(1);
3549 let (lw, lh) = surf
3550 .viewport_destination
3551 .filter(|&(dw, dh)| dw > 0 && dh > 0)
3552 .unwrap_or((sm.width as i32 / s, sm.height as i32 / s));
3553 Some((0, 0, lw, lh))
3554 })
3555 .unwrap_or((0, 0, state.output_width, state.output_height));
3556 let (px, py, pw, ph) = state
3557 .positioners
3558 .get(&pos_id)
3559 .map(|p| p.geometry.compute_position(parent_abs, bounds))
3560 .unwrap_or((0, 0, 200, 200));
3561 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3562 let old_gx = surf.xdg_geometry.map(|(gx, _, _, _)| gx).unwrap_or(0);
3565 let old_gy = surf.xdg_geometry.map(|(_, gy, _, _)| gy).unwrap_or(0);
3566 surf.subsurface_position = (
3567 parent_geom_offset.0 + px - old_gx,
3568 parent_geom_offset.1 + py - old_gy,
3569 );
3570 if let Some(ref popup) = surf.xdg_popup {
3571 popup.configure(px, py, pw, ph);
3572 popup.repositioned(token);
3573 }
3574 if let Some(ref xs) = surf.xdg_surface {
3575 let serial = state.serial.wrapping_add(1);
3576 state.serial = serial;
3577 xs.configure(serial);
3578 }
3579 }
3580 }
3581 }
3582 Request::Destroy => {
3583 state
3585 .popup_grab_stack
3586 .retain(|id| *id != data.wl_surface_id);
3587 if let Some(parent_id) = state
3589 .surfaces
3590 .get(&data.wl_surface_id)
3591 .and_then(|s| s.parent_surface_id.clone())
3592 && let Some(parent) = state.surfaces.get_mut(&parent_id)
3593 {
3594 parent.children.retain(|c| *c != data.wl_surface_id);
3595 }
3596 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3597 surf.xdg_popup = None;
3598 surf.parent_surface_id = None;
3599 }
3600 }
3601 _ => {}
3602 }
3603 }
3604}
3605
3606use wayland_protocols::xdg::shell::server::xdg_positioner;
3608impl Dispatch<XdgPositioner, ()> for Compositor {
3609 fn request(
3610 state: &mut Self,
3611 _: &Client,
3612 resource: &XdgPositioner,
3613 request: <XdgPositioner as Resource>::Request,
3614 _: &(),
3615 _: &DisplayHandle,
3616 _: &mut DataInit<'_, Self>,
3617 ) {
3618 use xdg_positioner::Request;
3619 let pos_id = resource.id();
3620 let Some(pos) = state.positioners.get_mut(&pos_id) else {
3621 return;
3622 };
3623 match request {
3624 Request::SetSize { width, height } => {
3625 pos.geometry.size = (width, height);
3626 }
3627 Request::SetAnchorRect {
3628 x,
3629 y,
3630 width,
3631 height,
3632 } => {
3633 pos.geometry.anchor_rect = (x, y, width, height);
3634 }
3635 Request::SetAnchor {
3636 anchor: wayland_server::WEnum::Value(v),
3637 } => {
3638 pos.geometry.anchor = v as u32;
3639 }
3640 Request::SetGravity {
3641 gravity: wayland_server::WEnum::Value(v),
3642 } => {
3643 pos.geometry.gravity = v as u32;
3644 }
3645 Request::SetOffset { x, y } => {
3646 pos.geometry.offset = (x, y);
3647 }
3648 Request::SetConstraintAdjustment {
3649 constraint_adjustment,
3650 } => {
3651 pos.geometry.constraint_adjustment = constraint_adjustment.into();
3652 }
3653 Request::Destroy => {
3654 state.positioners.remove(&pos_id);
3655 }
3656 _ => {}
3657 }
3658 }
3659}
3660
3661impl GlobalDispatch<ZxdgDecorationManagerV1, ()> for Compositor {
3663 fn bind(
3664 _: &mut Self,
3665 _: &DisplayHandle,
3666 _: &Client,
3667 resource: New<ZxdgDecorationManagerV1>,
3668 _: &(),
3669 data_init: &mut DataInit<'_, Self>,
3670 ) {
3671 data_init.init(resource, ());
3672 }
3673}
3674
3675impl Dispatch<ZxdgDecorationManagerV1, ()> for Compositor {
3676 fn request(
3677 _: &mut Self,
3678 _: &Client,
3679 _: &ZxdgDecorationManagerV1,
3680 request: <ZxdgDecorationManagerV1 as Resource>::Request,
3681 _: &(),
3682 _: &DisplayHandle,
3683 data_init: &mut DataInit<'_, Self>,
3684 ) {
3685 use zxdg_decoration_manager_v1::Request;
3686 match request {
3687 Request::GetToplevelDecoration { id, toplevel: _ } => {
3688 let decoration = data_init.init(id, ());
3689 decoration.configure(zxdg_toplevel_decoration_v1::Mode::ServerSide);
3691 }
3692 Request::Destroy => {}
3693 _ => {}
3694 }
3695 }
3696}
3697
3698impl Dispatch<ZxdgToplevelDecorationV1, ()> for Compositor {
3699 fn request(
3700 _: &mut Self,
3701 _: &Client,
3702 resource: &ZxdgToplevelDecorationV1,
3703 request: <ZxdgToplevelDecorationV1 as Resource>::Request,
3704 _: &(),
3705 _: &DisplayHandle,
3706 _: &mut DataInit<'_, Self>,
3707 ) {
3708 use zxdg_toplevel_decoration_v1::Request;
3709 match request {
3710 Request::SetMode { .. } | Request::UnsetMode => {
3711 resource.configure(zxdg_toplevel_decoration_v1::Mode::ServerSide);
3712 }
3713 Request::Destroy => {}
3714 _ => {}
3715 }
3716 }
3717}
3718
3719impl GlobalDispatch<WlShm, ()> for Compositor {
3721 fn bind(
3722 _: &mut Self,
3723 _: &DisplayHandle,
3724 _: &Client,
3725 resource: New<WlShm>,
3726 _: &(),
3727 data_init: &mut DataInit<'_, Self>,
3728 ) {
3729 let shm = data_init.init(resource, ());
3730 shm.format(wl_shm::Format::Argb8888);
3731 shm.format(wl_shm::Format::Xrgb8888);
3732 shm.format(wl_shm::Format::Abgr8888);
3733 shm.format(wl_shm::Format::Xbgr8888);
3734 }
3735}
3736
3737impl Dispatch<WlShm, ()> for Compositor {
3738 fn request(
3739 state: &mut Self,
3740 _: &Client,
3741 _: &WlShm,
3742 request: <WlShm as Resource>::Request,
3743 _: &(),
3744 _: &DisplayHandle,
3745 data_init: &mut DataInit<'_, Self>,
3746 ) {
3747 use wayland_server::protocol::wl_shm::Request;
3748 if let Request::CreatePool { id, fd, size } = request {
3749 let pool = data_init.init(id, ());
3750 let pool_id = pool.id();
3751 state
3752 .shm_pools
3753 .insert(pool_id, Arc::new(ShmPool::new(pool, fd, size)));
3754 }
3755 }
3756}
3757
3758impl Dispatch<WlShmPool, ()> for Compositor {
3760 fn request(
3761 state: &mut Self,
3762 _: &Client,
3763 resource: &WlShmPool,
3764 request: <WlShmPool as Resource>::Request,
3765 _: &(),
3766 _: &DisplayHandle,
3767 data_init: &mut DataInit<'_, Self>,
3768 ) {
3769 use wayland_server::protocol::wl_shm_pool::Request;
3770 let pool_id = resource.id();
3771 match request {
3772 Request::CreateBuffer {
3773 id,
3774 offset,
3775 width,
3776 height,
3777 stride,
3778 format,
3779 } => {
3780 let fmt = match format {
3782 wayland_server::WEnum::Value(f) => f,
3783 _ => wl_shm::Format::Argb8888, };
3785 let Some(pool) = state.shm_pools.get(&pool_id).cloned() else {
3786 return;
3787 };
3788 data_init.init(
3789 id,
3790 ShmBufferData {
3791 pool,
3792 offset,
3793 width,
3794 height,
3795 stride,
3796 format: fmt,
3797 },
3798 );
3799 }
3800 Request::Resize { size } => {
3801 if let Some(pool) = state.shm_pools.get(&pool_id) {
3802 pool.resize(size);
3803 }
3804 }
3805 Request::Destroy => {
3806 state.shm_pools.remove(&pool_id);
3809 }
3810 _ => {}
3811 }
3812 }
3813}
3814
3815impl Dispatch<WlBuffer, ShmBufferData> for Compositor {
3817 fn request(
3818 _: &mut Self,
3819 _: &Client,
3820 _: &WlBuffer,
3821 _: <WlBuffer as Resource>::Request,
3822 _: &ShmBufferData,
3823 _: &DisplayHandle,
3824 _: &mut DataInit<'_, Self>,
3825 ) {
3826 }
3827}
3828
3829impl Dispatch<WlBuffer, DmaBufBufferData> for Compositor {
3831 fn request(
3832 _: &mut Self,
3833 _: &Client,
3834 _: &WlBuffer,
3835 _: <WlBuffer as Resource>::Request,
3836 _: &DmaBufBufferData,
3837 _: &DisplayHandle,
3838 _: &mut DataInit<'_, Self>,
3839 ) {
3840 }
3841}
3842
3843impl GlobalDispatch<WlOutput, ()> for Compositor {
3845 fn bind(
3846 state: &mut Self,
3847 _: &DisplayHandle,
3848 _: &Client,
3849 resource: New<WlOutput>,
3850 _: &(),
3851 data_init: &mut DataInit<'_, Self>,
3852 ) {
3853 let output = data_init.init(resource, ());
3854 output.geometry(
3855 0,
3856 0,
3857 0,
3858 0,
3859 wl_output::Subpixel::Unknown,
3860 "Virtual".to_string(),
3861 "Headless".to_string(),
3862 wl_output::Transform::Normal,
3863 );
3864 let s120 = state.output_scale_120 as i32;
3865 let mode_w = state.output_width * s120 / 120;
3866 let mode_h = state.output_height * s120 / 120;
3867 output.mode(
3868 wl_output::Mode::Current | wl_output::Mode::Preferred,
3869 mode_w,
3870 mode_h,
3871 state.output_refresh_mhz as i32,
3872 );
3873 if output.version() >= 2 {
3874 output.scale(((state.output_scale_120 as i32) + 119) / 120);
3875 }
3876 if output.version() >= 2 {
3877 output.done();
3878 }
3879 state.outputs.push(output);
3880 }
3881}
3882
3883impl Dispatch<WlOutput, ()> for Compositor {
3884 fn request(
3885 state: &mut Self,
3886 _: &Client,
3887 resource: &WlOutput,
3888 request: <WlOutput as Resource>::Request,
3889 _: &(),
3890 _: &DisplayHandle,
3891 _: &mut DataInit<'_, Self>,
3892 ) {
3893 use wayland_server::protocol::wl_output::Request;
3894 if let Request::Release = request {
3895 state.outputs.retain(|o| o.id() != resource.id());
3896 }
3897 }
3898}
3899
3900impl GlobalDispatch<WlSeat, ()> for Compositor {
3902 fn bind(
3903 _: &mut Self,
3904 _: &DisplayHandle,
3905 _: &Client,
3906 resource: New<WlSeat>,
3907 _: &(),
3908 data_init: &mut DataInit<'_, Self>,
3909 ) {
3910 let seat = data_init.init(resource, ());
3911 seat.capabilities(wl_seat::Capability::Keyboard | wl_seat::Capability::Pointer);
3912 if seat.version() >= 2 {
3913 seat.name("headless".to_string());
3914 }
3915 }
3916}
3917
3918impl Dispatch<WlSeat, ()> for Compositor {
3919 fn request(
3920 state: &mut Self,
3921 _: &Client,
3922 _: &WlSeat,
3923 request: <WlSeat as Resource>::Request,
3924 _: &(),
3925 _: &DisplayHandle,
3926 data_init: &mut DataInit<'_, Self>,
3927 ) {
3928 use wayland_server::protocol::wl_seat::Request;
3929 match request {
3930 Request::GetKeyboard { id } => {
3931 let kb = data_init.init(id, ());
3932 if let Some(fd) = create_keymap_fd(&state.keyboard_keymap_data) {
3933 kb.keymap(
3934 wl_keyboard::KeymapFormat::XkbV1,
3935 fd.as_fd(),
3936 state.keyboard_keymap_data.len() as u32,
3937 );
3938 }
3939 if kb.version() >= 4 {
3940 kb.repeat_info(25, 200);
3941 }
3942 state.keyboards.push(kb);
3943 }
3944 Request::GetPointer { id } => {
3945 let ptr = data_init.init(id, ());
3946 state.pointers.push(ptr);
3947 }
3948 Request::GetTouch { id } => {
3949 data_init.init(id, ());
3950 }
3951 Request::Release => {}
3952 _ => {}
3953 }
3954 }
3955}
3956
3957impl Dispatch<WlKeyboard, ()> for Compositor {
3959 fn request(
3960 state: &mut Self,
3961 _: &Client,
3962 resource: &WlKeyboard,
3963 request: <WlKeyboard as Resource>::Request,
3964 _: &(),
3965 _: &DisplayHandle,
3966 _: &mut DataInit<'_, Self>,
3967 ) {
3968 if let wl_keyboard::Request::Release = request {
3969 state.keyboards.retain(|k| k.id() != resource.id());
3970 }
3971 }
3972}
3973
3974impl Dispatch<WlPointer, ()> for Compositor {
3976 fn request(
3977 state: &mut Self,
3978 _: &Client,
3979 resource: &WlPointer,
3980 request: <WlPointer as Resource>::Request,
3981 _: &(),
3982 _: &DisplayHandle,
3983 _: &mut DataInit<'_, Self>,
3984 ) {
3985 use wl_pointer::Request;
3986 match request {
3987 Request::SetCursor {
3988 serial: _,
3989 surface,
3990 hotspot_x,
3991 hotspot_y,
3992 } => {
3993 if let Some(surface) = surface {
3994 let sid = surface.id();
3995 if let Some(surf) = state.surfaces.get_mut(&sid) {
3996 surf.is_cursor = true;
3997 surf.cursor_hotspot = (hotspot_x, hotspot_y);
3998 }
3999 } else {
4000 let _ = state.event_tx.send(CompositorEvent::SurfaceCursor {
4001 surface_id: state.focused_surface_id,
4002 cursor: CursorImage::Hidden,
4003 });
4004 }
4005 }
4006 Request::Release => {
4007 state.pointers.retain(|p| p.id() != resource.id());
4008 }
4009 _ => {}
4010 }
4011 }
4012}
4013
4014impl Dispatch<wayland_server::protocol::wl_touch::WlTouch, ()> for Compositor {
4016 fn request(
4017 _: &mut Self,
4018 _: &Client,
4019 _: &wayland_server::protocol::wl_touch::WlTouch,
4020 _: <wayland_server::protocol::wl_touch::WlTouch as Resource>::Request,
4021 _: &(),
4022 _: &DisplayHandle,
4023 _: &mut DataInit<'_, Self>,
4024 ) {
4025 }
4026}
4027
4028impl GlobalDispatch<ZwpLinuxDmabufV1, ()> for Compositor {
4030 fn bind(
4031 state: &mut Self,
4032 _: &DisplayHandle,
4033 _: &Client,
4034 resource: New<ZwpLinuxDmabufV1>,
4035 _: &(),
4036 data_init: &mut DataInit<'_, Self>,
4037 ) {
4038 let dmabuf = data_init.init(resource, ());
4039 if dmabuf.version() >= 4 {
4042 return;
4043 }
4044 if dmabuf.version() >= 3 {
4045 if let Some(ref vk) = state.vulkan_renderer
4051 && !vk.supported_dmabuf_modifiers.is_empty()
4052 {
4053 for &(drm_fmt, modifier) in &vk.supported_dmabuf_modifiers {
4054 let mod_hi = (modifier >> 32) as u32;
4055 let mod_lo = (modifier & 0xFFFFFFFF) as u32;
4056 dmabuf.modifier(drm_fmt, mod_hi, mod_lo);
4057 }
4058 }
4059 } else if state
4063 .vulkan_renderer
4064 .as_ref()
4065 .is_some_and(|vk| vk.has_dmabuf())
4066 {
4067 dmabuf.format(drm_fourcc::ARGB8888);
4068 dmabuf.format(drm_fourcc::XRGB8888);
4069 dmabuf.format(drm_fourcc::ABGR8888);
4070 dmabuf.format(drm_fourcc::XBGR8888);
4071 }
4072 }
4073}
4074
4075impl Dispatch<ZwpLinuxDmabufV1, ()> for Compositor {
4076 fn request(
4077 state: &mut Self,
4078 _: &Client,
4079 _: &ZwpLinuxDmabufV1,
4080 request: <ZwpLinuxDmabufV1 as Resource>::Request,
4081 _: &(),
4082 _: &DisplayHandle,
4083 data_init: &mut DataInit<'_, Self>,
4084 ) {
4085 use zwp_linux_dmabuf_v1::Request;
4086 match request {
4087 Request::CreateParams { params_id } => {
4088 data_init.init(params_id, ());
4089 }
4090 Request::GetDefaultFeedback { id } => {
4091 let fb = data_init.init(id, ());
4092 state.send_dmabuf_feedback(&fb);
4093 }
4094 Request::GetSurfaceFeedback { id, .. } => {
4095 let fb = data_init.init(id, ());
4096 state.send_dmabuf_feedback(&fb);
4097 }
4098 Request::Destroy => {}
4099 _ => {}
4100 }
4101 }
4102}
4103
4104impl Dispatch<ZwpLinuxDmabufFeedbackV1, ()> for Compositor {
4105 fn request(
4106 _: &mut Self,
4107 _: &Client,
4108 _: &ZwpLinuxDmabufFeedbackV1,
4109 _request: <ZwpLinuxDmabufFeedbackV1 as Resource>::Request,
4110 _: &(),
4111 _: &DisplayHandle,
4112 _data_init: &mut DataInit<'_, Self>,
4113 ) {
4114 }
4116}
4117
4118impl Dispatch<ZwpLinuxBufferParamsV1, ()> for Compositor {
4120 fn request(
4121 state: &mut Self,
4122 client: &Client,
4123 resource: &ZwpLinuxBufferParamsV1,
4124 request: <ZwpLinuxBufferParamsV1 as Resource>::Request,
4125 _: &(),
4126 dh: &DisplayHandle,
4127 data_init: &mut DataInit<'_, Self>,
4128 ) {
4129 use zwp_linux_buffer_params_v1::Request;
4130 let params_id = resource.id();
4131 match request {
4132 Request::Add {
4133 fd,
4134 plane_idx: _,
4135 offset,
4136 stride,
4137 modifier_hi,
4138 modifier_lo,
4139 } => {
4140 let modifier = ((modifier_hi as u64) << 32) | (modifier_lo as u64);
4141 let entry = state
4142 .dmabuf_params
4143 .entry(params_id.clone())
4144 .or_insert_with(|| DmaBufParamsPending {
4145 resource: resource.clone(),
4146 planes: Vec::new(),
4147 modifier,
4148 });
4149 entry.modifier = modifier;
4150 entry.planes.push(DmaBufPlane { fd, offset, stride });
4151 }
4152 Request::Create {
4153 width,
4154 height,
4155 format,
4156 flags,
4157 } => {
4158 let pending = state.dmabuf_params.remove(¶ms_id);
4159 let (planes, modifier) = match pending {
4160 Some(p) => (p.planes, p.modifier),
4161 None => {
4162 resource.failed();
4163 return;
4164 }
4165 };
4166 let y_invert = flags
4167 .into_result()
4168 .ok()
4169 .is_some_and(|f| f.contains(zwp_linux_buffer_params_v1::Flags::YInvert));
4170 match client.create_resource::<WlBuffer, DmaBufBufferData, Compositor>(
4171 dh,
4172 1,
4173 DmaBufBufferData {
4174 width,
4175 height,
4176 fourcc: format,
4177 modifier,
4178 planes,
4179 y_invert,
4180 },
4181 ) {
4182 Ok(buffer) => resource.created(&buffer),
4183 Err(_) => resource.failed(),
4184 }
4185 }
4186 Request::CreateImmed {
4187 buffer_id,
4188 width,
4189 height,
4190 format,
4191 flags,
4192 } => {
4193 let (planes, modifier) = state
4194 .dmabuf_params
4195 .remove(¶ms_id)
4196 .map(|p| (p.planes, p.modifier))
4197 .unwrap_or_default();
4198 let y_invert = flags
4199 .into_result()
4200 .ok()
4201 .is_some_and(|f| f.contains(zwp_linux_buffer_params_v1::Flags::YInvert));
4202 data_init.init(
4203 buffer_id,
4204 DmaBufBufferData {
4205 width,
4206 height,
4207 fourcc: format,
4208 modifier,
4209 planes,
4210 y_invert,
4211 },
4212 );
4213 }
4214 Request::Destroy => {
4215 state.dmabuf_params.remove(¶ms_id);
4216 }
4217 _ => {}
4218 }
4219 }
4220}
4221
4222impl GlobalDispatch<WpFractionalScaleManagerV1, ()> for Compositor {
4224 fn bind(
4225 _: &mut Self,
4226 _: &DisplayHandle,
4227 _: &Client,
4228 resource: New<WpFractionalScaleManagerV1>,
4229 _: &(),
4230 data_init: &mut DataInit<'_, Self>,
4231 ) {
4232 data_init.init(resource, ());
4233 }
4234}
4235
4236impl Dispatch<WpFractionalScaleManagerV1, ()> for Compositor {
4237 fn request(
4238 state: &mut Self,
4239 _: &Client,
4240 _: &WpFractionalScaleManagerV1,
4241 request: <WpFractionalScaleManagerV1 as Resource>::Request,
4242 _: &(),
4243 _: &DisplayHandle,
4244 data_init: &mut DataInit<'_, Self>,
4245 ) {
4246 use wp_fractional_scale_manager_v1::Request;
4247 match request {
4248 Request::GetFractionalScale { id, surface: _ } => {
4249 let fs = data_init.init(id, ());
4250 fs.preferred_scale(state.output_scale_120 as u32);
4252 state.fractional_scales.push(fs);
4253 }
4254 Request::Destroy => {}
4255 _ => {}
4256 }
4257 }
4258}
4259
4260impl Dispatch<WpFractionalScaleV1, ()> for Compositor {
4262 fn request(
4263 state: &mut Self,
4264 _: &Client,
4265 resource: &WpFractionalScaleV1,
4266 _: <WpFractionalScaleV1 as Resource>::Request,
4267 _: &(),
4268 _: &DisplayHandle,
4269 _: &mut DataInit<'_, Self>,
4270 ) {
4271 state
4273 .fractional_scales
4274 .retain(|fs| fs.id() != resource.id());
4275 }
4276}
4277
4278impl GlobalDispatch<WpViewporter, ()> for Compositor {
4280 fn bind(
4281 _: &mut Self,
4282 _: &DisplayHandle,
4283 _: &Client,
4284 resource: New<WpViewporter>,
4285 _: &(),
4286 data_init: &mut DataInit<'_, Self>,
4287 ) {
4288 data_init.init(resource, ());
4289 }
4290}
4291
4292impl Dispatch<WpViewporter, ()> for Compositor {
4293 fn request(
4294 _: &mut Self,
4295 _: &Client,
4296 _: &WpViewporter,
4297 request: <WpViewporter as Resource>::Request,
4298 _: &(),
4299 _: &DisplayHandle,
4300 data_init: &mut DataInit<'_, Self>,
4301 ) {
4302 use wp_viewporter::Request;
4303 match request {
4304 Request::GetViewport { id, surface } => {
4305 let obj_id = surface.id();
4308 data_init.init(id, obj_id);
4309 }
4310 Request::Destroy => {}
4311 _ => {}
4312 }
4313 }
4314}
4315
4316impl Dispatch<WpViewport, ObjectId> for Compositor {
4318 fn request(
4319 state: &mut Self,
4320 _: &Client,
4321 _: &WpViewport,
4322 request: <WpViewport as Resource>::Request,
4323 surface_obj_id: &ObjectId,
4324 _: &DisplayHandle,
4325 _: &mut DataInit<'_, Self>,
4326 ) {
4327 use wayland_protocols::wp::viewporter::server::wp_viewport::Request;
4328 match request {
4329 Request::SetDestination { width, height } => {
4330 if let Some(surf) = state.surfaces.get_mut(surface_obj_id) {
4331 if width > 0 && height > 0 {
4333 surf.pending_viewport_destination = Some((width, height));
4334 } else {
4335 surf.pending_viewport_destination = None;
4336 }
4337 }
4338 }
4339 Request::SetSource { .. } => {
4340 }
4342 Request::Destroy => {}
4343 _ => {}
4344 }
4345 }
4346}
4347
4348impl GlobalDispatch<WlDataDeviceManager, ()> for Compositor {
4355 fn bind(
4356 _: &mut Self,
4357 _: &DisplayHandle,
4358 _: &Client,
4359 resource: New<WlDataDeviceManager>,
4360 _: &(),
4361 data_init: &mut DataInit<'_, Self>,
4362 ) {
4363 data_init.init(resource, ());
4364 }
4365}
4366
4367impl Dispatch<WlDataDeviceManager, ()> for Compositor {
4368 fn request(
4369 state: &mut Self,
4370 _: &Client,
4371 _: &WlDataDeviceManager,
4372 request: <WlDataDeviceManager as Resource>::Request,
4373 _: &(),
4374 _: &DisplayHandle,
4375 data_init: &mut DataInit<'_, Self>,
4376 ) {
4377 use wl_data_device_manager::Request;
4378 match request {
4379 Request::CreateDataSource { id } => {
4380 data_init.init(
4381 id,
4382 DataSourceData {
4383 mime_types: std::sync::Mutex::new(Vec::new()),
4384 },
4385 );
4386 }
4387 Request::GetDataDevice { id, seat: _ } => {
4388 let dd = data_init.init(id, ());
4389 state.data_devices.push(dd);
4390 }
4391 _ => {}
4392 }
4393 }
4394}
4395
4396impl Dispatch<WlDataSource, DataSourceData> for Compositor {
4397 fn request(
4398 _: &mut Self,
4399 _: &Client,
4400 _: &WlDataSource,
4401 request: <WlDataSource as Resource>::Request,
4402 data: &DataSourceData,
4403 _: &DisplayHandle,
4404 _: &mut DataInit<'_, Self>,
4405 ) {
4406 use wl_data_source::Request;
4407 match request {
4408 Request::Offer { mime_type } => {
4409 data.mime_types.lock().unwrap().push(mime_type);
4410 }
4411 Request::Destroy => {}
4412 _ => {} }
4414 }
4415
4416 fn destroyed(
4417 state: &mut Self,
4418 _: wayland_server::backend::ClientId,
4419 resource: &WlDataSource,
4420 _: &DataSourceData,
4421 ) {
4422 if state
4423 .selection_source
4424 .as_ref()
4425 .is_some_and(|s| s.id() == resource.id())
4426 {
4427 state.selection_source = None;
4428 }
4429 }
4430}
4431
4432impl Dispatch<WlDataDevice, ()> for Compositor {
4433 fn request(
4434 state: &mut Self,
4435 _: &Client,
4436 _: &WlDataDevice,
4437 request: <WlDataDevice as Resource>::Request,
4438 _: &(),
4439 _: &DisplayHandle,
4440 _: &mut DataInit<'_, Self>,
4441 ) {
4442 use wl_data_device::Request;
4443 match request {
4444 Request::SetSelection { source, serial: _ } => {
4445 state.selection_source = source.clone();
4446 if let Some(ref src) = source {
4448 let data = src.data::<DataSourceData>().unwrap();
4449 let mimes = data.mime_types.lock().unwrap();
4450 let text_mime = mimes
4451 .iter()
4452 .find(|m| {
4453 m.as_str() == "text/plain;charset=utf-8"
4454 || m.as_str() == "text/plain"
4455 || m.as_str() == "UTF8_STRING"
4456 })
4457 .cloned();
4458 drop(mimes);
4459 if let Some(mime) = text_mime {
4460 state.read_data_source_and_emit(src, &mime);
4461 }
4462 }
4463 }
4464 Request::Release => {}
4465 _ => {} }
4467 }
4468
4469 fn destroyed(
4470 state: &mut Self,
4471 _: wayland_server::backend::ClientId,
4472 resource: &WlDataDevice,
4473 _: &(),
4474 ) {
4475 state.data_devices.retain(|d| d.id() != resource.id());
4476 }
4477}
4478
4479impl Dispatch<WlDataOffer, DataOfferData> for Compositor {
4480 fn request(
4481 state: &mut Self,
4482 _: &Client,
4483 _: &WlDataOffer,
4484 request: <WlDataOffer as Resource>::Request,
4485 data: &DataOfferData,
4486 _: &DisplayHandle,
4487 _: &mut DataInit<'_, Self>,
4488 ) {
4489 use wl_data_offer::Request;
4490 match request {
4491 Request::Receive { mime_type, fd } => {
4492 if data.external {
4493 if let Some(ref cb) = state.external_clipboard
4495 && (cb.mime_type == mime_type
4496 || mime_type == "text/plain"
4497 || mime_type == "text/plain;charset=utf-8"
4498 || mime_type == "UTF8_STRING")
4499 {
4500 use std::io::Write;
4501 let mut f = std::fs::File::from(fd);
4502 let _ = f.write_all(&cb.data);
4503 }
4504 } else if let Some(ref src) = state.selection_source {
4505 src.send(mime_type, fd.as_fd());
4507 }
4508 }
4509 Request::Destroy => {}
4510 _ => {} }
4512 }
4513}
4514
4515impl Compositor {
4516 fn read_data_source_and_emit(&mut self, source: &WlDataSource, mime_type: &str) {
4519 let mut fds = [0i32; 2];
4520 if unsafe { libc::pipe(fds.as_mut_ptr()) } != 0 {
4521 return;
4522 }
4523 let read_fd = unsafe { OwnedFd::from_raw_fd(fds[0]) };
4524 let write_fd = unsafe { OwnedFd::from_raw_fd(fds[1]) };
4525 source.send(mime_type.to_string(), write_fd.as_fd());
4526 let _ = self.display_handle.flush_clients();
4527 unsafe {
4529 libc::fcntl(read_fd.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
4530 }
4531 std::thread::sleep(std::time::Duration::from_millis(5));
4533 let mut buf = Vec::new();
4534 let mut tmp = [0u8; 8192];
4535 loop {
4536 let n = unsafe {
4537 libc::read(
4538 read_fd.as_raw_fd(),
4539 tmp.as_mut_ptr() as *mut libc::c_void,
4540 tmp.len(),
4541 )
4542 };
4543 if n <= 0 {
4544 break;
4545 }
4546 buf.extend_from_slice(&tmp[..n as usize]);
4547 if buf.len() > 1024 * 1024 {
4548 break; }
4550 }
4551 if !buf.is_empty() {
4552 let _ = self.event_tx.send(CompositorEvent::ClipboardContent {
4553 mime_type: mime_type.to_string(),
4554 data: buf,
4555 });
4556 (self.event_notify)();
4557 }
4558 }
4559
4560 fn offer_external_clipboard(&mut self) {
4562 let Some(ref cb) = self.external_clipboard else {
4563 return;
4564 };
4565 let mime = cb.mime_type.clone();
4566 for dd in &self.data_devices {
4567 if let Some(client) = dd.client() {
4568 let offer = client
4569 .create_resource::<WlDataOffer, DataOfferData, Compositor>(
4570 &self.display_handle,
4571 dd.version(),
4572 DataOfferData { external: true },
4573 )
4574 .unwrap();
4575 dd.data_offer(&offer);
4576 offer.offer(mime.clone());
4577 if mime.starts_with("text/plain") {
4579 if mime != "text/plain" {
4580 offer.offer("text/plain".to_string());
4581 }
4582 if mime != "text/plain;charset=utf-8" {
4583 offer.offer("text/plain;charset=utf-8".to_string());
4584 }
4585 offer.offer("UTF8_STRING".to_string());
4586 }
4587 dd.selection(Some(&offer));
4588 }
4589 }
4590 let _ = self.display_handle.flush_clients();
4591 }
4592}
4593
4594impl GlobalDispatch<ZwpPrimarySelectionDeviceManagerV1, ()> for Compositor {
4597 fn bind(
4598 _: &mut Self,
4599 _: &DisplayHandle,
4600 _: &Client,
4601 resource: New<ZwpPrimarySelectionDeviceManagerV1>,
4602 _: &(),
4603 data_init: &mut DataInit<'_, Self>,
4604 ) {
4605 data_init.init(resource, ());
4606 }
4607}
4608
4609impl Dispatch<ZwpPrimarySelectionDeviceManagerV1, ()> for Compositor {
4610 fn request(
4611 state: &mut Self,
4612 _: &Client,
4613 _: &ZwpPrimarySelectionDeviceManagerV1,
4614 request: <ZwpPrimarySelectionDeviceManagerV1 as Resource>::Request,
4615 _: &(),
4616 _: &DisplayHandle,
4617 data_init: &mut DataInit<'_, Self>,
4618 ) {
4619 use zwp_primary_selection_device_manager_v1::Request;
4620 match request {
4621 Request::CreateSource { id } => {
4622 data_init.init(
4623 id,
4624 PrimarySourceData {
4625 mime_types: std::sync::Mutex::new(Vec::new()),
4626 },
4627 );
4628 }
4629 Request::GetDevice { id, seat: _ } => {
4630 let pd = data_init.init(id, ());
4631 state.primary_devices.push(pd);
4632 }
4633 Request::Destroy => {}
4634 _ => {}
4635 }
4636 }
4637}
4638
4639impl Dispatch<ZwpPrimarySelectionSourceV1, PrimarySourceData> for Compositor {
4640 fn request(
4641 _: &mut Self,
4642 _: &Client,
4643 _: &ZwpPrimarySelectionSourceV1,
4644 request: <ZwpPrimarySelectionSourceV1 as Resource>::Request,
4645 data: &PrimarySourceData,
4646 _: &DisplayHandle,
4647 _: &mut DataInit<'_, Self>,
4648 ) {
4649 use zwp_primary_selection_source_v1::Request;
4650 match request {
4651 Request::Offer { mime_type } => {
4652 data.mime_types.lock().unwrap().push(mime_type);
4653 }
4654 Request::Destroy => {}
4655 _ => {}
4656 }
4657 }
4658
4659 fn destroyed(
4660 state: &mut Self,
4661 _: wayland_server::backend::ClientId,
4662 resource: &ZwpPrimarySelectionSourceV1,
4663 _: &PrimarySourceData,
4664 ) {
4665 if state
4666 .primary_source
4667 .as_ref()
4668 .is_some_and(|s| s.id() == resource.id())
4669 {
4670 state.primary_source = None;
4671 }
4672 }
4673}
4674
4675impl Dispatch<ZwpPrimarySelectionDeviceV1, ()> for Compositor {
4676 fn request(
4677 state: &mut Self,
4678 _: &Client,
4679 _: &ZwpPrimarySelectionDeviceV1,
4680 request: <ZwpPrimarySelectionDeviceV1 as Resource>::Request,
4681 _: &(),
4682 _: &DisplayHandle,
4683 _: &mut DataInit<'_, Self>,
4684 ) {
4685 use zwp_primary_selection_device_v1::Request;
4686 match request {
4687 Request::SetSelection { source, serial: _ } => {
4688 state.primary_source = source;
4689 }
4690 Request::Destroy => {}
4691 _ => {}
4692 }
4693 }
4694
4695 fn destroyed(
4696 state: &mut Self,
4697 _: wayland_server::backend::ClientId,
4698 resource: &ZwpPrimarySelectionDeviceV1,
4699 _: &(),
4700 ) {
4701 state.primary_devices.retain(|d| d.id() != resource.id());
4702 }
4703}
4704
4705impl Dispatch<ZwpPrimarySelectionOfferV1, PrimaryOfferData> for Compositor {
4706 fn request(
4707 state: &mut Self,
4708 _: &Client,
4709 _: &ZwpPrimarySelectionOfferV1,
4710 request: <ZwpPrimarySelectionOfferV1 as Resource>::Request,
4711 data: &PrimaryOfferData,
4712 _: &DisplayHandle,
4713 _: &mut DataInit<'_, Self>,
4714 ) {
4715 use zwp_primary_selection_offer_v1::Request;
4716 match request {
4717 Request::Receive { mime_type, fd } => {
4718 if data.external {
4719 if let Some(ref cb) = state.external_primary {
4720 use std::io::Write;
4721 let mut f = std::fs::File::from(fd);
4722 let _ = f.write_all(&cb.data);
4723 let _ = mime_type; }
4725 } else if let Some(ref src) = state.primary_source {
4726 src.send(mime_type, fd.as_fd());
4727 }
4728 }
4729 Request::Destroy => {}
4730 _ => {}
4731 }
4732 }
4733}
4734
4735impl GlobalDispatch<ZwpPointerConstraintsV1, ()> for Compositor {
4738 fn bind(
4739 _: &mut Self,
4740 _: &DisplayHandle,
4741 _: &Client,
4742 resource: New<ZwpPointerConstraintsV1>,
4743 _: &(),
4744 data_init: &mut DataInit<'_, Self>,
4745 ) {
4746 data_init.init(resource, ());
4747 }
4748}
4749
4750impl Dispatch<ZwpPointerConstraintsV1, ()> for Compositor {
4751 fn request(
4752 _: &mut Self,
4753 _: &Client,
4754 _: &ZwpPointerConstraintsV1,
4755 request: <ZwpPointerConstraintsV1 as Resource>::Request,
4756 _: &(),
4757 _: &DisplayHandle,
4758 data_init: &mut DataInit<'_, Self>,
4759 ) {
4760 use zwp_pointer_constraints_v1::Request;
4761 match request {
4762 Request::LockPointer {
4763 id,
4764 surface: _,
4765 pointer: _,
4766 region: _,
4767 lifetime: _,
4768 } => {
4769 let lp = data_init.init(id, ());
4770 lp.locked();
4772 }
4773 Request::ConfinePointer {
4774 id,
4775 surface: _,
4776 pointer: _,
4777 region: _,
4778 lifetime: _,
4779 } => {
4780 let cp = data_init.init(id, ());
4781 cp.confined();
4782 }
4783 Request::Destroy => {}
4784 _ => {}
4785 }
4786 }
4787}
4788
4789impl Dispatch<ZwpLockedPointerV1, ()> for Compositor {
4790 fn request(
4791 _: &mut Self,
4792 _: &Client,
4793 _: &ZwpLockedPointerV1,
4794 _: <ZwpLockedPointerV1 as Resource>::Request,
4795 _: &(),
4796 _: &DisplayHandle,
4797 _: &mut DataInit<'_, Self>,
4798 ) {
4799 }
4801}
4802
4803impl Dispatch<ZwpConfinedPointerV1, ()> for Compositor {
4804 fn request(
4805 _: &mut Self,
4806 _: &Client,
4807 _: &ZwpConfinedPointerV1,
4808 _: <ZwpConfinedPointerV1 as Resource>::Request,
4809 _: &(),
4810 _: &DisplayHandle,
4811 _: &mut DataInit<'_, Self>,
4812 ) {
4813 }
4815}
4816
4817impl GlobalDispatch<ZwpRelativePointerManagerV1, ()> for Compositor {
4820 fn bind(
4821 _: &mut Self,
4822 _: &DisplayHandle,
4823 _: &Client,
4824 resource: New<ZwpRelativePointerManagerV1>,
4825 _: &(),
4826 data_init: &mut DataInit<'_, Self>,
4827 ) {
4828 data_init.init(resource, ());
4829 }
4830}
4831
4832impl Dispatch<ZwpRelativePointerManagerV1, ()> for Compositor {
4833 fn request(
4834 state: &mut Self,
4835 _: &Client,
4836 _: &ZwpRelativePointerManagerV1,
4837 request: <ZwpRelativePointerManagerV1 as Resource>::Request,
4838 _: &(),
4839 _: &DisplayHandle,
4840 data_init: &mut DataInit<'_, Self>,
4841 ) {
4842 use zwp_relative_pointer_manager_v1::Request;
4843 match request {
4844 Request::GetRelativePointer { id, pointer: _ } => {
4845 let rp = data_init.init(id, ());
4846 state.relative_pointers.push(rp);
4847 }
4848 Request::Destroy => {}
4849 _ => {}
4850 }
4851 }
4852}
4853
4854impl Dispatch<ZwpRelativePointerV1, ()> for Compositor {
4855 fn request(
4856 state: &mut Self,
4857 _: &Client,
4858 resource: &ZwpRelativePointerV1,
4859 _: <ZwpRelativePointerV1 as Resource>::Request,
4860 _: &(),
4861 _: &DisplayHandle,
4862 _: &mut DataInit<'_, Self>,
4863 ) {
4864 state
4866 .relative_pointers
4867 .retain(|rp| rp.id() != resource.id());
4868 }
4869}
4870
4871impl GlobalDispatch<ZwpTextInputManagerV3, ()> for Compositor {
4874 fn bind(
4875 _: &mut Self,
4876 _: &DisplayHandle,
4877 _: &Client,
4878 resource: New<ZwpTextInputManagerV3>,
4879 _: &(),
4880 data_init: &mut DataInit<'_, Self>,
4881 ) {
4882 data_init.init(resource, ());
4883 }
4884}
4885
4886impl Dispatch<ZwpTextInputManagerV3, ()> for Compositor {
4887 fn request(
4888 state: &mut Self,
4889 _: &Client,
4890 _: &ZwpTextInputManagerV3,
4891 request: <ZwpTextInputManagerV3 as Resource>::Request,
4892 _: &(),
4893 _: &DisplayHandle,
4894 data_init: &mut DataInit<'_, Self>,
4895 ) {
4896 use zwp_text_input_manager_v3::Request;
4897 match request {
4898 Request::GetTextInput { id, seat: _ } => {
4899 let ti = data_init.init(id, ());
4900 state.text_inputs.push(TextInputState {
4901 resource: ti,
4902 enabled: false,
4903 });
4904 }
4905 Request::Destroy => {}
4906 _ => {}
4907 }
4908 }
4909}
4910
4911impl Dispatch<ZwpTextInputV3, ()> for Compositor {
4912 fn request(
4913 state: &mut Self,
4914 _: &Client,
4915 resource: &ZwpTextInputV3,
4916 request: <ZwpTextInputV3 as Resource>::Request,
4917 _: &(),
4918 _: &DisplayHandle,
4919 _: &mut DataInit<'_, Self>,
4920 ) {
4921 use zwp_text_input_v3::Request;
4922 match request {
4923 Request::Enable => {
4924 if let Some(ti) = state
4925 .text_inputs
4926 .iter_mut()
4927 .find(|t| t.resource.id() == resource.id())
4928 {
4929 ti.enabled = true;
4930 }
4931 }
4932 Request::Disable => {
4933 if let Some(ti) = state
4934 .text_inputs
4935 .iter_mut()
4936 .find(|t| t.resource.id() == resource.id())
4937 {
4938 ti.enabled = false;
4939 }
4940 }
4941 Request::Commit => {
4942 }
4944 Request::Destroy => {
4945 state
4946 .text_inputs
4947 .retain(|t| t.resource.id() != resource.id());
4948 }
4949 _ => {}
4952 }
4953 }
4954}
4955
4956impl GlobalDispatch<XdgActivationV1, ()> for Compositor {
4959 fn bind(
4960 _: &mut Self,
4961 _: &DisplayHandle,
4962 _: &Client,
4963 resource: New<XdgActivationV1>,
4964 _: &(),
4965 data_init: &mut DataInit<'_, Self>,
4966 ) {
4967 data_init.init(resource, ());
4968 }
4969}
4970
4971impl Dispatch<XdgActivationV1, ()> for Compositor {
4972 fn request(
4973 state: &mut Self,
4974 _: &Client,
4975 _: &XdgActivationV1,
4976 request: <XdgActivationV1 as Resource>::Request,
4977 _: &(),
4978 _: &DisplayHandle,
4979 data_init: &mut DataInit<'_, Self>,
4980 ) {
4981 use xdg_activation_v1::Request;
4982 match request {
4983 Request::GetActivationToken { id } => {
4984 let serial = state.next_activation_token;
4985 state.next_activation_token = serial.wrapping_add(1);
4986 data_init.init(id, ActivationTokenData { serial });
4987 }
4988 Request::Activate {
4989 token: _,
4990 surface: _,
4991 } => {
4992 }
4995 Request::Destroy => {}
4996 _ => {}
4997 }
4998 }
4999}
5000
5001impl Dispatch<XdgActivationTokenV1, ActivationTokenData> for Compositor {
5002 fn request(
5003 _: &mut Self,
5004 _: &Client,
5005 resource: &XdgActivationTokenV1,
5006 request: <XdgActivationTokenV1 as Resource>::Request,
5007 data: &ActivationTokenData,
5008 _: &DisplayHandle,
5009 _: &mut DataInit<'_, Self>,
5010 ) {
5011 use xdg_activation_token_v1::Request;
5012 match request {
5013 Request::Commit => {
5014 resource.done(format!("blit-token-{}", data.serial));
5017 }
5018 Request::SetSerial { .. } | Request::SetAppId { .. } | Request::SetSurface { .. } => {}
5019 Request::Destroy => {}
5020 _ => {}
5021 }
5022 }
5023}
5024
5025impl GlobalDispatch<WpCursorShapeManagerV1, ()> for Compositor {
5028 fn bind(
5029 _: &mut Self,
5030 _: &DisplayHandle,
5031 _: &Client,
5032 resource: New<WpCursorShapeManagerV1>,
5033 _: &(),
5034 data_init: &mut DataInit<'_, Self>,
5035 ) {
5036 data_init.init(resource, ());
5037 }
5038}
5039
5040impl Dispatch<WpCursorShapeManagerV1, ()> for Compositor {
5041 fn request(
5042 _: &mut Self,
5043 _: &Client,
5044 _: &WpCursorShapeManagerV1,
5045 request: <WpCursorShapeManagerV1 as Resource>::Request,
5046 _: &(),
5047 _: &DisplayHandle,
5048 data_init: &mut DataInit<'_, Self>,
5049 ) {
5050 use wp_cursor_shape_manager_v1::Request;
5051 match request {
5052 Request::GetPointer {
5053 cursor_shape_device,
5054 pointer: _,
5055 } => {
5056 data_init.init(cursor_shape_device, ());
5057 }
5058 Request::GetTabletToolV2 {
5059 cursor_shape_device,
5060 tablet_tool: _,
5061 } => {
5062 data_init.init(cursor_shape_device, ());
5063 }
5064 Request::Destroy => {}
5065 _ => {}
5066 }
5067 }
5068}
5069
5070impl Dispatch<WpCursorShapeDeviceV1, ()> for Compositor {
5071 fn request(
5072 state: &mut Self,
5073 _: &Client,
5074 _: &WpCursorShapeDeviceV1,
5075 request: <WpCursorShapeDeviceV1 as Resource>::Request,
5076 _: &(),
5077 _: &DisplayHandle,
5078 _: &mut DataInit<'_, Self>,
5079 ) {
5080 use wp_cursor_shape_device_v1::Request;
5081 match request {
5082 Request::SetShape { serial: _, shape } => {
5083 use wayland_server::WEnum;
5084 use wp_cursor_shape_device_v1::Shape;
5085 let name = match shape {
5086 WEnum::Value(Shape::Default) => "default",
5087 WEnum::Value(Shape::ContextMenu) => "context-menu",
5088 WEnum::Value(Shape::Help) => "help",
5089 WEnum::Value(Shape::Pointer) => "pointer",
5090 WEnum::Value(Shape::Progress) => "progress",
5091 WEnum::Value(Shape::Wait) => "wait",
5092 WEnum::Value(Shape::Cell) => "cell",
5093 WEnum::Value(Shape::Crosshair) => "crosshair",
5094 WEnum::Value(Shape::Text) => "text",
5095 WEnum::Value(Shape::VerticalText) => "vertical-text",
5096 WEnum::Value(Shape::Alias) => "alias",
5097 WEnum::Value(Shape::Copy) => "copy",
5098 WEnum::Value(Shape::Move) => "move",
5099 WEnum::Value(Shape::NoDrop) => "no-drop",
5100 WEnum::Value(Shape::NotAllowed) => "not-allowed",
5101 WEnum::Value(Shape::Grab) => "grab",
5102 WEnum::Value(Shape::Grabbing) => "grabbing",
5103 WEnum::Value(Shape::EResize) => "e-resize",
5104 WEnum::Value(Shape::NResize) => "n-resize",
5105 WEnum::Value(Shape::NeResize) => "ne-resize",
5106 WEnum::Value(Shape::NwResize) => "nw-resize",
5107 WEnum::Value(Shape::SResize) => "s-resize",
5108 WEnum::Value(Shape::SeResize) => "se-resize",
5109 WEnum::Value(Shape::SwResize) => "sw-resize",
5110 WEnum::Value(Shape::WResize) => "w-resize",
5111 WEnum::Value(Shape::EwResize) => "ew-resize",
5112 WEnum::Value(Shape::NsResize) => "ns-resize",
5113 WEnum::Value(Shape::NeswResize) => "nesw-resize",
5114 WEnum::Value(Shape::NwseResize) => "nwse-resize",
5115 WEnum::Value(Shape::ColResize) => "col-resize",
5116 WEnum::Value(Shape::RowResize) => "row-resize",
5117 WEnum::Value(Shape::AllScroll) => "all-scroll",
5118 WEnum::Value(Shape::ZoomIn) => "zoom-in",
5119 WEnum::Value(Shape::ZoomOut) => "zoom-out",
5120 _ => "default",
5121 };
5122 let _ = state.event_tx.send(CompositorEvent::SurfaceCursor {
5123 surface_id: state.focused_surface_id,
5124 cursor: CursorImage::Named(name.to_string()),
5125 });
5126 (state.event_notify)();
5127 }
5128 Request::Destroy => {}
5129 _ => {}
5130 }
5131 }
5132}
5133
5134impl wayland_server::backend::ClientData for ClientState {
5136 fn initialized(&self, _: wayland_server::backend::ClientId) {}
5137 fn disconnected(
5138 &self,
5139 _: wayland_server::backend::ClientId,
5140 _: wayland_server::backend::DisconnectReason,
5141 ) {
5142 }
5143}
5144
5145pub struct CompositorHandle {
5150 pub event_rx: mpsc::Receiver<CompositorEvent>,
5151 pub command_tx: mpsc::Sender<CompositorCommand>,
5152 pub socket_name: String,
5153 pub thread: std::thread::JoinHandle<()>,
5154 pub shutdown: Arc<AtomicBool>,
5155 pub vulkan_video_encode: bool,
5157 pub vulkan_video_encode_av1: bool,
5159 loop_signal: LoopSignal,
5160}
5161
5162impl CompositorHandle {
5163 pub fn wake(&self) {
5164 self.loop_signal.wakeup();
5165 }
5166}
5167
5168pub fn spawn_compositor(
5169 verbose: bool,
5170 event_notify: Arc<dyn Fn() + Send + Sync>,
5171 gpu_device: &str,
5172) -> CompositorHandle {
5173 let _gpu_device = gpu_device.to_string();
5174 let (event_tx, event_rx) = mpsc::channel();
5175 let (command_tx, command_rx) = mpsc::channel();
5176 let (socket_tx, socket_rx) = mpsc::sync_channel(1);
5177 let (signal_tx, signal_rx) = mpsc::sync_channel::<LoopSignal>(1);
5178 let (caps_tx, caps_rx) = mpsc::sync_channel::<(bool, bool)>(1);
5179 let shutdown = Arc::new(AtomicBool::new(false));
5180 let shutdown_clone = shutdown.clone();
5181
5182 let runtime_dir = std::env::var_os("XDG_RUNTIME_DIR")
5183 .map(std::path::PathBuf::from)
5184 .filter(|p| {
5185 let probe = p.join(".blit-probe");
5186 if std::fs::write(&probe, b"").is_ok() {
5187 let _ = std::fs::remove_file(&probe);
5188 true
5189 } else {
5190 false
5191 }
5192 })
5193 .unwrap_or_else(std::env::temp_dir);
5194
5195 let runtime_dir_clone = runtime_dir.clone();
5196 let thread = std::thread::Builder::new()
5197 .name("compositor".into())
5198 .spawn(move || {
5199 unsafe { std::env::set_var("XDG_RUNTIME_DIR", &runtime_dir_clone) };
5200 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
5201 run_compositor(
5202 event_tx,
5203 command_rx,
5204 socket_tx,
5205 signal_tx,
5206 caps_tx,
5207 event_notify,
5208 shutdown_clone,
5209 verbose,
5210 _gpu_device,
5211 );
5212 }));
5213 if let Err(e) = result {
5214 let msg = if let Some(s) = e.downcast_ref::<&str>() {
5215 s.to_string()
5216 } else if let Some(s) = e.downcast_ref::<String>() {
5217 s.clone()
5218 } else {
5219 "unknown panic".to_string()
5220 };
5221 eprintln!("[compositor] PANIC: {msg}");
5222 }
5223 })
5224 .expect("failed to spawn compositor thread");
5225
5226 let socket_name = socket_rx.recv().expect("compositor failed to start");
5227 let socket_name = runtime_dir
5228 .join(&socket_name)
5229 .to_string_lossy()
5230 .into_owned();
5231 let loop_signal = signal_rx
5232 .recv()
5233 .expect("compositor failed to send loop signal");
5234 let (vulkan_video_encode, vulkan_video_encode_av1) = caps_rx.recv().unwrap_or((false, false));
5235
5236 CompositorHandle {
5237 event_rx,
5238 command_tx,
5239 socket_name,
5240 thread,
5241 shutdown,
5242 vulkan_video_encode,
5243 vulkan_video_encode_av1,
5244 loop_signal,
5245 }
5246}
5247
5248#[allow(clippy::too_many_arguments)]
5249fn run_compositor(
5250 event_tx: mpsc::Sender<CompositorEvent>,
5251 command_rx: mpsc::Receiver<CompositorCommand>,
5252 socket_tx: mpsc::SyncSender<String>,
5253 signal_tx: mpsc::SyncSender<LoopSignal>,
5254 caps_tx: mpsc::SyncSender<(bool, bool)>,
5255 event_notify: Arc<dyn Fn() + Send + Sync>,
5256 shutdown: Arc<AtomicBool>,
5257 verbose: bool,
5258 gpu_device: String,
5259) {
5260 let mut event_loop: EventLoop<Compositor> =
5261 EventLoop::try_new().expect("failed to create event loop");
5262 let loop_signal = event_loop.get_signal();
5263
5264 let display: Display<Compositor> = Display::new().expect("failed to create display");
5265 let dh = display.handle();
5266
5267 eprintln!("[compositor] trying Vulkan renderer for {gpu_device}");
5270 let vulkan_renderer = super::vulkan_render::VulkanRenderer::try_new(&gpu_device);
5271 let has_dmabuf = vulkan_renderer.as_ref().is_some_and(|vk| vk.has_dmabuf());
5272 eprintln!(
5273 "[compositor] Vulkan renderer: {} (dmabuf={})",
5274 vulkan_renderer.is_some(),
5275 has_dmabuf,
5276 );
5277
5278 dh.create_global::<Compositor, WlCompositor, ()>(6, ());
5280 dh.create_global::<Compositor, WlSubcompositor, ()>(1, ());
5281 dh.create_global::<Compositor, XdgWmBase, ()>(6, ());
5282 dh.create_global::<Compositor, WlShm, ()>(1, ());
5283 dh.create_global::<Compositor, WlOutput, ()>(4, ());
5284 dh.create_global::<Compositor, WlSeat, ()>(9, ());
5285 if has_dmabuf {
5290 dh.create_global::<Compositor, ZwpLinuxDmabufV1, ()>(4, ());
5291 }
5292 dh.create_global::<Compositor, WpViewporter, ()>(1, ());
5293 dh.create_global::<Compositor, WpFractionalScaleManagerV1, ()>(1, ());
5294 dh.create_global::<Compositor, ZxdgDecorationManagerV1, ()>(1, ());
5295 dh.create_global::<Compositor, WlDataDeviceManager, ()>(3, ());
5296 dh.create_global::<Compositor, ZwpPointerConstraintsV1, ()>(1, ());
5297 dh.create_global::<Compositor, ZwpRelativePointerManagerV1, ()>(1, ());
5298 dh.create_global::<Compositor, XdgActivationV1, ()>(1, ());
5299 dh.create_global::<Compositor, WpCursorShapeManagerV1, ()>(1, ());
5300 dh.create_global::<Compositor, ZwpPrimarySelectionDeviceManagerV1, ()>(1, ());
5301 dh.create_global::<Compositor, WpPresentation, ()>(1, ());
5302 dh.create_global::<Compositor, ZwpTextInputManagerV3, ()>(1, ());
5303
5304 let keymap_string = include_str!("../data/us-qwerty.xkb");
5306 let mut keymap_data = keymap_string.as_bytes().to_vec();
5307 keymap_data.push(0); let listening_socket = wayland_server::ListeningSocket::bind_auto("wayland", 0..33)
5311 .unwrap_or_else(|e| {
5312 let dir = std::env::var("XDG_RUNTIME_DIR").unwrap_or_else(|_| "(unset)".into());
5313 panic!("failed to create wayland socket in XDG_RUNTIME_DIR={dir}: {e}\nhint: ensure the directory exists and is writable by the current user");
5314 });
5315 let socket_name = listening_socket
5316 .socket_name()
5317 .unwrap()
5318 .to_string_lossy()
5319 .into_owned();
5320 socket_tx.send(socket_name).unwrap();
5321 let _ = signal_tx.send(loop_signal.clone());
5322
5323 let mut compositor = Compositor {
5324 display_handle: dh,
5325 surfaces: HashMap::new(),
5326 toplevel_surface_ids: HashMap::new(),
5327 next_surface_id: 1,
5328 shm_pools: HashMap::new(),
5329 surface_meta: HashMap::new(),
5330 dmabuf_params: HashMap::new(),
5331 vulkan_renderer,
5332 output_width: 1920,
5333 output_height: 1080,
5334 output_refresh_mhz: 60_000,
5335 output_scale_120: 120,
5336 outputs: Vec::new(),
5337 keyboards: Vec::new(),
5338 pointers: Vec::new(),
5339 keyboard_keymap_data: keymap_data,
5340 mods_depressed: 0,
5341 mods_locked: 0,
5342 serial: 0,
5343 event_tx,
5344 event_notify,
5345 loop_signal: loop_signal.clone(),
5346 pending_commits: HashMap::new(),
5347 focused_surface_id: 0,
5348 pointer_entered_id: None,
5349 pending_kb_reenter: false,
5350 gpu_device,
5351 verbose,
5352 shutdown: shutdown.clone(),
5353 last_reported_size: HashMap::new(),
5354 surface_sizes: HashMap::new(),
5355 positioners: HashMap::new(),
5356 fractional_scales: Vec::new(),
5357 data_devices: Vec::new(),
5358 selection_source: None,
5359 external_clipboard: None,
5360 primary_devices: Vec::new(),
5361 primary_source: None,
5362 external_primary: None,
5363 relative_pointers: Vec::new(),
5364 text_inputs: Vec::new(),
5365 text_input_serial: 0,
5366 next_activation_token: 1,
5367 popup_grab_stack: Vec::new(),
5368 held_buffers: HashMap::new(),
5369 cursor_rgba: HashMap::new(),
5370 };
5371
5372 {
5374 let (vve, vve_av1) = compositor
5375 .vulkan_renderer
5376 .as_ref()
5377 .map(|vk| (vk.has_video_encode(), vk.has_video_encode_av1()))
5378 .unwrap_or((false, false));
5379 let _ = caps_tx.send((vve, vve_av1));
5380 }
5381
5382 let handle = event_loop.handle();
5383
5384 let display_source = Generic::new(display, Interest::READ, calloop::Mode::Level);
5386 handle
5387 .insert_source(display_source, |_, display, state| {
5388 let d = unsafe { display.get_mut() };
5389 if let Err(e) = d.dispatch_clients(state)
5390 && state.verbose
5391 {
5392 eprintln!("[compositor] dispatch_clients error: {e}");
5393 }
5394 state.cleanup_dead_surfaces();
5395 if let Err(e) = d.flush_clients()
5396 && state.verbose
5397 {
5398 eprintln!("[compositor] flush_clients error: {e}");
5399 }
5400 Ok(PostAction::Continue)
5401 })
5402 .expect("failed to insert display source");
5403
5404 let socket_source = Generic::new(listening_socket, Interest::READ, calloop::Mode::Level);
5406 handle
5407 .insert_source(socket_source, |_, socket, state| {
5408 let ls = unsafe { socket.get_mut() };
5409 if let Some(client_stream) = ls.accept().ok().flatten()
5410 && let Err(e) = state
5411 .display_handle
5412 .insert_client(client_stream, Arc::new(ClientState))
5413 && state.verbose
5414 {
5415 eprintln!("[compositor] insert_client error: {e}");
5416 }
5417 Ok(PostAction::Continue)
5418 })
5419 .expect("failed to insert listening socket");
5420
5421 if verbose {
5422 eprintln!("[compositor] entering event loop");
5423 }
5424
5425 while !shutdown.load(Ordering::Relaxed) {
5426 while let Ok(cmd) = command_rx.try_recv() {
5428 match cmd {
5429 CompositorCommand::Shutdown => {
5430 shutdown.store(true, Ordering::Relaxed);
5431 return;
5432 }
5433 other => compositor.handle_command(other),
5434 }
5435 }
5436
5437 let poll_timeout = if compositor
5440 .vulkan_renderer
5441 .as_ref()
5442 .is_some_and(|vk| vk.has_pending())
5443 {
5444 std::time::Duration::from_millis(1)
5445 } else {
5446 std::time::Duration::from_secs(1)
5447 };
5448
5449 if let Err(e) = event_loop.dispatch(Some(poll_timeout), &mut compositor)
5450 && verbose
5451 {
5452 eprintln!("[compositor] event loop error: {e}");
5453 }
5454
5455 if let Some(ref mut vk) = compositor.vulkan_renderer
5459 && let Some((sid, w, h, pixels)) = vk.try_retire_pending()
5460 {
5461 let s120_u32 = (compositor.output_scale_120 as u32).max(120);
5462 let log_w = (w * 120).div_ceil(s120_u32);
5463 let log_h = (h * 120).div_ceil(s120_u32);
5464 compositor
5465 .pending_commits
5466 .insert(sid, (w, h, log_w, log_h, pixels));
5467 }
5468
5469 if !compositor.pending_commits.is_empty() {
5470 compositor.flush_pending_commits();
5471 }
5472
5473 if let Err(e) = compositor.display_handle.flush_clients()
5474 && verbose
5475 {
5476 eprintln!("[compositor] flush error: {e}");
5477 }
5478 }
5479
5480 if verbose {
5481 eprintln!("[compositor] event loop exited");
5482 }
5483}