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