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 self.selection_source = None;
2432 self.offer_external_clipboard();
2433 }
2434 CompositorCommand::Capture {
2435 surface_id,
2436 scale_120,
2437 reply,
2438 } => {
2439 let cap_s120 = if scale_120 > 0 {
2442 scale_120
2443 } else {
2444 self.output_scale_120
2445 };
2446 let result = if let Some(root_id) = self.toplevel_surface_ids.get(&surface_id) {
2447 if let Some(ref mut vk) = self.vulkan_renderer {
2448 vk.render_tree_sized(
2449 root_id,
2450 &self.surfaces,
2451 &self.surface_meta,
2452 cap_s120,
2453 None,
2454 surface_id,
2455 )
2456 .map(|(_sid, w, h, pixels)| {
2457 let rgba = pixels.to_rgba(w, h);
2458 (w, h, rgba)
2459 })
2460 } else {
2461 None
2462 }
2463 } else {
2464 None
2465 };
2466 let _ = reply.send(result);
2467 }
2468 CompositorCommand::RequestFrame { surface_id } => {
2469 self.fire_frame_callbacks_for_toplevel(surface_id);
2470 }
2471 CompositorCommand::ReleaseKeys { keycodes } => {
2472 let time = elapsed_ms();
2473 let focused_wl = self
2474 .toplevel_surface_ids
2475 .get(&self.focused_surface_id)
2476 .and_then(|root_id| self.surfaces.get(root_id))
2477 .map(|s| s.wl_surface.clone());
2478 for keycode in &keycodes {
2479 let serial = self.next_serial();
2480 for kb in &self.keyboards {
2481 if let Some(ref wl) = focused_wl
2482 && same_client(kb, wl)
2483 {
2484 kb.key(serial, time, *keycode, wl_keyboard::KeyState::Released);
2485 }
2486 }
2487 }
2488 for keycode in &keycodes {
2490 self.update_and_send_modifiers(*keycode, false);
2491 }
2492 let _ = self.display_handle.flush_clients();
2493 }
2494 CompositorCommand::ClipboardListMimes { reply } => {
2495 let mimes = self.collect_clipboard_mime_types();
2496 let _ = reply.send(mimes);
2497 }
2498 CompositorCommand::ClipboardGet { mime_type, reply } => {
2499 let data = self.get_clipboard_content(&mime_type);
2500 let _ = reply.send(data);
2501 }
2502 CompositorCommand::SetExternalOutputBuffers {
2503 surface_id,
2504 buffers,
2505 } => {
2506 if let Some(ref mut vk) = self.vulkan_renderer {
2507 vk.set_external_output_buffers(surface_id, buffers);
2508 }
2509 }
2510 CompositorCommand::SetRefreshRate { mhz } => {
2511 let diff = (mhz as i64 - self.output_refresh_mhz as i64).unsigned_abs();
2515 if diff > 2000 && mhz > 0 {
2516 self.output_refresh_mhz = mhz;
2517 let s120 = self.output_scale_120 as i32;
2518 let mode_w = self.output_width * s120 / 120;
2519 let mode_h = self.output_height * s120 / 120;
2520 for output in &self.outputs {
2521 output.mode(
2522 wl_output::Mode::Current | wl_output::Mode::Preferred,
2523 mode_w,
2524 mode_h,
2525 mhz as i32,
2526 );
2527 if output.version() >= 2 {
2528 output.done();
2529 }
2530 }
2531 let _ = self.display_handle.flush_clients();
2532 }
2533 }
2534 CompositorCommand::SetVulkanEncoder {
2535 surface_id,
2536 codec,
2537 qp,
2538 width,
2539 height,
2540 } => {
2541 if let Some(ref mut vk) = self.vulkan_renderer {
2542 vk.create_vulkan_encoder(surface_id, codec, qp, width, height);
2543 }
2544 }
2545 CompositorCommand::RequestVulkanKeyframe { surface_id } => {
2546 if let Some(ref mut vk) = self.vulkan_renderer {
2547 vk.request_encoder_keyframe(surface_id);
2548 }
2549 }
2550 CompositorCommand::DestroyVulkanEncoder { surface_id } => {
2551 if let Some(ref mut vk) = self.vulkan_renderer {
2552 vk.destroy_vulkan_encoder(surface_id);
2553 }
2554 }
2555 CompositorCommand::Shutdown => {
2556 self.shutdown.store(true, Ordering::Relaxed);
2557 self.loop_signal.stop();
2558 }
2559 }
2560 }
2561
2562 fn send_dmabuf_feedback(&self, fb: &ZwpLinuxDmabufFeedbackV1) {
2566 use std::os::unix::fs::MetadataExt;
2567
2568 let modifiers: &[(u32, u64)] = self
2570 .vulkan_renderer
2571 .as_ref()
2572 .map(|vk| vk.supported_dmabuf_modifiers.as_slice())
2573 .unwrap_or(&[]);
2574
2575 let entry_size = 16usize;
2577 let table_size = modifiers.len() * entry_size;
2578 let mut table_data = vec![0u8; table_size];
2579 for (i, &(fmt, modifier)) in modifiers.iter().enumerate() {
2580 let off = i * entry_size;
2581 table_data[off..off + 4].copy_from_slice(&fmt.to_ne_bytes());
2582 table_data[off + 8..off + 16].copy_from_slice(&modifier.to_ne_bytes());
2584 }
2585
2586 let name = c"dmabuf-feedback-table";
2588 let raw_fd = unsafe { libc::memfd_create(name.as_ptr(), libc::MFD_CLOEXEC) };
2589 if raw_fd < 0 {
2590 eprintln!("[compositor] memfd_create for dmabuf feedback failed");
2591 fb.done();
2592 return;
2593 }
2594 let table_fd = unsafe { OwnedFd::from_raw_fd(raw_fd) };
2595 if !table_data.is_empty() {
2596 use std::io::Write;
2597 let mut file = std::fs::File::from(table_fd.try_clone().unwrap());
2598 if file.write_all(&table_data).is_err() {
2599 eprintln!("[compositor] failed to write dmabuf feedback table");
2600 fb.done();
2601 return;
2602 }
2603 }
2604 fb.format_table(table_fd.as_fd(), table_size as u32);
2605
2606 let dev = std::fs::metadata(&self.gpu_device)
2608 .map(|m| m.rdev())
2609 .unwrap_or(0);
2610 let dev_bytes = dev.to_ne_bytes().to_vec();
2611 fb.main_device(dev_bytes.clone());
2612
2613 fb.tranche_target_device(dev_bytes);
2615
2616 let indices: Vec<u8> = (0..modifiers.len() as u16)
2618 .flat_map(|i| i.to_ne_bytes())
2619 .collect();
2620 fb.tranche_formats(indices);
2621
2622 fb.tranche_flags(
2623 wayland_protocols::wp::linux_dmabuf::zv1::server::zwp_linux_dmabuf_feedback_v1::TrancheFlags::empty(),
2624 );
2625 fb.tranche_done();
2626 fb.done();
2627 }
2628}
2629
2630impl Compositor {
2631 fn collect_clipboard_mime_types(&self) -> Vec<String> {
2633 if let Some(ref src) = self.selection_source {
2635 let data = src.data::<DataSourceData>().unwrap();
2636 return data.mime_types.lock().unwrap().clone();
2637 }
2638 if let Some(ref cb) = self.external_clipboard
2640 && !cb.mime_type.is_empty()
2641 {
2642 let mut mimes = vec![cb.mime_type.clone()];
2643 if cb.mime_type.starts_with("text/plain") {
2645 if cb.mime_type != "text/plain" {
2646 mimes.push("text/plain".to_string());
2647 }
2648 if cb.mime_type != "text/plain;charset=utf-8" {
2649 mimes.push("text/plain;charset=utf-8".to_string());
2650 }
2651 mimes.push("UTF8_STRING".to_string());
2652 }
2653 return mimes;
2654 }
2655 Vec::new()
2656 }
2657
2658 fn get_clipboard_content(&mut self, mime_type: &str) -> Option<Vec<u8>> {
2660 if let Some(ref cb) = self.external_clipboard
2662 && self.selection_source.is_none()
2663 {
2664 let matches = cb.mime_type == mime_type
2666 || (cb.mime_type.starts_with("text/plain")
2667 && (mime_type == "text/plain"
2668 || mime_type == "text/plain;charset=utf-8"
2669 || mime_type == "UTF8_STRING"));
2670 if matches {
2671 return Some(cb.data.clone());
2672 }
2673 return None;
2674 }
2675 if let Some(src) = self.selection_source.clone() {
2677 return self.read_data_source_sync(&src, mime_type);
2678 }
2679 None
2680 }
2681
2682 fn read_data_source_sync(&mut self, source: &WlDataSource, mime_type: &str) -> Option<Vec<u8>> {
2684 let mut fds = [0i32; 2];
2685 if unsafe { libc::pipe(fds.as_mut_ptr()) } != 0 {
2686 return None;
2687 }
2688 let read_fd = unsafe { OwnedFd::from_raw_fd(fds[0]) };
2689 let write_fd = unsafe { OwnedFd::from_raw_fd(fds[1]) };
2690 source.send(mime_type.to_string(), write_fd.as_fd());
2691 let _ = self.display_handle.flush_clients();
2692 drop(write_fd); unsafe {
2695 libc::fcntl(read_fd.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
2696 }
2697 std::thread::sleep(std::time::Duration::from_millis(5));
2698 let mut buf = Vec::new();
2699 let mut tmp = [0u8; 8192];
2700 loop {
2701 let n = unsafe {
2702 libc::read(
2703 read_fd.as_raw_fd(),
2704 tmp.as_mut_ptr() as *mut libc::c_void,
2705 tmp.len(),
2706 )
2707 };
2708 if n <= 0 {
2709 break;
2710 }
2711 buf.extend_from_slice(&tmp[..n as usize]);
2712 if buf.len() > 1024 * 1024 {
2713 break; }
2715 }
2716 if buf.is_empty() { None } else { Some(buf) }
2717 }
2718}
2719
2720fn monotonic_timespec() -> (i64, i64) {
2726 let mut ts = libc::timespec {
2727 tv_sec: 0,
2728 tv_nsec: 0,
2729 };
2730 unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts) };
2732 (ts.tv_sec, ts.tv_nsec)
2733}
2734
2735fn elapsed_ms() -> u32 {
2736 let (sec, nsec) = monotonic_timespec();
2741 (sec as u32)
2742 .wrapping_mul(1000)
2743 .wrapping_add(nsec as u32 / 1_000_000)
2744}
2745
2746fn same_client<R1: Resource, R2: Resource>(a: &R1, b: &R2) -> bool {
2748 match (a.client(), b.client()) {
2749 (Some(ca), Some(cb)) => ca.id() == cb.id(),
2750 _ => false,
2751 }
2752}
2753
2754fn yuv420_to_rgb(y: u8, u: u8, v: u8) -> [u8; 3] {
2755 let y = (y as i32 - 16).max(0);
2756 let u = u as i32 - 128;
2757 let v = v as i32 - 128;
2758 let r = ((298 * y + 409 * v + 128) >> 8).clamp(0, 255) as u8;
2759 let g = ((298 * y - 100 * u - 208 * v + 128) >> 8).clamp(0, 255) as u8;
2760 let b = ((298 * y + 516 * u + 128) >> 8).clamp(0, 255) as u8;
2761 [r, g, b]
2762}
2763
2764fn xdg_toplevel_states(states: &[xdg_toplevel::State]) -> Vec<u8> {
2766 let mut bytes = Vec::with_capacity(states.len() * 4);
2767 for state in states {
2768 bytes.extend_from_slice(&(*state as u32).to_ne_bytes());
2769 }
2770 bytes
2771}
2772
2773fn create_keymap_fd(keymap_data: &[u8]) -> Option<OwnedFd> {
2774 use std::io::Write;
2775 let name = c"blit-keymap";
2776 let raw_fd = unsafe { libc::memfd_create(name.as_ptr(), libc::MFD_CLOEXEC) };
2777 if raw_fd < 0 {
2778 return None;
2779 }
2780 let fd = unsafe { OwnedFd::from_raw_fd(raw_fd) };
2781 let mut file = std::fs::File::from(fd);
2782 file.write_all(keymap_data).ok()?;
2783 Some(file.into())
2784}
2785
2786impl GlobalDispatch<WlCompositor, ()> for Compositor {
2793 fn bind(
2794 _state: &mut Self,
2795 _handle: &DisplayHandle,
2796 _client: &Client,
2797 resource: New<WlCompositor>,
2798 _data: &(),
2799 data_init: &mut DataInit<'_, Self>,
2800 ) {
2801 data_init.init(resource, ());
2802 }
2803}
2804
2805impl Dispatch<WlCompositor, ()> for Compositor {
2806 fn request(
2807 state: &mut Self,
2808 _client: &Client,
2809 _resource: &WlCompositor,
2810 request: <WlCompositor as Resource>::Request,
2811 _data: &(),
2812 _dh: &DisplayHandle,
2813 data_init: &mut DataInit<'_, Self>,
2814 ) {
2815 use wayland_server::protocol::wl_compositor::Request;
2816 match request {
2817 Request::CreateSurface { id } => {
2818 let surface = data_init.init(id, ());
2819 let proto_id = surface.id();
2820 state.surfaces.insert(
2821 proto_id,
2822 Surface {
2823 surface_id: 0,
2824 wl_surface: surface,
2825 pending_buffer: None,
2826 pending_buffer_scale: 1,
2827 pending_damage: false,
2828 pending_frame_callbacks: Vec::new(),
2829 pending_presentation_feedbacks: Vec::new(),
2830 pending_opaque: false,
2831 buffer_scale: 1,
2832 is_opaque: false,
2833 parent_surface_id: None,
2834 pending_subsurface_position: None,
2835 subsurface_position: (0, 0),
2836 children: Vec::new(),
2837 xdg_surface: None,
2838 xdg_toplevel: None,
2839 xdg_popup: None,
2840 xdg_geometry: None,
2841 title: String::new(),
2842 app_id: String::new(),
2843 pending_viewport_destination: None,
2844 viewport_destination: None,
2845 is_cursor: false,
2846 cursor_hotspot: (0, 0),
2847 },
2848 );
2849 }
2850 Request::CreateRegion { id } => {
2851 data_init.init(id, ());
2852 }
2853 _ => {}
2854 }
2855 }
2856}
2857
2858impl Dispatch<WlSurface, ()> for Compositor {
2861 fn request(
2862 state: &mut Self,
2863 _client: &Client,
2864 resource: &WlSurface,
2865 request: <WlSurface as Resource>::Request,
2866 _data: &(),
2867 _dh: &DisplayHandle,
2868 data_init: &mut DataInit<'_, Self>,
2869 ) {
2870 use wayland_server::protocol::wl_surface::Request;
2871 let sid = resource.id();
2872 match request {
2873 Request::Attach { buffer, x: _, y: _ } => {
2874 if let Some(surf) = state.surfaces.get_mut(&sid) {
2875 surf.pending_buffer = buffer;
2876 }
2877 }
2878 Request::Damage { .. } | Request::DamageBuffer { .. } => {
2879 if let Some(surf) = state.surfaces.get_mut(&sid) {
2880 surf.pending_damage = true;
2881 }
2882 }
2883 Request::Frame { callback } => {
2884 let cb = data_init.init(callback, ());
2885 if let Some(surf) = state.surfaces.get_mut(&sid) {
2886 surf.pending_frame_callbacks.push(cb);
2887 }
2888 }
2889 Request::SetBufferScale { scale } => {
2890 if let Some(surf) = state.surfaces.get_mut(&sid) {
2891 surf.pending_buffer_scale = scale;
2892 }
2893 }
2894 Request::SetOpaqueRegion { region: _ } => {
2895 if let Some(surf) = state.surfaces.get_mut(&sid) {
2896 surf.pending_opaque = true;
2897 }
2898 }
2899 Request::SetInputRegion { .. } => {}
2900 Request::Commit => {
2901 let is_cursor = state.surfaces.get(&sid).is_some_and(|s| s.is_cursor);
2902 if is_cursor {
2903 state.handle_cursor_commit(&sid);
2904 } else {
2905 state.handle_surface_commit(&sid);
2906 }
2907 }
2908 Request::SetBufferTransform { .. } => {}
2909 Request::Offset { .. } => {}
2910 Request::Destroy => {
2911 state.surface_meta.remove(&sid);
2912 state.cursor_rgba.remove(&sid);
2913 if let Some(ref mut vk) = state.vulkan_renderer {
2914 vk.remove_surface(&sid);
2915 }
2916 if let Some(held) = state.held_buffers.remove(&sid) {
2917 held.release();
2918 }
2919 if let Some(parent_id) = state
2920 .surfaces
2921 .get(&sid)
2922 .and_then(|s| s.parent_surface_id.clone())
2923 && let Some(parent) = state.surfaces.get_mut(&parent_id)
2924 {
2925 parent.children.retain(|c| *c != sid);
2926 }
2927 if let Some(surf) = state.surfaces.remove(&sid) {
2928 for fb in surf.pending_presentation_feedbacks {
2929 fb.discarded();
2930 }
2931 if surf.surface_id > 0 {
2932 state.toplevel_surface_ids.remove(&surf.surface_id);
2933 state.last_reported_size.remove(&surf.surface_id);
2934 state.surface_sizes.remove(&surf.surface_id);
2935 let _ = state.event_tx.send(CompositorEvent::SurfaceDestroyed {
2936 surface_id: surf.surface_id,
2937 });
2938 (state.event_notify)();
2939 }
2940 }
2941 }
2942 _ => {}
2943 }
2944 }
2945}
2946
2947impl Dispatch<WlCallback, ()> for Compositor {
2949 fn request(
2950 _: &mut Self,
2951 _: &Client,
2952 _: &WlCallback,
2953 _: <WlCallback as Resource>::Request,
2954 _: &(),
2955 _: &DisplayHandle,
2956 _: &mut DataInit<'_, Self>,
2957 ) {
2958 }
2959}
2960
2961impl GlobalDispatch<WpPresentation, ()> for Compositor {
2963 fn bind(
2964 _: &mut Self,
2965 _: &DisplayHandle,
2966 _: &Client,
2967 resource: New<WpPresentation>,
2968 _: &(),
2969 data_init: &mut DataInit<'_, Self>,
2970 ) {
2971 let pres = data_init.init(resource, ());
2972 pres.clock_id(libc::CLOCK_MONOTONIC as u32);
2974 }
2975}
2976
2977impl Dispatch<WpPresentation, ()> for Compositor {
2978 fn request(
2979 state: &mut Self,
2980 _: &Client,
2981 _: &WpPresentation,
2982 request: <WpPresentation as Resource>::Request,
2983 _: &(),
2984 _: &DisplayHandle,
2985 data_init: &mut DataInit<'_, Self>,
2986 ) {
2987 use wp_presentation::Request;
2988 match request {
2989 Request::Feedback { surface, callback } => {
2990 let fb = data_init.init(callback, ());
2991 let sid = surface.id();
2992 if let Some(surf) = state.surfaces.get_mut(&sid) {
2993 surf.pending_presentation_feedbacks.push(fb);
2994 }
2995 }
2996 Request::Destroy => {}
2997 _ => {}
2998 }
2999 }
3000}
3001
3002impl Dispatch<WpPresentationFeedback, ()> for Compositor {
3004 fn request(
3005 _: &mut Self,
3006 _: &Client,
3007 _: &WpPresentationFeedback,
3008 _: <WpPresentationFeedback as Resource>::Request,
3009 _: &(),
3010 _: &DisplayHandle,
3011 _: &mut DataInit<'_, Self>,
3012 ) {
3013 }
3014}
3015
3016impl Dispatch<WlRegion, ()> for Compositor {
3018 fn request(
3019 _: &mut Self,
3020 _: &Client,
3021 _: &WlRegion,
3022 _: <WlRegion as Resource>::Request,
3023 _: &(),
3024 _: &DisplayHandle,
3025 _: &mut DataInit<'_, Self>,
3026 ) {
3027 }
3028}
3029
3030impl GlobalDispatch<WlSubcompositor, ()> for Compositor {
3032 fn bind(
3033 _: &mut Self,
3034 _: &DisplayHandle,
3035 _: &Client,
3036 resource: New<WlSubcompositor>,
3037 _: &(),
3038 data_init: &mut DataInit<'_, Self>,
3039 ) {
3040 data_init.init(resource, ());
3041 }
3042}
3043
3044impl Dispatch<WlSubcompositor, ()> for Compositor {
3045 fn request(
3046 state: &mut Self,
3047 _: &Client,
3048 _: &WlSubcompositor,
3049 request: <WlSubcompositor as Resource>::Request,
3050 _: &(),
3051 _: &DisplayHandle,
3052 data_init: &mut DataInit<'_, Self>,
3053 ) {
3054 use wayland_server::protocol::wl_subcompositor::Request;
3055 match request {
3056 Request::GetSubsurface {
3057 id,
3058 surface,
3059 parent,
3060 } => {
3061 let child_id = surface.id();
3062 let parent_id = parent.id();
3063 data_init.init(
3064 id,
3065 SubsurfaceData {
3066 wl_surface_id: child_id.clone(),
3067 parent_surface_id: parent_id.clone(),
3068 },
3069 );
3070 if let Some(surf) = state.surfaces.get_mut(&child_id) {
3071 surf.parent_surface_id = Some(parent_id.clone());
3072 }
3073 if let Some(parent_surf) = state.surfaces.get_mut(&parent_id)
3074 && !parent_surf.children.contains(&child_id)
3075 {
3076 parent_surf.children.push(child_id);
3077 }
3078 }
3079 Request::Destroy => {}
3080 _ => {}
3081 }
3082 }
3083}
3084
3085impl Dispatch<WlSubsurface, SubsurfaceData> for Compositor {
3087 fn request(
3088 state: &mut Self,
3089 _: &Client,
3090 _: &WlSubsurface,
3091 request: <WlSubsurface as Resource>::Request,
3092 data: &SubsurfaceData,
3093 _: &DisplayHandle,
3094 _: &mut DataInit<'_, Self>,
3095 ) {
3096 use wayland_server::protocol::wl_subsurface::Request;
3097 match request {
3098 Request::SetPosition { x, y } => {
3099 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3100 surf.pending_subsurface_position = Some((x, y));
3101 }
3102 }
3103 Request::PlaceAbove { sibling } => {
3104 let sibling_id = sibling.id();
3105 if let Some(parent) = state.surfaces.get_mut(&data.parent_surface_id) {
3106 let child_id = &data.wl_surface_id;
3107 parent.children.retain(|c| c != child_id);
3108 let pos = parent
3109 .children
3110 .iter()
3111 .position(|c| *c == sibling_id)
3112 .map(|p| p + 1)
3113 .unwrap_or(parent.children.len());
3114 parent.children.insert(pos, child_id.clone());
3115 }
3116 }
3117 Request::PlaceBelow { sibling } => {
3118 let sibling_id = sibling.id();
3119 if let Some(parent) = state.surfaces.get_mut(&data.parent_surface_id) {
3120 let child_id = &data.wl_surface_id;
3121 parent.children.retain(|c| c != child_id);
3122 let pos = parent
3123 .children
3124 .iter()
3125 .position(|c| *c == sibling_id)
3126 .unwrap_or(0);
3127 parent.children.insert(pos, child_id.clone());
3128 }
3129 }
3130 Request::SetSync | Request::SetDesync => {}
3131 Request::Destroy => {
3132 let child_id = &data.wl_surface_id;
3133 if let Some(parent) = state.surfaces.get_mut(&data.parent_surface_id) {
3134 parent.children.retain(|c| c != child_id);
3135 }
3136 if let Some(surf) = state.surfaces.get_mut(child_id) {
3137 surf.parent_surface_id = None;
3138 }
3139 }
3140 _ => {}
3141 }
3142 }
3143}
3144
3145impl GlobalDispatch<XdgWmBase, ()> for Compositor {
3147 fn bind(
3148 _: &mut Self,
3149 _: &DisplayHandle,
3150 _: &Client,
3151 resource: New<XdgWmBase>,
3152 _: &(),
3153 data_init: &mut DataInit<'_, Self>,
3154 ) {
3155 data_init.init(resource, ());
3156 }
3157}
3158
3159impl Dispatch<XdgWmBase, ()> for Compositor {
3160 fn request(
3161 state: &mut Self,
3162 _: &Client,
3163 _: &XdgWmBase,
3164 request: <XdgWmBase as Resource>::Request,
3165 _: &(),
3166 _: &DisplayHandle,
3167 data_init: &mut DataInit<'_, Self>,
3168 ) {
3169 use xdg_wm_base::Request;
3170 match request {
3171 Request::GetXdgSurface { id, surface } => {
3172 let wl_surface_id = surface.id();
3173 let xdg_surface = data_init.init(
3174 id,
3175 XdgSurfaceData {
3176 wl_surface_id: wl_surface_id.clone(),
3177 },
3178 );
3179 if let Some(surf) = state.surfaces.get_mut(&wl_surface_id) {
3180 surf.xdg_surface = Some(xdg_surface);
3181 }
3182 }
3183 Request::CreatePositioner { id } => {
3184 let positioner = data_init.init(id, ());
3185 let pos_id = positioner.id();
3186 state.positioners.insert(
3187 pos_id,
3188 PositionerState {
3189 resource: positioner,
3190 geometry: PositionerGeometry {
3191 size: (0, 0),
3192 anchor_rect: (0, 0, 0, 0),
3193 anchor: 0,
3194 gravity: 0,
3195 constraint_adjustment: 0,
3196 offset: (0, 0),
3197 },
3198 },
3199 );
3200 }
3201 Request::Pong { .. } => {}
3202 Request::Destroy => {}
3203 _ => {}
3204 }
3205 }
3206}
3207
3208impl Dispatch<XdgSurface, XdgSurfaceData> for Compositor {
3210 fn request(
3211 state: &mut Self,
3212 _: &Client,
3213 resource: &XdgSurface,
3214 request: <XdgSurface as Resource>::Request,
3215 data: &XdgSurfaceData,
3216 _: &DisplayHandle,
3217 data_init: &mut DataInit<'_, Self>,
3218 ) {
3219 use xdg_surface::Request;
3220 match request {
3221 Request::GetToplevel { id } => {
3222 let toplevel = data_init.init(
3223 id,
3224 XdgToplevelData {
3225 wl_surface_id: data.wl_surface_id.clone(),
3226 },
3227 );
3228 let surface_id = state.allocate_surface_id();
3229 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3230 surf.xdg_toplevel = Some(toplevel.clone());
3231 surf.surface_id = surface_id;
3232 }
3233 state
3234 .toplevel_surface_ids
3235 .insert(surface_id, data.wl_surface_id.clone());
3236
3237 let (cw, ch) = state
3242 .surface_sizes
3243 .get(&surface_id)
3244 .copied()
3245 .unwrap_or((state.output_width, state.output_height));
3246 let states = xdg_toplevel_states(&[
3247 xdg_toplevel::State::Activated,
3248 xdg_toplevel::State::Maximized,
3249 ]);
3250 toplevel.configure(cw, ch, states);
3251 let serial = state.next_serial();
3252 resource.configure(serial);
3253
3254 state.set_keyboard_focus(surface_id);
3257 if let Some(surf) = state.surfaces.get(&data.wl_surface_id) {
3260 for output in &state.outputs {
3261 if same_client(output, &surf.wl_surface) {
3262 surf.wl_surface.enter(output);
3263 }
3264 }
3265 }
3266 let _ = state.display_handle.flush_clients();
3267
3268 let _ = state.event_tx.send(CompositorEvent::SurfaceCreated {
3269 surface_id,
3270 title: String::new(),
3271 app_id: String::new(),
3272 parent_id: 0,
3273 width: 0,
3274 height: 0,
3275 });
3276 (state.event_notify)();
3277 if state.verbose {
3278 eprintln!("[compositor] new_toplevel sid={surface_id}");
3279 }
3280 }
3281 Request::GetPopup {
3282 id,
3283 parent,
3284 positioner,
3285 } => {
3286 let popup = data_init.init(
3287 id,
3288 XdgPopupData {
3289 wl_surface_id: data.wl_surface_id.clone(),
3290 },
3291 );
3292
3293 let parent_wl_id: Option<ObjectId> = parent
3296 .as_ref()
3297 .and_then(|p| p.data::<XdgSurfaceData>())
3298 .map(|d| d.wl_surface_id.clone());
3299
3300 let parent_geom_offset = parent_wl_id
3305 .as_ref()
3306 .and_then(|pid| state.surfaces.get(pid))
3307 .and_then(|s| s.xdg_geometry)
3308 .map(|(gx, gy, _, _)| (gx, gy))
3309 .unwrap_or((0, 0));
3310
3311 let parent_abs = parent_wl_id
3316 .as_ref()
3317 .map(|pid| {
3318 let abs = state.surface_absolute_position(pid);
3319 (abs.0 + parent_geom_offset.0, abs.1 + parent_geom_offset.1)
3320 })
3321 .unwrap_or((0, 0));
3322 let (_, toplevel_root) = parent_wl_id
3325 .as_ref()
3326 .map(|pid| state.find_toplevel_root(pid))
3327 .unwrap_or_else(|| {
3328 (data.wl_surface_id.clone(), None)
3330 });
3331 let bounds = toplevel_root
3332 .and_then(|_| {
3333 let root_wl_id = parent_wl_id.as_ref().map(|pid| {
3334 let (rid, _) = state.find_toplevel_root(pid);
3335 rid
3336 })?;
3337 let surf = state.surfaces.get(&root_wl_id)?;
3338 if let Some((gx, gy, gw, gh)) = surf.xdg_geometry
3339 && gw > 0
3340 && gh > 0
3341 {
3342 return Some((gx, gy, gw, gh));
3343 }
3344
3345 let sm = state.surface_meta.get(&root_wl_id)?;
3348 let s = (sm.scale).max(1);
3349 let (lw, lh) = surf
3350 .viewport_destination
3351 .filter(|&(dw, dh)| dw > 0 && dh > 0)
3352 .unwrap_or((sm.width as i32 / s, sm.height as i32 / s));
3353 Some((0, 0, lw, lh))
3354 })
3355 .unwrap_or((0, 0, state.output_width, state.output_height));
3356
3357 eprintln!(
3358 "[popup] parent_abs={parent_abs:?} bounds={bounds:?} parent_wl={parent_wl_id:?} geom_off={parent_geom_offset:?}"
3359 );
3360 let pos_id = positioner.id();
3362 let (px, py, pw, ph) = state
3363 .positioners
3364 .get(&pos_id)
3365 .map(|p| p.geometry.compute_position(parent_abs, bounds))
3366 .unwrap_or((0, 0, 200, 200));
3367 eprintln!("[popup] result=({px},{py},{pw},{ph})");
3368
3369 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3370 surf.xdg_popup = Some(popup.clone());
3371 surf.parent_surface_id = parent_wl_id.clone();
3372 surf.subsurface_position =
3377 (parent_geom_offset.0 + px, parent_geom_offset.1 + py);
3378 }
3379 if let Some(ref parent_id) = parent_wl_id
3380 && let Some(parent_surf) = state.surfaces.get_mut(parent_id)
3381 && !parent_surf.children.contains(&data.wl_surface_id)
3382 {
3383 parent_surf.children.push(data.wl_surface_id.clone());
3384 }
3385
3386 popup.configure(px, py, pw, ph);
3387 let serial = state.next_serial();
3388 resource.configure(serial);
3389 let _ = state.display_handle.flush_clients();
3390 }
3391 Request::SetWindowGeometry {
3392 x,
3393 y,
3394 width,
3395 height,
3396 } => {
3397 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3398 if surf.xdg_popup.is_some() {
3406 let (old_gx, old_gy) = surf
3407 .xdg_geometry
3408 .map(|(gx, gy, _, _)| (gx, gy))
3409 .unwrap_or((0, 0));
3410 surf.subsurface_position.0 += old_gx - x;
3411 surf.subsurface_position.1 += old_gy - y;
3412 }
3413 surf.xdg_geometry = Some((x, y, width, height));
3414 }
3415 }
3416 Request::AckConfigure { .. } => {}
3417 Request::Destroy => {}
3418 _ => {}
3419 }
3420 }
3421}
3422
3423impl Dispatch<XdgToplevel, XdgToplevelData> for Compositor {
3425 fn request(
3426 state: &mut Self,
3427 _: &Client,
3428 _: &XdgToplevel,
3429 request: <XdgToplevel as Resource>::Request,
3430 data: &XdgToplevelData,
3431 _: &DisplayHandle,
3432 _: &mut DataInit<'_, Self>,
3433 ) {
3434 use xdg_toplevel::Request;
3435 match request {
3436 Request::SetTitle { title } => {
3437 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id)
3438 && surf.title != title
3439 {
3440 surf.title = title.clone();
3441 if surf.surface_id > 0 {
3442 let _ = state.event_tx.send(CompositorEvent::SurfaceTitle {
3443 surface_id: surf.surface_id,
3444 title,
3445 });
3446 (state.event_notify)();
3447 }
3448 }
3449 }
3450 Request::SetAppId { app_id } => {
3451 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id)
3452 && surf.app_id != app_id
3453 {
3454 surf.app_id = app_id.clone();
3455 if surf.surface_id > 0 {
3456 let _ = state.event_tx.send(CompositorEvent::SurfaceAppId {
3457 surface_id: surf.surface_id,
3458 app_id,
3459 });
3460 (state.event_notify)();
3461 }
3462 }
3463 }
3464 Request::Destroy => {
3465 let wl_surface_id = &data.wl_surface_id;
3466 state.surface_meta.remove(wl_surface_id);
3467 state.cursor_rgba.remove(wl_surface_id);
3468 if let Some(ref mut vk) = state.vulkan_renderer {
3469 vk.remove_surface(wl_surface_id);
3470 }
3471 if let Some(held) = state.held_buffers.remove(wl_surface_id) {
3472 held.release();
3473 }
3474 if let Some(surf) = state.surfaces.get_mut(wl_surface_id) {
3475 let sid = surf.surface_id;
3476 surf.xdg_toplevel = None;
3477 if sid > 0 {
3478 state.toplevel_surface_ids.remove(&sid);
3479 state.last_reported_size.remove(&sid);
3480 state.surface_sizes.remove(&sid);
3481 let _ = state
3482 .event_tx
3483 .send(CompositorEvent::SurfaceDestroyed { surface_id: sid });
3484 (state.event_notify)();
3485 surf.surface_id = 0;
3486 }
3487 }
3488 }
3489 _ => {}
3490 }
3491 }
3492}
3493
3494impl Dispatch<XdgPopup, XdgPopupData> for Compositor {
3496 fn request(
3497 state: &mut Self,
3498 _: &Client,
3499 _: &XdgPopup,
3500 request: <XdgPopup as Resource>::Request,
3501 data: &XdgPopupData,
3502 _: &DisplayHandle,
3503 _: &mut DataInit<'_, Self>,
3504 ) {
3505 use xdg_popup::Request;
3506 match request {
3507 Request::Grab { seat: _, serial: _ } => {
3508 state
3511 .popup_grab_stack
3512 .retain(|id| *id != data.wl_surface_id);
3513 state.popup_grab_stack.push(data.wl_surface_id.clone());
3514 }
3515 Request::Reposition { positioner, token } => {
3516 let pos_id = positioner.id();
3518 if let Some(surf) = state.surfaces.get(&data.wl_surface_id)
3519 && let Some(parent_id) = surf.parent_surface_id.clone()
3520 {
3521 let parent_geom_offset = state
3522 .surfaces
3523 .get(&parent_id)
3524 .and_then(|s| s.xdg_geometry)
3525 .map(|(gx, gy, _, _)| (gx, gy))
3526 .unwrap_or((0, 0));
3527 let parent_abs = {
3528 let abs = state.surface_absolute_position(&parent_id);
3529 (abs.0 + parent_geom_offset.0, abs.1 + parent_geom_offset.1)
3530 };
3531 let (root_id, toplevel_root) = state.find_toplevel_root(&parent_id);
3532 let bounds = toplevel_root
3533 .and_then(|_| {
3534 let surf = state.surfaces.get(&root_id)?;
3535 if let Some((gx, gy, gw, gh)) = surf.xdg_geometry
3536 && gw > 0
3537 && gh > 0
3538 {
3539 return Some((gx, gy, gw, gh));
3540 }
3541 let sm = state.surface_meta.get(&root_id)?;
3542 let s = (sm.scale).max(1);
3543 let (lw, lh) = surf
3544 .viewport_destination
3545 .filter(|&(dw, dh)| dw > 0 && dh > 0)
3546 .unwrap_or((sm.width as i32 / s, sm.height as i32 / s));
3547 Some((0, 0, lw, lh))
3548 })
3549 .unwrap_or((0, 0, state.output_width, state.output_height));
3550 let (px, py, pw, ph) = state
3551 .positioners
3552 .get(&pos_id)
3553 .map(|p| p.geometry.compute_position(parent_abs, bounds))
3554 .unwrap_or((0, 0, 200, 200));
3555 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3556 let old_gx = surf.xdg_geometry.map(|(gx, _, _, _)| gx).unwrap_or(0);
3559 let old_gy = surf.xdg_geometry.map(|(_, gy, _, _)| gy).unwrap_or(0);
3560 surf.subsurface_position = (
3561 parent_geom_offset.0 + px - old_gx,
3562 parent_geom_offset.1 + py - old_gy,
3563 );
3564 if let Some(ref popup) = surf.xdg_popup {
3565 popup.configure(px, py, pw, ph);
3566 popup.repositioned(token);
3567 }
3568 if let Some(ref xs) = surf.xdg_surface {
3569 let serial = state.serial.wrapping_add(1);
3570 state.serial = serial;
3571 xs.configure(serial);
3572 }
3573 }
3574 }
3575 }
3576 Request::Destroy => {
3577 state
3579 .popup_grab_stack
3580 .retain(|id| *id != data.wl_surface_id);
3581 if let Some(parent_id) = state
3583 .surfaces
3584 .get(&data.wl_surface_id)
3585 .and_then(|s| s.parent_surface_id.clone())
3586 && let Some(parent) = state.surfaces.get_mut(&parent_id)
3587 {
3588 parent.children.retain(|c| *c != data.wl_surface_id);
3589 }
3590 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3591 surf.xdg_popup = None;
3592 surf.parent_surface_id = None;
3593 }
3594 }
3595 _ => {}
3596 }
3597 }
3598}
3599
3600use wayland_protocols::xdg::shell::server::xdg_positioner;
3602impl Dispatch<XdgPositioner, ()> for Compositor {
3603 fn request(
3604 state: &mut Self,
3605 _: &Client,
3606 resource: &XdgPositioner,
3607 request: <XdgPositioner as Resource>::Request,
3608 _: &(),
3609 _: &DisplayHandle,
3610 _: &mut DataInit<'_, Self>,
3611 ) {
3612 use xdg_positioner::Request;
3613 let pos_id = resource.id();
3614 let Some(pos) = state.positioners.get_mut(&pos_id) else {
3615 return;
3616 };
3617 match request {
3618 Request::SetSize { width, height } => {
3619 pos.geometry.size = (width, height);
3620 }
3621 Request::SetAnchorRect {
3622 x,
3623 y,
3624 width,
3625 height,
3626 } => {
3627 pos.geometry.anchor_rect = (x, y, width, height);
3628 }
3629 Request::SetAnchor {
3630 anchor: wayland_server::WEnum::Value(v),
3631 } => {
3632 pos.geometry.anchor = v as u32;
3633 }
3634 Request::SetGravity {
3635 gravity: wayland_server::WEnum::Value(v),
3636 } => {
3637 pos.geometry.gravity = v as u32;
3638 }
3639 Request::SetOffset { x, y } => {
3640 pos.geometry.offset = (x, y);
3641 }
3642 Request::SetConstraintAdjustment {
3643 constraint_adjustment,
3644 } => {
3645 pos.geometry.constraint_adjustment = constraint_adjustment.into();
3646 }
3647 Request::Destroy => {
3648 state.positioners.remove(&pos_id);
3649 }
3650 _ => {}
3651 }
3652 }
3653}
3654
3655impl GlobalDispatch<ZxdgDecorationManagerV1, ()> for Compositor {
3657 fn bind(
3658 _: &mut Self,
3659 _: &DisplayHandle,
3660 _: &Client,
3661 resource: New<ZxdgDecorationManagerV1>,
3662 _: &(),
3663 data_init: &mut DataInit<'_, Self>,
3664 ) {
3665 data_init.init(resource, ());
3666 }
3667}
3668
3669impl Dispatch<ZxdgDecorationManagerV1, ()> for Compositor {
3670 fn request(
3671 _: &mut Self,
3672 _: &Client,
3673 _: &ZxdgDecorationManagerV1,
3674 request: <ZxdgDecorationManagerV1 as Resource>::Request,
3675 _: &(),
3676 _: &DisplayHandle,
3677 data_init: &mut DataInit<'_, Self>,
3678 ) {
3679 use zxdg_decoration_manager_v1::Request;
3680 match request {
3681 Request::GetToplevelDecoration { id, toplevel: _ } => {
3682 let decoration = data_init.init(id, ());
3683 decoration.configure(zxdg_toplevel_decoration_v1::Mode::ServerSide);
3685 }
3686 Request::Destroy => {}
3687 _ => {}
3688 }
3689 }
3690}
3691
3692impl Dispatch<ZxdgToplevelDecorationV1, ()> for Compositor {
3693 fn request(
3694 _: &mut Self,
3695 _: &Client,
3696 resource: &ZxdgToplevelDecorationV1,
3697 request: <ZxdgToplevelDecorationV1 as Resource>::Request,
3698 _: &(),
3699 _: &DisplayHandle,
3700 _: &mut DataInit<'_, Self>,
3701 ) {
3702 use zxdg_toplevel_decoration_v1::Request;
3703 match request {
3704 Request::SetMode { .. } | Request::UnsetMode => {
3705 resource.configure(zxdg_toplevel_decoration_v1::Mode::ServerSide);
3706 }
3707 Request::Destroy => {}
3708 _ => {}
3709 }
3710 }
3711}
3712
3713impl GlobalDispatch<WlShm, ()> for Compositor {
3715 fn bind(
3716 _: &mut Self,
3717 _: &DisplayHandle,
3718 _: &Client,
3719 resource: New<WlShm>,
3720 _: &(),
3721 data_init: &mut DataInit<'_, Self>,
3722 ) {
3723 let shm = data_init.init(resource, ());
3724 shm.format(wl_shm::Format::Argb8888);
3725 shm.format(wl_shm::Format::Xrgb8888);
3726 shm.format(wl_shm::Format::Abgr8888);
3727 shm.format(wl_shm::Format::Xbgr8888);
3728 }
3729}
3730
3731impl Dispatch<WlShm, ()> for Compositor {
3732 fn request(
3733 state: &mut Self,
3734 _: &Client,
3735 _: &WlShm,
3736 request: <WlShm as Resource>::Request,
3737 _: &(),
3738 _: &DisplayHandle,
3739 data_init: &mut DataInit<'_, Self>,
3740 ) {
3741 use wayland_server::protocol::wl_shm::Request;
3742 if let Request::CreatePool { id, fd, size } = request {
3743 let pool = data_init.init(id, ());
3744 let pool_id = pool.id();
3745 state
3746 .shm_pools
3747 .insert(pool_id, Arc::new(ShmPool::new(pool, fd, size)));
3748 }
3749 }
3750}
3751
3752impl Dispatch<WlShmPool, ()> for Compositor {
3754 fn request(
3755 state: &mut Self,
3756 _: &Client,
3757 resource: &WlShmPool,
3758 request: <WlShmPool as Resource>::Request,
3759 _: &(),
3760 _: &DisplayHandle,
3761 data_init: &mut DataInit<'_, Self>,
3762 ) {
3763 use wayland_server::protocol::wl_shm_pool::Request;
3764 let pool_id = resource.id();
3765 match request {
3766 Request::CreateBuffer {
3767 id,
3768 offset,
3769 width,
3770 height,
3771 stride,
3772 format,
3773 } => {
3774 let fmt = match format {
3776 wayland_server::WEnum::Value(f) => f,
3777 _ => wl_shm::Format::Argb8888, };
3779 let Some(pool) = state.shm_pools.get(&pool_id).cloned() else {
3780 return;
3781 };
3782 data_init.init(
3783 id,
3784 ShmBufferData {
3785 pool,
3786 offset,
3787 width,
3788 height,
3789 stride,
3790 format: fmt,
3791 },
3792 );
3793 }
3794 Request::Resize { size } => {
3795 if let Some(pool) = state.shm_pools.get(&pool_id) {
3796 pool.resize(size);
3797 }
3798 }
3799 Request::Destroy => {
3800 state.shm_pools.remove(&pool_id);
3803 }
3804 _ => {}
3805 }
3806 }
3807}
3808
3809impl Dispatch<WlBuffer, ShmBufferData> for Compositor {
3811 fn request(
3812 _: &mut Self,
3813 _: &Client,
3814 _: &WlBuffer,
3815 _: <WlBuffer as Resource>::Request,
3816 _: &ShmBufferData,
3817 _: &DisplayHandle,
3818 _: &mut DataInit<'_, Self>,
3819 ) {
3820 }
3821}
3822
3823impl Dispatch<WlBuffer, DmaBufBufferData> for Compositor {
3825 fn request(
3826 _: &mut Self,
3827 _: &Client,
3828 _: &WlBuffer,
3829 _: <WlBuffer as Resource>::Request,
3830 _: &DmaBufBufferData,
3831 _: &DisplayHandle,
3832 _: &mut DataInit<'_, Self>,
3833 ) {
3834 }
3835}
3836
3837impl GlobalDispatch<WlOutput, ()> for Compositor {
3839 fn bind(
3840 state: &mut Self,
3841 _: &DisplayHandle,
3842 _: &Client,
3843 resource: New<WlOutput>,
3844 _: &(),
3845 data_init: &mut DataInit<'_, Self>,
3846 ) {
3847 let output = data_init.init(resource, ());
3848 output.geometry(
3849 0,
3850 0,
3851 0,
3852 0,
3853 wl_output::Subpixel::Unknown,
3854 "Virtual".to_string(),
3855 "Headless".to_string(),
3856 wl_output::Transform::Normal,
3857 );
3858 let s120 = state.output_scale_120 as i32;
3859 let mode_w = state.output_width * s120 / 120;
3860 let mode_h = state.output_height * s120 / 120;
3861 output.mode(
3862 wl_output::Mode::Current | wl_output::Mode::Preferred,
3863 mode_w,
3864 mode_h,
3865 state.output_refresh_mhz as i32,
3866 );
3867 if output.version() >= 2 {
3868 output.scale(((state.output_scale_120 as i32) + 119) / 120);
3869 }
3870 if output.version() >= 2 {
3871 output.done();
3872 }
3873 state.outputs.push(output);
3874 }
3875}
3876
3877impl Dispatch<WlOutput, ()> for Compositor {
3878 fn request(
3879 state: &mut Self,
3880 _: &Client,
3881 resource: &WlOutput,
3882 request: <WlOutput as Resource>::Request,
3883 _: &(),
3884 _: &DisplayHandle,
3885 _: &mut DataInit<'_, Self>,
3886 ) {
3887 use wayland_server::protocol::wl_output::Request;
3888 if let Request::Release = request {
3889 state.outputs.retain(|o| o.id() != resource.id());
3890 }
3891 }
3892}
3893
3894impl GlobalDispatch<WlSeat, ()> for Compositor {
3896 fn bind(
3897 _: &mut Self,
3898 _: &DisplayHandle,
3899 _: &Client,
3900 resource: New<WlSeat>,
3901 _: &(),
3902 data_init: &mut DataInit<'_, Self>,
3903 ) {
3904 let seat = data_init.init(resource, ());
3905 seat.capabilities(wl_seat::Capability::Keyboard | wl_seat::Capability::Pointer);
3906 if seat.version() >= 2 {
3907 seat.name("headless".to_string());
3908 }
3909 }
3910}
3911
3912impl Dispatch<WlSeat, ()> for Compositor {
3913 fn request(
3914 state: &mut Self,
3915 _: &Client,
3916 _: &WlSeat,
3917 request: <WlSeat as Resource>::Request,
3918 _: &(),
3919 _: &DisplayHandle,
3920 data_init: &mut DataInit<'_, Self>,
3921 ) {
3922 use wayland_server::protocol::wl_seat::Request;
3923 match request {
3924 Request::GetKeyboard { id } => {
3925 let kb = data_init.init(id, ());
3926 if let Some(fd) = create_keymap_fd(&state.keyboard_keymap_data) {
3927 kb.keymap(
3928 wl_keyboard::KeymapFormat::XkbV1,
3929 fd.as_fd(),
3930 state.keyboard_keymap_data.len() as u32,
3931 );
3932 }
3933 if kb.version() >= 4 {
3934 kb.repeat_info(25, 200);
3935 }
3936 state.keyboards.push(kb);
3937 }
3938 Request::GetPointer { id } => {
3939 let ptr = data_init.init(id, ());
3940 state.pointers.push(ptr);
3941 }
3942 Request::GetTouch { id } => {
3943 data_init.init(id, ());
3944 }
3945 Request::Release => {}
3946 _ => {}
3947 }
3948 }
3949}
3950
3951impl Dispatch<WlKeyboard, ()> for Compositor {
3953 fn request(
3954 state: &mut Self,
3955 _: &Client,
3956 resource: &WlKeyboard,
3957 request: <WlKeyboard as Resource>::Request,
3958 _: &(),
3959 _: &DisplayHandle,
3960 _: &mut DataInit<'_, Self>,
3961 ) {
3962 if let wl_keyboard::Request::Release = request {
3963 state.keyboards.retain(|k| k.id() != resource.id());
3964 }
3965 }
3966}
3967
3968impl Dispatch<WlPointer, ()> for Compositor {
3970 fn request(
3971 state: &mut Self,
3972 _: &Client,
3973 resource: &WlPointer,
3974 request: <WlPointer as Resource>::Request,
3975 _: &(),
3976 _: &DisplayHandle,
3977 _: &mut DataInit<'_, Self>,
3978 ) {
3979 use wl_pointer::Request;
3980 match request {
3981 Request::SetCursor {
3982 serial: _,
3983 surface,
3984 hotspot_x,
3985 hotspot_y,
3986 } => {
3987 if let Some(surface) = surface {
3988 let sid = surface.id();
3989 if let Some(surf) = state.surfaces.get_mut(&sid) {
3990 surf.is_cursor = true;
3991 surf.cursor_hotspot = (hotspot_x, hotspot_y);
3992 }
3993 } else {
3994 let _ = state.event_tx.send(CompositorEvent::SurfaceCursor {
3995 surface_id: state.focused_surface_id,
3996 cursor: CursorImage::Hidden,
3997 });
3998 }
3999 }
4000 Request::Release => {
4001 state.pointers.retain(|p| p.id() != resource.id());
4002 }
4003 _ => {}
4004 }
4005 }
4006}
4007
4008impl Dispatch<wayland_server::protocol::wl_touch::WlTouch, ()> for Compositor {
4010 fn request(
4011 _: &mut Self,
4012 _: &Client,
4013 _: &wayland_server::protocol::wl_touch::WlTouch,
4014 _: <wayland_server::protocol::wl_touch::WlTouch as Resource>::Request,
4015 _: &(),
4016 _: &DisplayHandle,
4017 _: &mut DataInit<'_, Self>,
4018 ) {
4019 }
4020}
4021
4022impl GlobalDispatch<ZwpLinuxDmabufV1, ()> for Compositor {
4024 fn bind(
4025 state: &mut Self,
4026 _: &DisplayHandle,
4027 _: &Client,
4028 resource: New<ZwpLinuxDmabufV1>,
4029 _: &(),
4030 data_init: &mut DataInit<'_, Self>,
4031 ) {
4032 let dmabuf = data_init.init(resource, ());
4033 if dmabuf.version() >= 4 {
4036 return;
4037 }
4038 if dmabuf.version() >= 3 {
4039 if let Some(ref vk) = state.vulkan_renderer
4045 && !vk.supported_dmabuf_modifiers.is_empty()
4046 {
4047 for &(drm_fmt, modifier) in &vk.supported_dmabuf_modifiers {
4048 let mod_hi = (modifier >> 32) as u32;
4049 let mod_lo = (modifier & 0xFFFFFFFF) as u32;
4050 dmabuf.modifier(drm_fmt, mod_hi, mod_lo);
4051 }
4052 }
4053 } else if state
4057 .vulkan_renderer
4058 .as_ref()
4059 .is_some_and(|vk| vk.has_dmabuf())
4060 {
4061 dmabuf.format(drm_fourcc::ARGB8888);
4062 dmabuf.format(drm_fourcc::XRGB8888);
4063 dmabuf.format(drm_fourcc::ABGR8888);
4064 dmabuf.format(drm_fourcc::XBGR8888);
4065 }
4066 }
4067}
4068
4069impl Dispatch<ZwpLinuxDmabufV1, ()> for Compositor {
4070 fn request(
4071 state: &mut Self,
4072 _: &Client,
4073 _: &ZwpLinuxDmabufV1,
4074 request: <ZwpLinuxDmabufV1 as Resource>::Request,
4075 _: &(),
4076 _: &DisplayHandle,
4077 data_init: &mut DataInit<'_, Self>,
4078 ) {
4079 use zwp_linux_dmabuf_v1::Request;
4080 match request {
4081 Request::CreateParams { params_id } => {
4082 data_init.init(params_id, ());
4083 }
4084 Request::GetDefaultFeedback { id } => {
4085 let fb = data_init.init(id, ());
4086 state.send_dmabuf_feedback(&fb);
4087 }
4088 Request::GetSurfaceFeedback { id, .. } => {
4089 let fb = data_init.init(id, ());
4090 state.send_dmabuf_feedback(&fb);
4091 }
4092 Request::Destroy => {}
4093 _ => {}
4094 }
4095 }
4096}
4097
4098impl Dispatch<ZwpLinuxDmabufFeedbackV1, ()> for Compositor {
4099 fn request(
4100 _: &mut Self,
4101 _: &Client,
4102 _: &ZwpLinuxDmabufFeedbackV1,
4103 _request: <ZwpLinuxDmabufFeedbackV1 as Resource>::Request,
4104 _: &(),
4105 _: &DisplayHandle,
4106 _data_init: &mut DataInit<'_, Self>,
4107 ) {
4108 }
4110}
4111
4112impl Dispatch<ZwpLinuxBufferParamsV1, ()> for Compositor {
4114 fn request(
4115 state: &mut Self,
4116 client: &Client,
4117 resource: &ZwpLinuxBufferParamsV1,
4118 request: <ZwpLinuxBufferParamsV1 as Resource>::Request,
4119 _: &(),
4120 dh: &DisplayHandle,
4121 data_init: &mut DataInit<'_, Self>,
4122 ) {
4123 use zwp_linux_buffer_params_v1::Request;
4124 let params_id = resource.id();
4125 match request {
4126 Request::Add {
4127 fd,
4128 plane_idx: _,
4129 offset,
4130 stride,
4131 modifier_hi,
4132 modifier_lo,
4133 } => {
4134 let modifier = ((modifier_hi as u64) << 32) | (modifier_lo as u64);
4135 let entry = state
4136 .dmabuf_params
4137 .entry(params_id.clone())
4138 .or_insert_with(|| DmaBufParamsPending {
4139 resource: resource.clone(),
4140 planes: Vec::new(),
4141 modifier,
4142 });
4143 entry.modifier = modifier;
4144 entry.planes.push(DmaBufPlane { fd, offset, stride });
4145 }
4146 Request::Create {
4147 width,
4148 height,
4149 format,
4150 flags,
4151 } => {
4152 let pending = state.dmabuf_params.remove(¶ms_id);
4153 let (planes, modifier) = match pending {
4154 Some(p) => (p.planes, p.modifier),
4155 None => {
4156 resource.failed();
4157 return;
4158 }
4159 };
4160 let y_invert = flags
4161 .into_result()
4162 .ok()
4163 .is_some_and(|f| f.contains(zwp_linux_buffer_params_v1::Flags::YInvert));
4164 match client.create_resource::<WlBuffer, DmaBufBufferData, Compositor>(
4165 dh,
4166 1,
4167 DmaBufBufferData {
4168 width,
4169 height,
4170 fourcc: format,
4171 modifier,
4172 planes,
4173 y_invert,
4174 },
4175 ) {
4176 Ok(buffer) => resource.created(&buffer),
4177 Err(_) => resource.failed(),
4178 }
4179 }
4180 Request::CreateImmed {
4181 buffer_id,
4182 width,
4183 height,
4184 format,
4185 flags,
4186 } => {
4187 let (planes, modifier) = state
4188 .dmabuf_params
4189 .remove(¶ms_id)
4190 .map(|p| (p.planes, p.modifier))
4191 .unwrap_or_default();
4192 let y_invert = flags
4193 .into_result()
4194 .ok()
4195 .is_some_and(|f| f.contains(zwp_linux_buffer_params_v1::Flags::YInvert));
4196 data_init.init(
4197 buffer_id,
4198 DmaBufBufferData {
4199 width,
4200 height,
4201 fourcc: format,
4202 modifier,
4203 planes,
4204 y_invert,
4205 },
4206 );
4207 }
4208 Request::Destroy => {
4209 state.dmabuf_params.remove(¶ms_id);
4210 }
4211 _ => {}
4212 }
4213 }
4214}
4215
4216impl GlobalDispatch<WpFractionalScaleManagerV1, ()> for Compositor {
4218 fn bind(
4219 _: &mut Self,
4220 _: &DisplayHandle,
4221 _: &Client,
4222 resource: New<WpFractionalScaleManagerV1>,
4223 _: &(),
4224 data_init: &mut DataInit<'_, Self>,
4225 ) {
4226 data_init.init(resource, ());
4227 }
4228}
4229
4230impl Dispatch<WpFractionalScaleManagerV1, ()> for Compositor {
4231 fn request(
4232 state: &mut Self,
4233 _: &Client,
4234 _: &WpFractionalScaleManagerV1,
4235 request: <WpFractionalScaleManagerV1 as Resource>::Request,
4236 _: &(),
4237 _: &DisplayHandle,
4238 data_init: &mut DataInit<'_, Self>,
4239 ) {
4240 use wp_fractional_scale_manager_v1::Request;
4241 match request {
4242 Request::GetFractionalScale { id, surface: _ } => {
4243 let fs = data_init.init(id, ());
4244 fs.preferred_scale(state.output_scale_120 as u32);
4246 state.fractional_scales.push(fs);
4247 }
4248 Request::Destroy => {}
4249 _ => {}
4250 }
4251 }
4252}
4253
4254impl Dispatch<WpFractionalScaleV1, ()> for Compositor {
4256 fn request(
4257 state: &mut Self,
4258 _: &Client,
4259 resource: &WpFractionalScaleV1,
4260 _: <WpFractionalScaleV1 as Resource>::Request,
4261 _: &(),
4262 _: &DisplayHandle,
4263 _: &mut DataInit<'_, Self>,
4264 ) {
4265 state
4267 .fractional_scales
4268 .retain(|fs| fs.id() != resource.id());
4269 }
4270}
4271
4272impl GlobalDispatch<WpViewporter, ()> for Compositor {
4274 fn bind(
4275 _: &mut Self,
4276 _: &DisplayHandle,
4277 _: &Client,
4278 resource: New<WpViewporter>,
4279 _: &(),
4280 data_init: &mut DataInit<'_, Self>,
4281 ) {
4282 data_init.init(resource, ());
4283 }
4284}
4285
4286impl Dispatch<WpViewporter, ()> for Compositor {
4287 fn request(
4288 _: &mut Self,
4289 _: &Client,
4290 _: &WpViewporter,
4291 request: <WpViewporter as Resource>::Request,
4292 _: &(),
4293 _: &DisplayHandle,
4294 data_init: &mut DataInit<'_, Self>,
4295 ) {
4296 use wp_viewporter::Request;
4297 match request {
4298 Request::GetViewport { id, surface } => {
4299 let obj_id = surface.id();
4302 data_init.init(id, obj_id);
4303 }
4304 Request::Destroy => {}
4305 _ => {}
4306 }
4307 }
4308}
4309
4310impl Dispatch<WpViewport, ObjectId> for Compositor {
4312 fn request(
4313 state: &mut Self,
4314 _: &Client,
4315 _: &WpViewport,
4316 request: <WpViewport as Resource>::Request,
4317 surface_obj_id: &ObjectId,
4318 _: &DisplayHandle,
4319 _: &mut DataInit<'_, Self>,
4320 ) {
4321 use wayland_protocols::wp::viewporter::server::wp_viewport::Request;
4322 match request {
4323 Request::SetDestination { width, height } => {
4324 if let Some(surf) = state.surfaces.get_mut(surface_obj_id) {
4325 if width > 0 && height > 0 {
4327 surf.pending_viewport_destination = Some((width, height));
4328 } else {
4329 surf.pending_viewport_destination = None;
4330 }
4331 }
4332 }
4333 Request::SetSource { .. } => {
4334 }
4336 Request::Destroy => {}
4337 _ => {}
4338 }
4339 }
4340}
4341
4342impl GlobalDispatch<WlDataDeviceManager, ()> for Compositor {
4349 fn bind(
4350 _: &mut Self,
4351 _: &DisplayHandle,
4352 _: &Client,
4353 resource: New<WlDataDeviceManager>,
4354 _: &(),
4355 data_init: &mut DataInit<'_, Self>,
4356 ) {
4357 data_init.init(resource, ());
4358 }
4359}
4360
4361impl Dispatch<WlDataDeviceManager, ()> for Compositor {
4362 fn request(
4363 state: &mut Self,
4364 _: &Client,
4365 _: &WlDataDeviceManager,
4366 request: <WlDataDeviceManager as Resource>::Request,
4367 _: &(),
4368 _: &DisplayHandle,
4369 data_init: &mut DataInit<'_, Self>,
4370 ) {
4371 use wl_data_device_manager::Request;
4372 match request {
4373 Request::CreateDataSource { id } => {
4374 data_init.init(
4375 id,
4376 DataSourceData {
4377 mime_types: std::sync::Mutex::new(Vec::new()),
4378 },
4379 );
4380 }
4381 Request::GetDataDevice { id, seat: _ } => {
4382 let dd = data_init.init(id, ());
4383 state.data_devices.push(dd);
4384 }
4385 _ => {}
4386 }
4387 }
4388}
4389
4390impl Dispatch<WlDataSource, DataSourceData> for Compositor {
4391 fn request(
4392 _: &mut Self,
4393 _: &Client,
4394 _: &WlDataSource,
4395 request: <WlDataSource as Resource>::Request,
4396 data: &DataSourceData,
4397 _: &DisplayHandle,
4398 _: &mut DataInit<'_, Self>,
4399 ) {
4400 use wl_data_source::Request;
4401 match request {
4402 Request::Offer { mime_type } => {
4403 data.mime_types.lock().unwrap().push(mime_type);
4404 }
4405 Request::Destroy => {}
4406 _ => {} }
4408 }
4409
4410 fn destroyed(
4411 state: &mut Self,
4412 _: wayland_server::backend::ClientId,
4413 resource: &WlDataSource,
4414 _: &DataSourceData,
4415 ) {
4416 if state
4417 .selection_source
4418 .as_ref()
4419 .is_some_and(|s| s.id() == resource.id())
4420 {
4421 state.selection_source = None;
4422 }
4423 }
4424}
4425
4426impl Dispatch<WlDataDevice, ()> for Compositor {
4427 fn request(
4428 state: &mut Self,
4429 _: &Client,
4430 _: &WlDataDevice,
4431 request: <WlDataDevice as Resource>::Request,
4432 _: &(),
4433 _: &DisplayHandle,
4434 _: &mut DataInit<'_, Self>,
4435 ) {
4436 use wl_data_device::Request;
4437 match request {
4438 Request::SetSelection { source, serial: _ } => {
4439 state.selection_source = source.clone();
4440 if let Some(ref src) = source {
4442 let data = src.data::<DataSourceData>().unwrap();
4443 let mimes = data.mime_types.lock().unwrap();
4444 let text_mime = mimes
4445 .iter()
4446 .find(|m| {
4447 m.as_str() == "text/plain;charset=utf-8"
4448 || m.as_str() == "text/plain"
4449 || m.as_str() == "UTF8_STRING"
4450 })
4451 .cloned();
4452 drop(mimes);
4453 if let Some(mime) = text_mime {
4454 state.read_data_source_and_emit(src, &mime);
4455 }
4456 }
4457 }
4458 Request::Release => {}
4459 _ => {} }
4461 }
4462
4463 fn destroyed(
4464 state: &mut Self,
4465 _: wayland_server::backend::ClientId,
4466 resource: &WlDataDevice,
4467 _: &(),
4468 ) {
4469 state.data_devices.retain(|d| d.id() != resource.id());
4470 }
4471}
4472
4473impl Dispatch<WlDataOffer, DataOfferData> for Compositor {
4474 fn request(
4475 state: &mut Self,
4476 _: &Client,
4477 _: &WlDataOffer,
4478 request: <WlDataOffer as Resource>::Request,
4479 data: &DataOfferData,
4480 _: &DisplayHandle,
4481 _: &mut DataInit<'_, Self>,
4482 ) {
4483 use wl_data_offer::Request;
4484 match request {
4485 Request::Receive { mime_type, fd } => {
4486 if data.external {
4487 if let Some(ref cb) = state.external_clipboard
4489 && (cb.mime_type == mime_type
4490 || mime_type == "text/plain"
4491 || mime_type == "text/plain;charset=utf-8"
4492 || mime_type == "UTF8_STRING")
4493 {
4494 use std::io::Write;
4495 let mut f = std::fs::File::from(fd);
4496 let _ = f.write_all(&cb.data);
4497 }
4498 } else if let Some(ref src) = state.selection_source {
4499 src.send(mime_type, fd.as_fd());
4501 }
4502 }
4503 Request::Destroy => {}
4504 _ => {} }
4506 }
4507}
4508
4509impl Compositor {
4510 fn read_data_source_and_emit(&mut self, source: &WlDataSource, mime_type: &str) {
4513 let mut fds = [0i32; 2];
4514 if unsafe { libc::pipe(fds.as_mut_ptr()) } != 0 {
4515 return;
4516 }
4517 let read_fd = unsafe { OwnedFd::from_raw_fd(fds[0]) };
4518 let write_fd = unsafe { OwnedFd::from_raw_fd(fds[1]) };
4519 source.send(mime_type.to_string(), write_fd.as_fd());
4520 let _ = self.display_handle.flush_clients();
4521 unsafe {
4523 libc::fcntl(read_fd.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
4524 }
4525 std::thread::sleep(std::time::Duration::from_millis(5));
4527 let mut buf = Vec::new();
4528 let mut tmp = [0u8; 8192];
4529 loop {
4530 let n = unsafe {
4531 libc::read(
4532 read_fd.as_raw_fd(),
4533 tmp.as_mut_ptr() as *mut libc::c_void,
4534 tmp.len(),
4535 )
4536 };
4537 if n <= 0 {
4538 break;
4539 }
4540 buf.extend_from_slice(&tmp[..n as usize]);
4541 if buf.len() > 1024 * 1024 {
4542 break; }
4544 }
4545 if !buf.is_empty() {
4546 let _ = self.event_tx.send(CompositorEvent::ClipboardContent {
4547 mime_type: mime_type.to_string(),
4548 data: buf,
4549 });
4550 (self.event_notify)();
4551 }
4552 }
4553
4554 fn offer_external_clipboard(&mut self) {
4556 let Some(ref cb) = self.external_clipboard else {
4557 return;
4558 };
4559 let mime = cb.mime_type.clone();
4560 for dd in &self.data_devices {
4561 if let Some(client) = dd.client() {
4562 let offer = client
4563 .create_resource::<WlDataOffer, DataOfferData, Compositor>(
4564 &self.display_handle,
4565 dd.version(),
4566 DataOfferData { external: true },
4567 )
4568 .unwrap();
4569 dd.data_offer(&offer);
4570 offer.offer(mime.clone());
4571 if mime.starts_with("text/plain") {
4573 if mime != "text/plain" {
4574 offer.offer("text/plain".to_string());
4575 }
4576 if mime != "text/plain;charset=utf-8" {
4577 offer.offer("text/plain;charset=utf-8".to_string());
4578 }
4579 offer.offer("UTF8_STRING".to_string());
4580 }
4581 dd.selection(Some(&offer));
4582 }
4583 }
4584 let _ = self.display_handle.flush_clients();
4585 }
4586}
4587
4588impl GlobalDispatch<ZwpPrimarySelectionDeviceManagerV1, ()> for Compositor {
4591 fn bind(
4592 _: &mut Self,
4593 _: &DisplayHandle,
4594 _: &Client,
4595 resource: New<ZwpPrimarySelectionDeviceManagerV1>,
4596 _: &(),
4597 data_init: &mut DataInit<'_, Self>,
4598 ) {
4599 data_init.init(resource, ());
4600 }
4601}
4602
4603impl Dispatch<ZwpPrimarySelectionDeviceManagerV1, ()> for Compositor {
4604 fn request(
4605 state: &mut Self,
4606 _: &Client,
4607 _: &ZwpPrimarySelectionDeviceManagerV1,
4608 request: <ZwpPrimarySelectionDeviceManagerV1 as Resource>::Request,
4609 _: &(),
4610 _: &DisplayHandle,
4611 data_init: &mut DataInit<'_, Self>,
4612 ) {
4613 use zwp_primary_selection_device_manager_v1::Request;
4614 match request {
4615 Request::CreateSource { id } => {
4616 data_init.init(
4617 id,
4618 PrimarySourceData {
4619 mime_types: std::sync::Mutex::new(Vec::new()),
4620 },
4621 );
4622 }
4623 Request::GetDevice { id, seat: _ } => {
4624 let pd = data_init.init(id, ());
4625 state.primary_devices.push(pd);
4626 }
4627 Request::Destroy => {}
4628 _ => {}
4629 }
4630 }
4631}
4632
4633impl Dispatch<ZwpPrimarySelectionSourceV1, PrimarySourceData> for Compositor {
4634 fn request(
4635 _: &mut Self,
4636 _: &Client,
4637 _: &ZwpPrimarySelectionSourceV1,
4638 request: <ZwpPrimarySelectionSourceV1 as Resource>::Request,
4639 data: &PrimarySourceData,
4640 _: &DisplayHandle,
4641 _: &mut DataInit<'_, Self>,
4642 ) {
4643 use zwp_primary_selection_source_v1::Request;
4644 match request {
4645 Request::Offer { mime_type } => {
4646 data.mime_types.lock().unwrap().push(mime_type);
4647 }
4648 Request::Destroy => {}
4649 _ => {}
4650 }
4651 }
4652
4653 fn destroyed(
4654 state: &mut Self,
4655 _: wayland_server::backend::ClientId,
4656 resource: &ZwpPrimarySelectionSourceV1,
4657 _: &PrimarySourceData,
4658 ) {
4659 if state
4660 .primary_source
4661 .as_ref()
4662 .is_some_and(|s| s.id() == resource.id())
4663 {
4664 state.primary_source = None;
4665 }
4666 }
4667}
4668
4669impl Dispatch<ZwpPrimarySelectionDeviceV1, ()> for Compositor {
4670 fn request(
4671 state: &mut Self,
4672 _: &Client,
4673 _: &ZwpPrimarySelectionDeviceV1,
4674 request: <ZwpPrimarySelectionDeviceV1 as Resource>::Request,
4675 _: &(),
4676 _: &DisplayHandle,
4677 _: &mut DataInit<'_, Self>,
4678 ) {
4679 use zwp_primary_selection_device_v1::Request;
4680 match request {
4681 Request::SetSelection { source, serial: _ } => {
4682 state.primary_source = source;
4683 }
4684 Request::Destroy => {}
4685 _ => {}
4686 }
4687 }
4688
4689 fn destroyed(
4690 state: &mut Self,
4691 _: wayland_server::backend::ClientId,
4692 resource: &ZwpPrimarySelectionDeviceV1,
4693 _: &(),
4694 ) {
4695 state.primary_devices.retain(|d| d.id() != resource.id());
4696 }
4697}
4698
4699impl Dispatch<ZwpPrimarySelectionOfferV1, PrimaryOfferData> for Compositor {
4700 fn request(
4701 state: &mut Self,
4702 _: &Client,
4703 _: &ZwpPrimarySelectionOfferV1,
4704 request: <ZwpPrimarySelectionOfferV1 as Resource>::Request,
4705 data: &PrimaryOfferData,
4706 _: &DisplayHandle,
4707 _: &mut DataInit<'_, Self>,
4708 ) {
4709 use zwp_primary_selection_offer_v1::Request;
4710 match request {
4711 Request::Receive { mime_type, fd } => {
4712 if data.external {
4713 if let Some(ref cb) = state.external_primary {
4714 use std::io::Write;
4715 let mut f = std::fs::File::from(fd);
4716 let _ = f.write_all(&cb.data);
4717 let _ = mime_type; }
4719 } else if let Some(ref src) = state.primary_source {
4720 src.send(mime_type, fd.as_fd());
4721 }
4722 }
4723 Request::Destroy => {}
4724 _ => {}
4725 }
4726 }
4727}
4728
4729impl GlobalDispatch<ZwpPointerConstraintsV1, ()> for Compositor {
4732 fn bind(
4733 _: &mut Self,
4734 _: &DisplayHandle,
4735 _: &Client,
4736 resource: New<ZwpPointerConstraintsV1>,
4737 _: &(),
4738 data_init: &mut DataInit<'_, Self>,
4739 ) {
4740 data_init.init(resource, ());
4741 }
4742}
4743
4744impl Dispatch<ZwpPointerConstraintsV1, ()> for Compositor {
4745 fn request(
4746 _: &mut Self,
4747 _: &Client,
4748 _: &ZwpPointerConstraintsV1,
4749 request: <ZwpPointerConstraintsV1 as Resource>::Request,
4750 _: &(),
4751 _: &DisplayHandle,
4752 data_init: &mut DataInit<'_, Self>,
4753 ) {
4754 use zwp_pointer_constraints_v1::Request;
4755 match request {
4756 Request::LockPointer {
4757 id,
4758 surface: _,
4759 pointer: _,
4760 region: _,
4761 lifetime: _,
4762 } => {
4763 let lp = data_init.init(id, ());
4764 lp.locked();
4766 }
4767 Request::ConfinePointer {
4768 id,
4769 surface: _,
4770 pointer: _,
4771 region: _,
4772 lifetime: _,
4773 } => {
4774 let cp = data_init.init(id, ());
4775 cp.confined();
4776 }
4777 Request::Destroy => {}
4778 _ => {}
4779 }
4780 }
4781}
4782
4783impl Dispatch<ZwpLockedPointerV1, ()> for Compositor {
4784 fn request(
4785 _: &mut Self,
4786 _: &Client,
4787 _: &ZwpLockedPointerV1,
4788 _: <ZwpLockedPointerV1 as Resource>::Request,
4789 _: &(),
4790 _: &DisplayHandle,
4791 _: &mut DataInit<'_, Self>,
4792 ) {
4793 }
4795}
4796
4797impl Dispatch<ZwpConfinedPointerV1, ()> for Compositor {
4798 fn request(
4799 _: &mut Self,
4800 _: &Client,
4801 _: &ZwpConfinedPointerV1,
4802 _: <ZwpConfinedPointerV1 as Resource>::Request,
4803 _: &(),
4804 _: &DisplayHandle,
4805 _: &mut DataInit<'_, Self>,
4806 ) {
4807 }
4809}
4810
4811impl GlobalDispatch<ZwpRelativePointerManagerV1, ()> for Compositor {
4814 fn bind(
4815 _: &mut Self,
4816 _: &DisplayHandle,
4817 _: &Client,
4818 resource: New<ZwpRelativePointerManagerV1>,
4819 _: &(),
4820 data_init: &mut DataInit<'_, Self>,
4821 ) {
4822 data_init.init(resource, ());
4823 }
4824}
4825
4826impl Dispatch<ZwpRelativePointerManagerV1, ()> for Compositor {
4827 fn request(
4828 state: &mut Self,
4829 _: &Client,
4830 _: &ZwpRelativePointerManagerV1,
4831 request: <ZwpRelativePointerManagerV1 as Resource>::Request,
4832 _: &(),
4833 _: &DisplayHandle,
4834 data_init: &mut DataInit<'_, Self>,
4835 ) {
4836 use zwp_relative_pointer_manager_v1::Request;
4837 match request {
4838 Request::GetRelativePointer { id, pointer: _ } => {
4839 let rp = data_init.init(id, ());
4840 state.relative_pointers.push(rp);
4841 }
4842 Request::Destroy => {}
4843 _ => {}
4844 }
4845 }
4846}
4847
4848impl Dispatch<ZwpRelativePointerV1, ()> for Compositor {
4849 fn request(
4850 state: &mut Self,
4851 _: &Client,
4852 resource: &ZwpRelativePointerV1,
4853 _: <ZwpRelativePointerV1 as Resource>::Request,
4854 _: &(),
4855 _: &DisplayHandle,
4856 _: &mut DataInit<'_, Self>,
4857 ) {
4858 state
4860 .relative_pointers
4861 .retain(|rp| rp.id() != resource.id());
4862 }
4863}
4864
4865impl GlobalDispatch<ZwpTextInputManagerV3, ()> for Compositor {
4868 fn bind(
4869 _: &mut Self,
4870 _: &DisplayHandle,
4871 _: &Client,
4872 resource: New<ZwpTextInputManagerV3>,
4873 _: &(),
4874 data_init: &mut DataInit<'_, Self>,
4875 ) {
4876 data_init.init(resource, ());
4877 }
4878}
4879
4880impl Dispatch<ZwpTextInputManagerV3, ()> for Compositor {
4881 fn request(
4882 state: &mut Self,
4883 _: &Client,
4884 _: &ZwpTextInputManagerV3,
4885 request: <ZwpTextInputManagerV3 as Resource>::Request,
4886 _: &(),
4887 _: &DisplayHandle,
4888 data_init: &mut DataInit<'_, Self>,
4889 ) {
4890 use zwp_text_input_manager_v3::Request;
4891 match request {
4892 Request::GetTextInput { id, seat: _ } => {
4893 let ti = data_init.init(id, ());
4894 state.text_inputs.push(TextInputState {
4895 resource: ti,
4896 enabled: false,
4897 });
4898 }
4899 Request::Destroy => {}
4900 _ => {}
4901 }
4902 }
4903}
4904
4905impl Dispatch<ZwpTextInputV3, ()> for Compositor {
4906 fn request(
4907 state: &mut Self,
4908 _: &Client,
4909 resource: &ZwpTextInputV3,
4910 request: <ZwpTextInputV3 as Resource>::Request,
4911 _: &(),
4912 _: &DisplayHandle,
4913 _: &mut DataInit<'_, Self>,
4914 ) {
4915 use zwp_text_input_v3::Request;
4916 match request {
4917 Request::Enable => {
4918 if let Some(ti) = state
4919 .text_inputs
4920 .iter_mut()
4921 .find(|t| t.resource.id() == resource.id())
4922 {
4923 ti.enabled = true;
4924 }
4925 }
4926 Request::Disable => {
4927 if let Some(ti) = state
4928 .text_inputs
4929 .iter_mut()
4930 .find(|t| t.resource.id() == resource.id())
4931 {
4932 ti.enabled = false;
4933 }
4934 }
4935 Request::Commit => {
4936 }
4938 Request::Destroy => {
4939 state
4940 .text_inputs
4941 .retain(|t| t.resource.id() != resource.id());
4942 }
4943 _ => {}
4946 }
4947 }
4948}
4949
4950impl GlobalDispatch<XdgActivationV1, ()> for Compositor {
4953 fn bind(
4954 _: &mut Self,
4955 _: &DisplayHandle,
4956 _: &Client,
4957 resource: New<XdgActivationV1>,
4958 _: &(),
4959 data_init: &mut DataInit<'_, Self>,
4960 ) {
4961 data_init.init(resource, ());
4962 }
4963}
4964
4965impl Dispatch<XdgActivationV1, ()> for Compositor {
4966 fn request(
4967 state: &mut Self,
4968 _: &Client,
4969 _: &XdgActivationV1,
4970 request: <XdgActivationV1 as Resource>::Request,
4971 _: &(),
4972 _: &DisplayHandle,
4973 data_init: &mut DataInit<'_, Self>,
4974 ) {
4975 use xdg_activation_v1::Request;
4976 match request {
4977 Request::GetActivationToken { id } => {
4978 let serial = state.next_activation_token;
4979 state.next_activation_token = serial.wrapping_add(1);
4980 data_init.init(id, ActivationTokenData { serial });
4981 }
4982 Request::Activate {
4983 token: _,
4984 surface: _,
4985 } => {
4986 }
4989 Request::Destroy => {}
4990 _ => {}
4991 }
4992 }
4993}
4994
4995impl Dispatch<XdgActivationTokenV1, ActivationTokenData> for Compositor {
4996 fn request(
4997 _: &mut Self,
4998 _: &Client,
4999 resource: &XdgActivationTokenV1,
5000 request: <XdgActivationTokenV1 as Resource>::Request,
5001 data: &ActivationTokenData,
5002 _: &DisplayHandle,
5003 _: &mut DataInit<'_, Self>,
5004 ) {
5005 use xdg_activation_token_v1::Request;
5006 match request {
5007 Request::Commit => {
5008 resource.done(format!("blit-token-{}", data.serial));
5011 }
5012 Request::SetSerial { .. } | Request::SetAppId { .. } | Request::SetSurface { .. } => {}
5013 Request::Destroy => {}
5014 _ => {}
5015 }
5016 }
5017}
5018
5019impl GlobalDispatch<WpCursorShapeManagerV1, ()> for Compositor {
5022 fn bind(
5023 _: &mut Self,
5024 _: &DisplayHandle,
5025 _: &Client,
5026 resource: New<WpCursorShapeManagerV1>,
5027 _: &(),
5028 data_init: &mut DataInit<'_, Self>,
5029 ) {
5030 data_init.init(resource, ());
5031 }
5032}
5033
5034impl Dispatch<WpCursorShapeManagerV1, ()> for Compositor {
5035 fn request(
5036 _: &mut Self,
5037 _: &Client,
5038 _: &WpCursorShapeManagerV1,
5039 request: <WpCursorShapeManagerV1 as Resource>::Request,
5040 _: &(),
5041 _: &DisplayHandle,
5042 data_init: &mut DataInit<'_, Self>,
5043 ) {
5044 use wp_cursor_shape_manager_v1::Request;
5045 match request {
5046 Request::GetPointer {
5047 cursor_shape_device,
5048 pointer: _,
5049 } => {
5050 data_init.init(cursor_shape_device, ());
5051 }
5052 Request::GetTabletToolV2 {
5053 cursor_shape_device,
5054 tablet_tool: _,
5055 } => {
5056 data_init.init(cursor_shape_device, ());
5057 }
5058 Request::Destroy => {}
5059 _ => {}
5060 }
5061 }
5062}
5063
5064impl Dispatch<WpCursorShapeDeviceV1, ()> for Compositor {
5065 fn request(
5066 state: &mut Self,
5067 _: &Client,
5068 _: &WpCursorShapeDeviceV1,
5069 request: <WpCursorShapeDeviceV1 as Resource>::Request,
5070 _: &(),
5071 _: &DisplayHandle,
5072 _: &mut DataInit<'_, Self>,
5073 ) {
5074 use wp_cursor_shape_device_v1::Request;
5075 match request {
5076 Request::SetShape { serial: _, shape } => {
5077 use wayland_server::WEnum;
5078 use wp_cursor_shape_device_v1::Shape;
5079 let name = match shape {
5080 WEnum::Value(Shape::Default) => "default",
5081 WEnum::Value(Shape::ContextMenu) => "context-menu",
5082 WEnum::Value(Shape::Help) => "help",
5083 WEnum::Value(Shape::Pointer) => "pointer",
5084 WEnum::Value(Shape::Progress) => "progress",
5085 WEnum::Value(Shape::Wait) => "wait",
5086 WEnum::Value(Shape::Cell) => "cell",
5087 WEnum::Value(Shape::Crosshair) => "crosshair",
5088 WEnum::Value(Shape::Text) => "text",
5089 WEnum::Value(Shape::VerticalText) => "vertical-text",
5090 WEnum::Value(Shape::Alias) => "alias",
5091 WEnum::Value(Shape::Copy) => "copy",
5092 WEnum::Value(Shape::Move) => "move",
5093 WEnum::Value(Shape::NoDrop) => "no-drop",
5094 WEnum::Value(Shape::NotAllowed) => "not-allowed",
5095 WEnum::Value(Shape::Grab) => "grab",
5096 WEnum::Value(Shape::Grabbing) => "grabbing",
5097 WEnum::Value(Shape::EResize) => "e-resize",
5098 WEnum::Value(Shape::NResize) => "n-resize",
5099 WEnum::Value(Shape::NeResize) => "ne-resize",
5100 WEnum::Value(Shape::NwResize) => "nw-resize",
5101 WEnum::Value(Shape::SResize) => "s-resize",
5102 WEnum::Value(Shape::SeResize) => "se-resize",
5103 WEnum::Value(Shape::SwResize) => "sw-resize",
5104 WEnum::Value(Shape::WResize) => "w-resize",
5105 WEnum::Value(Shape::EwResize) => "ew-resize",
5106 WEnum::Value(Shape::NsResize) => "ns-resize",
5107 WEnum::Value(Shape::NeswResize) => "nesw-resize",
5108 WEnum::Value(Shape::NwseResize) => "nwse-resize",
5109 WEnum::Value(Shape::ColResize) => "col-resize",
5110 WEnum::Value(Shape::RowResize) => "row-resize",
5111 WEnum::Value(Shape::AllScroll) => "all-scroll",
5112 WEnum::Value(Shape::ZoomIn) => "zoom-in",
5113 WEnum::Value(Shape::ZoomOut) => "zoom-out",
5114 _ => "default",
5115 };
5116 let _ = state.event_tx.send(CompositorEvent::SurfaceCursor {
5117 surface_id: state.focused_surface_id,
5118 cursor: CursorImage::Named(name.to_string()),
5119 });
5120 (state.event_notify)();
5121 }
5122 Request::Destroy => {}
5123 _ => {}
5124 }
5125 }
5126}
5127
5128impl wayland_server::backend::ClientData for ClientState {
5130 fn initialized(&self, _: wayland_server::backend::ClientId) {}
5131 fn disconnected(
5132 &self,
5133 _: wayland_server::backend::ClientId,
5134 _: wayland_server::backend::DisconnectReason,
5135 ) {
5136 }
5137}
5138
5139pub struct CompositorHandle {
5144 pub event_rx: mpsc::Receiver<CompositorEvent>,
5145 pub command_tx: mpsc::Sender<CompositorCommand>,
5146 pub socket_name: String,
5147 pub thread: std::thread::JoinHandle<()>,
5148 pub shutdown: Arc<AtomicBool>,
5149 pub vulkan_video_encode: bool,
5151 pub vulkan_video_encode_av1: bool,
5153 loop_signal: LoopSignal,
5154}
5155
5156impl CompositorHandle {
5157 pub fn wake(&self) {
5158 self.loop_signal.wakeup();
5159 }
5160}
5161
5162pub fn spawn_compositor(
5163 verbose: bool,
5164 event_notify: Arc<dyn Fn() + Send + Sync>,
5165 gpu_device: &str,
5166) -> CompositorHandle {
5167 let _gpu_device = gpu_device.to_string();
5168 let (event_tx, event_rx) = mpsc::channel();
5169 let (command_tx, command_rx) = mpsc::channel();
5170 let (socket_tx, socket_rx) = mpsc::sync_channel(1);
5171 let (signal_tx, signal_rx) = mpsc::sync_channel::<LoopSignal>(1);
5172 let (caps_tx, caps_rx) = mpsc::sync_channel::<(bool, bool)>(1);
5173 let shutdown = Arc::new(AtomicBool::new(false));
5174 let shutdown_clone = shutdown.clone();
5175
5176 let runtime_dir = std::env::var_os("XDG_RUNTIME_DIR")
5177 .map(std::path::PathBuf::from)
5178 .filter(|p| {
5179 let probe = p.join(".blit-probe");
5180 if std::fs::write(&probe, b"").is_ok() {
5181 let _ = std::fs::remove_file(&probe);
5182 true
5183 } else {
5184 false
5185 }
5186 })
5187 .unwrap_or_else(std::env::temp_dir);
5188
5189 let runtime_dir_clone = runtime_dir.clone();
5190 let thread = std::thread::Builder::new()
5191 .name("compositor".into())
5192 .spawn(move || {
5193 unsafe { std::env::set_var("XDG_RUNTIME_DIR", &runtime_dir_clone) };
5194 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
5195 run_compositor(
5196 event_tx,
5197 command_rx,
5198 socket_tx,
5199 signal_tx,
5200 caps_tx,
5201 event_notify,
5202 shutdown_clone,
5203 verbose,
5204 _gpu_device,
5205 );
5206 }));
5207 if let Err(e) = result {
5208 let msg = if let Some(s) = e.downcast_ref::<&str>() {
5209 s.to_string()
5210 } else if let Some(s) = e.downcast_ref::<String>() {
5211 s.clone()
5212 } else {
5213 "unknown panic".to_string()
5214 };
5215 eprintln!("[compositor] PANIC: {msg}");
5216 }
5217 })
5218 .expect("failed to spawn compositor thread");
5219
5220 let socket_name = socket_rx.recv().expect("compositor failed to start");
5221 let socket_name = runtime_dir
5222 .join(&socket_name)
5223 .to_string_lossy()
5224 .into_owned();
5225 let loop_signal = signal_rx
5226 .recv()
5227 .expect("compositor failed to send loop signal");
5228 let (vulkan_video_encode, vulkan_video_encode_av1) = caps_rx.recv().unwrap_or((false, false));
5229
5230 CompositorHandle {
5231 event_rx,
5232 command_tx,
5233 socket_name,
5234 thread,
5235 shutdown,
5236 vulkan_video_encode,
5237 vulkan_video_encode_av1,
5238 loop_signal,
5239 }
5240}
5241
5242#[allow(clippy::too_many_arguments)]
5243fn run_compositor(
5244 event_tx: mpsc::Sender<CompositorEvent>,
5245 command_rx: mpsc::Receiver<CompositorCommand>,
5246 socket_tx: mpsc::SyncSender<String>,
5247 signal_tx: mpsc::SyncSender<LoopSignal>,
5248 caps_tx: mpsc::SyncSender<(bool, bool)>,
5249 event_notify: Arc<dyn Fn() + Send + Sync>,
5250 shutdown: Arc<AtomicBool>,
5251 verbose: bool,
5252 gpu_device: String,
5253) {
5254 let mut event_loop: EventLoop<Compositor> =
5255 EventLoop::try_new().expect("failed to create event loop");
5256 let loop_signal = event_loop.get_signal();
5257
5258 let display: Display<Compositor> = Display::new().expect("failed to create display");
5259 let dh = display.handle();
5260
5261 eprintln!("[compositor] trying Vulkan renderer for {gpu_device}");
5264 let vulkan_renderer = super::vulkan_render::VulkanRenderer::try_new(&gpu_device);
5265 let has_dmabuf = vulkan_renderer.as_ref().is_some_and(|vk| vk.has_dmabuf());
5266 eprintln!(
5267 "[compositor] Vulkan renderer: {} (dmabuf={})",
5268 vulkan_renderer.is_some(),
5269 has_dmabuf,
5270 );
5271
5272 dh.create_global::<Compositor, WlCompositor, ()>(6, ());
5274 dh.create_global::<Compositor, WlSubcompositor, ()>(1, ());
5275 dh.create_global::<Compositor, XdgWmBase, ()>(6, ());
5276 dh.create_global::<Compositor, WlShm, ()>(1, ());
5277 dh.create_global::<Compositor, WlOutput, ()>(4, ());
5278 dh.create_global::<Compositor, WlSeat, ()>(9, ());
5279 if has_dmabuf {
5284 dh.create_global::<Compositor, ZwpLinuxDmabufV1, ()>(4, ());
5285 }
5286 dh.create_global::<Compositor, WpViewporter, ()>(1, ());
5287 dh.create_global::<Compositor, WpFractionalScaleManagerV1, ()>(1, ());
5288 dh.create_global::<Compositor, ZxdgDecorationManagerV1, ()>(1, ());
5289 dh.create_global::<Compositor, WlDataDeviceManager, ()>(3, ());
5290 dh.create_global::<Compositor, ZwpPointerConstraintsV1, ()>(1, ());
5291 dh.create_global::<Compositor, ZwpRelativePointerManagerV1, ()>(1, ());
5292 dh.create_global::<Compositor, XdgActivationV1, ()>(1, ());
5293 dh.create_global::<Compositor, WpCursorShapeManagerV1, ()>(1, ());
5294 dh.create_global::<Compositor, ZwpPrimarySelectionDeviceManagerV1, ()>(1, ());
5295 dh.create_global::<Compositor, WpPresentation, ()>(1, ());
5296 dh.create_global::<Compositor, ZwpTextInputManagerV3, ()>(1, ());
5297
5298 let keymap_string = include_str!("../data/us-qwerty.xkb");
5300 let mut keymap_data = keymap_string.as_bytes().to_vec();
5301 keymap_data.push(0); let listening_socket = wayland_server::ListeningSocket::bind_auto("wayland", 0..33)
5305 .unwrap_or_else(|e| {
5306 let dir = std::env::var("XDG_RUNTIME_DIR").unwrap_or_else(|_| "(unset)".into());
5307 panic!("failed to create wayland socket in XDG_RUNTIME_DIR={dir}: {e}\nhint: ensure the directory exists and is writable by the current user");
5308 });
5309 let socket_name = listening_socket
5310 .socket_name()
5311 .unwrap()
5312 .to_string_lossy()
5313 .into_owned();
5314 socket_tx.send(socket_name).unwrap();
5315 let _ = signal_tx.send(loop_signal.clone());
5316
5317 let mut compositor = Compositor {
5318 display_handle: dh,
5319 surfaces: HashMap::new(),
5320 toplevel_surface_ids: HashMap::new(),
5321 next_surface_id: 1,
5322 shm_pools: HashMap::new(),
5323 surface_meta: HashMap::new(),
5324 dmabuf_params: HashMap::new(),
5325 vulkan_renderer,
5326 output_width: 1920,
5327 output_height: 1080,
5328 output_refresh_mhz: 60_000,
5329 output_scale_120: 120,
5330 outputs: Vec::new(),
5331 keyboards: Vec::new(),
5332 pointers: Vec::new(),
5333 keyboard_keymap_data: keymap_data,
5334 mods_depressed: 0,
5335 mods_locked: 0,
5336 serial: 0,
5337 event_tx,
5338 event_notify,
5339 loop_signal: loop_signal.clone(),
5340 pending_commits: HashMap::new(),
5341 focused_surface_id: 0,
5342 pointer_entered_id: None,
5343 pending_kb_reenter: false,
5344 gpu_device,
5345 verbose,
5346 shutdown: shutdown.clone(),
5347 last_reported_size: HashMap::new(),
5348 surface_sizes: HashMap::new(),
5349 positioners: HashMap::new(),
5350 fractional_scales: Vec::new(),
5351 data_devices: Vec::new(),
5352 selection_source: None,
5353 external_clipboard: None,
5354 primary_devices: Vec::new(),
5355 primary_source: None,
5356 external_primary: None,
5357 relative_pointers: Vec::new(),
5358 text_inputs: Vec::new(),
5359 text_input_serial: 0,
5360 next_activation_token: 1,
5361 popup_grab_stack: Vec::new(),
5362 held_buffers: HashMap::new(),
5363 cursor_rgba: HashMap::new(),
5364 };
5365
5366 {
5368 let (vve, vve_av1) = compositor
5369 .vulkan_renderer
5370 .as_ref()
5371 .map(|vk| (vk.has_video_encode(), vk.has_video_encode_av1()))
5372 .unwrap_or((false, false));
5373 let _ = caps_tx.send((vve, vve_av1));
5374 }
5375
5376 let handle = event_loop.handle();
5377
5378 let display_source = Generic::new(display, Interest::READ, calloop::Mode::Level);
5380 handle
5381 .insert_source(display_source, |_, display, state| {
5382 let d = unsafe { display.get_mut() };
5383 if let Err(e) = d.dispatch_clients(state)
5384 && state.verbose
5385 {
5386 eprintln!("[compositor] dispatch_clients error: {e}");
5387 }
5388 state.cleanup_dead_surfaces();
5389 if let Err(e) = d.flush_clients()
5390 && state.verbose
5391 {
5392 eprintln!("[compositor] flush_clients error: {e}");
5393 }
5394 Ok(PostAction::Continue)
5395 })
5396 .expect("failed to insert display source");
5397
5398 let socket_source = Generic::new(listening_socket, Interest::READ, calloop::Mode::Level);
5400 handle
5401 .insert_source(socket_source, |_, socket, state| {
5402 let ls = unsafe { socket.get_mut() };
5403 if let Some(client_stream) = ls.accept().ok().flatten()
5404 && let Err(e) = state
5405 .display_handle
5406 .insert_client(client_stream, Arc::new(ClientState))
5407 && state.verbose
5408 {
5409 eprintln!("[compositor] insert_client error: {e}");
5410 }
5411 Ok(PostAction::Continue)
5412 })
5413 .expect("failed to insert listening socket");
5414
5415 if verbose {
5416 eprintln!("[compositor] entering event loop");
5417 }
5418
5419 while !shutdown.load(Ordering::Relaxed) {
5420 while let Ok(cmd) = command_rx.try_recv() {
5422 match cmd {
5423 CompositorCommand::Shutdown => {
5424 shutdown.store(true, Ordering::Relaxed);
5425 return;
5426 }
5427 other => compositor.handle_command(other),
5428 }
5429 }
5430
5431 let poll_timeout = if compositor
5434 .vulkan_renderer
5435 .as_ref()
5436 .is_some_and(|vk| vk.has_pending())
5437 {
5438 std::time::Duration::from_millis(1)
5439 } else {
5440 std::time::Duration::from_secs(1)
5441 };
5442
5443 if let Err(e) = event_loop.dispatch(Some(poll_timeout), &mut compositor)
5444 && verbose
5445 {
5446 eprintln!("[compositor] event loop error: {e}");
5447 }
5448
5449 if let Some(ref mut vk) = compositor.vulkan_renderer
5453 && let Some((sid, w, h, pixels)) = vk.try_retire_pending()
5454 {
5455 let s120_u32 = (compositor.output_scale_120 as u32).max(120);
5456 let log_w = (w * 120).div_ceil(s120_u32);
5457 let log_h = (h * 120).div_ceil(s120_u32);
5458 compositor
5459 .pending_commits
5460 .insert(sid, (w, h, log_w, log_h, pixels));
5461 }
5462
5463 if !compositor.pending_commits.is_empty() {
5464 compositor.flush_pending_commits();
5465 }
5466
5467 if let Err(e) = compositor.display_handle.flush_clients()
5468 && verbose
5469 {
5470 eprintln!("[compositor] flush error: {e}");
5471 }
5472 }
5473
5474 if verbose {
5475 eprintln!("[compositor] event loop exited");
5476 }
5477}