1use crate::positioner::PositionerGeometry;
9use std::collections::{HashMap, HashSet};
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 pending_recomposite_toplevels: HashSet<u16>,
1144 focused_surface_id: u16,
1145 pointer_entered_id: Option<ObjectId>,
1147 pending_kb_reenter: bool,
1151
1152 gpu_device: String,
1153 verbose: bool,
1154 shutdown: Arc<AtomicBool>,
1155 last_reported_size: HashMap<u16, (u32, u32, u32, u32)>,
1159 surface_sizes: HashMap<u16, (i32, i32)>,
1163 positioners: HashMap<ObjectId, PositionerState>,
1165 fractional_scales: Vec<WpFractionalScaleV1>,
1168
1169 data_devices: Vec<WlDataDevice>,
1172 selection_source: Option<WlDataSource>,
1175 external_clipboard: Option<ExternalClipboard>,
1177
1178 primary_devices: Vec<ZwpPrimarySelectionDeviceV1>,
1180 primary_source: Option<ZwpPrimarySelectionSourceV1>,
1181 external_primary: Option<ExternalClipboard>,
1182
1183 relative_pointers: Vec<ZwpRelativePointerV1>,
1185
1186 text_inputs: Vec<TextInputState>,
1191 #[expect(dead_code)]
1194 text_input_serial: u32,
1195
1196 next_activation_token: u32,
1198
1199 popup_grab_stack: Vec<ObjectId>,
1204
1205 held_buffers: HashMap<ObjectId, WlBuffer>,
1212
1213 cursor_rgba: HashMap<ObjectId, (u32, u32, Vec<u8>)>,
1218}
1219
1220impl Compositor {
1221 fn next_serial(&mut self) -> u32 {
1222 self.serial = self.serial.wrapping_add(1);
1223 self.serial
1224 }
1225
1226 fn update_and_send_modifiers(&mut self, keycode: u32, pressed: bool) {
1231 let m = keycode_to_mod(keycode);
1232 if m == 0 {
1233 return;
1234 }
1235 if keycode == 58 {
1236 if pressed {
1238 self.mods_locked ^= MOD_LOCK;
1239 }
1240 } else if pressed {
1241 self.mods_depressed |= m;
1242 } else {
1243 self.mods_depressed &= !m;
1244 }
1245 let serial = self.next_serial();
1246 let focused_wl = self
1247 .toplevel_surface_ids
1248 .get(&self.focused_surface_id)
1249 .and_then(|root_id| self.surfaces.get(root_id))
1250 .map(|s| s.wl_surface.clone());
1251 for kb in &self.keyboards {
1252 if let Some(ref wl) = focused_wl
1253 && same_client(kb, wl)
1254 {
1255 kb.modifiers(serial, self.mods_depressed, 0, self.mods_locked, 0);
1256 }
1257 }
1258 }
1259
1260 fn set_keyboard_focus(&mut self, new_surface_id: u16) {
1265 let old_id = self.focused_surface_id;
1266 if old_id == new_surface_id {
1267 self.focused_surface_id = new_surface_id;
1270 if let Some(root_id) = self.toplevel_surface_ids.get(&new_surface_id)
1271 && let Some(wl_surface) = self.surfaces.get(root_id).map(|s| s.wl_surface.clone())
1272 {
1273 let serial = self.next_serial();
1274 for kb in &self.keyboards {
1275 if same_client(kb, &wl_surface) {
1276 kb.enter(serial, &wl_surface, vec![]);
1277 }
1278 }
1279 for ti in &self.text_inputs {
1280 if same_client(&ti.resource, &wl_surface) {
1281 ti.resource.enter(&wl_surface);
1282 }
1283 }
1284 }
1285 return;
1286 }
1287
1288 if old_id != 0
1290 && let Some(old_root) = self.toplevel_surface_ids.get(&old_id)
1291 && let Some(old_wl) = self.surfaces.get(old_root).map(|s| s.wl_surface.clone())
1292 {
1293 let serial = self.next_serial();
1294 for kb in &self.keyboards {
1295 if same_client(kb, &old_wl) {
1296 kb.leave(serial, &old_wl);
1297 }
1298 }
1299 for ti in &self.text_inputs {
1300 if same_client(&ti.resource, &old_wl) {
1301 ti.resource.leave(&old_wl);
1302 }
1303 }
1304 }
1305
1306 self.focused_surface_id = new_surface_id;
1307
1308 if let Some(root_id) = self.toplevel_surface_ids.get(&new_surface_id)
1310 && let Some(wl_surface) = self.surfaces.get(root_id).map(|s| s.wl_surface.clone())
1311 {
1312 let serial = self.next_serial();
1313 for kb in &self.keyboards {
1314 if same_client(kb, &wl_surface) {
1315 kb.enter(serial, &wl_surface, vec![]);
1316 }
1317 }
1318 for ti in &self.text_inputs {
1319 if same_client(&ti.resource, &wl_surface) {
1320 ti.resource.enter(&wl_surface);
1321 }
1322 }
1323 }
1324 }
1325
1326 fn allocate_surface_id(&mut self) -> u16 {
1327 let mut id = self.next_surface_id;
1328 let start = id;
1329 loop {
1330 if !self.toplevel_surface_ids.contains_key(&id) {
1331 break;
1332 }
1333 id = id.wrapping_add(1);
1334 if id == 0 {
1335 id = 1;
1336 }
1337 if id == start {
1338 break;
1339 }
1340 }
1341 self.next_surface_id = id.wrapping_add(1);
1342 if self.next_surface_id == 0 {
1343 self.next_surface_id = 1;
1344 }
1345 id
1346 }
1347
1348 fn flush_pending_commits(&mut self) {
1349 for (surface_id, (width, height, log_w, log_h)) in self.pending_native_sizes.drain() {
1356 let prev = self.last_reported_size.get(&surface_id).copied();
1357 if prev.is_none() || prev.map(|(pw, ph, _, _)| (pw, ph)) != Some((width, height)) {
1358 self.last_reported_size
1359 .insert(surface_id, (width, height, log_w, log_h));
1360 let _ = self.event_tx.send(CompositorEvent::SurfaceResized {
1361 surface_id,
1362 width: width as u16,
1363 height: height as u16,
1364 });
1365 }
1366 }
1367 let now_ms = elapsed_ms();
1370 #[allow(clippy::type_complexity)]
1371 let mut entries: Vec<((u16, u32, u32), (u32, u32, PixelData))> =
1372 self.pending_commits.drain().collect();
1373 entries.sort_by_key(|((sid, w, h), _)| (*sid, *w, *h));
1374 for ((surface_id, width, height), (_log_w, _log_h, pixels)) in entries {
1375 let _ = self.event_tx.send(CompositorEvent::SurfaceCommit {
1376 surface_id,
1377 width,
1378 height,
1379 pixels,
1380 timestamp_ms: now_ms,
1381 });
1382 }
1383 (self.event_notify)();
1384 }
1385
1386 fn read_shm_buffer(&self, buffer: &WlBuffer) -> Option<(u32, u32, PixelData)> {
1387 let data = buffer.data::<ShmBufferData>()?;
1388 let r = data.pool.read_buffer(
1389 data.offset,
1390 data.width,
1391 data.height,
1392 data.stride,
1393 data.format,
1394 );
1395 if r.is_none() {
1396 static N: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1397 let n = N.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1398 if n < 10 || n.is_multiple_of(100) {
1399 eprintln!(
1400 "[read_shm_buffer #{n}] pool.read_buffer=None off={} {}x{} stride={} fmt={:?}",
1401 data.offset, data.width, data.height, data.stride, data.format,
1402 );
1403 }
1404 }
1405 r
1406 }
1407
1408 fn read_dmabuf_buffer(&self, buffer: &WlBuffer) -> Option<(u32, u32, PixelData)> {
1409 let data = buffer.data::<DmaBufBufferData>()?;
1410 let width = data.width as u32;
1411 let height = data.height as u32;
1412 if width == 0 || height == 0 || data.planes.is_empty() {
1413 static N: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1414 let n = N.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1415 if n < 10 || n.is_multiple_of(100) {
1416 eprintln!(
1417 "[read_dmabuf_buffer #{n}] empty: {}x{} planes={}",
1418 width,
1419 height,
1420 data.planes.len()
1421 );
1422 }
1423 return None;
1424 }
1425 let plane = &data.planes[0];
1426 if matches!(
1427 data.fourcc,
1428 drm_fourcc::ARGB8888
1429 | drm_fourcc::XRGB8888
1430 | drm_fourcc::ABGR8888
1431 | drm_fourcc::XBGR8888
1432 ) {
1433 use std::os::fd::AsRawFd;
1436 let raw_fd = plane.fd.as_raw_fd();
1437 let _is_drm = {
1438 let mut link_buf = [0u8; 256];
1439 let path = format!("/proc/self/fd/{raw_fd}\0");
1440 let n = unsafe {
1441 libc::readlink(
1442 path.as_ptr() as *const _,
1443 link_buf.as_mut_ptr() as *mut _,
1444 255,
1445 )
1446 };
1447 n > 0 && link_buf[..n as usize].starts_with(b"/dev/dri/")
1448 };
1449
1450 let owned = plane.fd.try_clone().ok()?;
1454 return Some((
1455 width,
1456 height,
1457 PixelData::DmaBuf {
1458 fd: Arc::new(owned),
1459 fourcc: data.fourcc,
1460 modifier: data.modifier,
1461 stride: plane.stride,
1462 offset: plane.offset,
1463 y_invert: data.y_invert,
1464 },
1465 ));
1466 }
1467 static N: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1468 let n = N.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1469 if n < 10 || n.is_multiple_of(100) {
1470 eprintln!(
1471 "[read_dmabuf_buffer #{n}] unsupported fourcc=0x{:08x} ({}x{}) modifier=0x{:x}",
1472 data.fourcc, width, height, data.modifier,
1473 );
1474 }
1475 None
1476 }
1477
1478 fn read_buffer(&self, buffer: &WlBuffer) -> Option<(u32, u32, PixelData)> {
1479 if buffer.data::<ShmBufferData>().is_some() {
1483 return self.read_shm_buffer(buffer);
1484 }
1485 if buffer.data::<DmaBufBufferData>().is_some() {
1486 return self.read_dmabuf_buffer(buffer);
1487 }
1488 static N: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1489 let n = N.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1490 if n < 10 || n.is_multiple_of(100) {
1491 eprintln!(
1492 "[read_buffer #{n}] buffer has unknown role (neither Shm nor DmaBuf data attached)",
1493 );
1494 }
1495 None
1496 }
1497
1498 fn handle_surface_commit(&mut self, surface_id: &ObjectId) {
1499 let (root_id, toplevel_sid) = self.find_toplevel_root(surface_id);
1500
1501 let had_buffer = self
1506 .surfaces
1507 .get(surface_id)
1508 .is_some_and(|s| s.pending_buffer.is_some());
1509 {
1510 static N: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1511 let n = N.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1512 if n < 40 || n.is_multiple_of(200) {
1513 let children = self
1514 .surfaces
1515 .get(surface_id)
1516 .map(|s| s.children.len())
1517 .unwrap_or(0);
1518 eprintln!(
1519 "[commit-in #{n}] sid={surface_id:?} toplevel={toplevel_sid:?} root={root_id:?} had_buffer={had_buffer} children={children}",
1520 );
1521 }
1522 }
1523 self.apply_pending_state(surface_id);
1524
1525 let toplevel_sid = match toplevel_sid {
1526 Some(sid) => sid,
1527 None => {
1528 if let Some(held) = self.held_buffers.remove(surface_id) {
1531 held.release();
1532 }
1533 self.fire_surface_frame_callbacks(surface_id);
1536 let _ = self.display_handle.flush_clients();
1537 return;
1538 }
1539 };
1540
1541 self.composite_toplevel_into_pending(&root_id, toplevel_sid);
1542
1543 if let Some(held) = self.held_buffers.remove(surface_id) {
1548 held.release();
1549 }
1550
1551 self.fire_frame_callbacks_for_toplevel(toplevel_sid);
1556
1557 if self.pending_kb_reenter {
1562 self.pending_kb_reenter = false;
1563 let root_ids: Vec<ObjectId> = self.toplevel_surface_ids.values().cloned().collect();
1564 for root_id in root_ids {
1565 let wl = self.surfaces.get(&root_id).map(|s| s.wl_surface.clone());
1566 if let Some(wl) = wl {
1567 let serial = self.next_serial();
1568 for kb in &self.keyboards {
1569 if same_client(kb, &wl) {
1570 kb.leave(serial, &wl);
1571 }
1572 }
1573 let serial = self.next_serial();
1574 for kb in &self.keyboards {
1575 if same_client(kb, &wl) {
1576 kb.enter(serial, &wl, vec![]);
1577 }
1578 }
1579 }
1580 }
1581 let _ = self.display_handle.flush_clients();
1582 }
1583
1584 if self.verbose {
1585 let cache_entries = self.surface_meta.len();
1586 let has_pending = self
1587 .pending_commits
1588 .keys()
1589 .any(|(sid, _, _)| *sid == toplevel_sid);
1590 static COMMIT_COUNT: std::sync::atomic::AtomicU64 =
1591 std::sync::atomic::AtomicU64::new(0);
1592 let n = COMMIT_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1593 if n < 5 || n.is_multiple_of(1000) {
1594 eprintln!(
1595 "[commit #{n}] sid={surface_id:?} root={root_id:?} cache={cache_entries} pending={has_pending} buf={had_buffer}",
1596 );
1597 }
1598 }
1599 }
1600
1601 fn composite_toplevel_into_pending(&mut self, root_id: &ObjectId, toplevel_sid: u16) {
1611 let s120 = self.output_scale_120;
1615 let target_phys = self.surface_sizes.get(&toplevel_sid).map(|&(lw, lh)| {
1616 let pw = super::render::to_physical(lw as u32, s120 as u32);
1617 let ph = super::render::to_physical(lh as u32, s120 as u32);
1618 (pw, ph)
1619 });
1620 let composited = if let Some(ref mut vk) = self.vulkan_renderer {
1621 vk.render_tree_sized(
1622 root_id,
1623 &self.surfaces,
1624 &self.surface_meta,
1625 s120,
1626 target_phys,
1627 toplevel_sid,
1628 )
1629 } else {
1630 Vec::new()
1631 };
1632
1633 let s120_u32 = (s120 as u32).max(120);
1640 if let Some((nw, nh)) = target_phys {
1641 let nlog_w = (nw * 120).div_ceil(s120_u32);
1642 let nlog_h = (nh * 120).div_ceil(s120_u32);
1643 self.pending_native_sizes
1644 .insert(toplevel_sid, (nw, nh, nlog_w, nlog_h));
1645 } else if let Some((sid, nw, nh, _)) = composited
1646 .iter()
1647 .max_by_key(|(_, w, h, _)| (*w as u64) * (*h as u64))
1648 {
1649 let nlog_w = (nw * 120).div_ceil(s120_u32);
1650 let nlog_h = (nh * 120).div_ceil(s120_u32);
1651 self.pending_native_sizes
1652 .insert(*sid, (*nw, *nh, nlog_w, nlog_h));
1653 }
1654
1655 for (result_sid, w, h, pixels) in composited {
1656 if pixels.is_empty() {
1657 continue;
1658 }
1659 let kind = match &pixels {
1660 PixelData::Bgra(_) => "bgra",
1661 PixelData::Rgba(_) => "rgba",
1662 PixelData::Nv12 { .. } => "nv12",
1663 PixelData::VaSurface { .. } => "va-surface",
1664 PixelData::Nv12DmaBuf { .. } => "nv12-dmabuf",
1665 PixelData::Encoded { .. } => "vulkan-encoded",
1666 PixelData::DmaBuf { fd, .. } => {
1667 use std::os::fd::AsRawFd;
1668 let raw = fd.as_raw_fd();
1669 let mut lb = [0u8; 128];
1670 let p = format!("/proc/self/fd/{raw}\0");
1671 let n = unsafe {
1672 libc::readlink(p.as_ptr() as *const _, lb.as_mut_ptr() as *mut _, 127)
1673 };
1674 if n > 0 && lb[..n as usize].starts_with(b"/dev/dri/") {
1675 "dmabuf-drm"
1676 } else {
1677 "dmabuf-anon"
1678 }
1679 }
1680 };
1681 if self.verbose {
1682 static LC: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1683 let lc = LC.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1684 if lc < 3 || lc.is_multiple_of(1000) {
1685 eprintln!("[pending #{lc}] {w}x{h} kind={kind}");
1686 }
1687 }
1688 let log_w = (w * 120).div_ceil(s120_u32);
1693 let log_h = (h * 120).div_ceil(s120_u32);
1694 self.pending_commits
1695 .insert((result_sid, w, h), (log_w, log_h, pixels));
1696 }
1697 }
1698
1699 fn surface_absolute_position(&self, surface_id: &ObjectId) -> (i32, i32) {
1703 let mut x = 0i32;
1704 let mut y = 0i32;
1705 let mut current = surface_id.clone();
1706 while let Some(surf) = self.surfaces.get(¤t) {
1707 x += surf.subsurface_position.0;
1708 y += surf.subsurface_position.1;
1709 match surf.parent_surface_id {
1710 Some(ref parent) => current = parent.clone(),
1711 None => break,
1712 }
1713 }
1714 (x, y)
1715 }
1716
1717 fn find_toplevel_root(&self, surface_id: &ObjectId) -> (ObjectId, Option<u16>) {
1718 let mut current = surface_id.clone();
1719 loop {
1720 match self.surfaces.get(¤t) {
1721 Some(surf) => {
1722 if let Some(ref parent) = surf.parent_surface_id {
1723 current = parent.clone();
1724 } else {
1725 return (
1726 current,
1727 if surf.surface_id > 0 {
1728 Some(surf.surface_id)
1729 } else {
1730 None
1731 },
1732 );
1733 }
1734 }
1735 None => return (current, None),
1736 }
1737 }
1738 }
1739
1740 fn collect_surface_tree(&self, root_id: &ObjectId) -> Vec<ObjectId> {
1741 let mut result = Vec::new();
1742 self.collect_tree_recursive(root_id, &mut result);
1743 result
1744 }
1745
1746 fn collect_tree_recursive(&self, surface_id: &ObjectId, result: &mut Vec<ObjectId>) {
1747 result.push(surface_id.clone());
1748 if let Some(surf) = self.surfaces.get(surface_id) {
1749 for child_id in &surf.children {
1750 self.collect_tree_recursive(child_id, result);
1751 }
1752 }
1753 }
1754
1755 fn hit_test_surface_at(
1760 &self,
1761 root_id: &ObjectId,
1762 x: f64,
1763 y: f64,
1764 ) -> Option<(WlSurface, f64, f64)> {
1765 self.hit_test_recursive(root_id, x, y, 0, 0).or_else(|| {
1766 self.surfaces
1768 .get(root_id)
1769 .map(|s| (s.wl_surface.clone(), x, y))
1770 })
1771 }
1772
1773 fn hit_test_recursive(
1774 &self,
1775 surface_id: &ObjectId,
1776 x: f64,
1777 y: f64,
1778 offset_x: i32,
1779 offset_y: i32,
1780 ) -> Option<(WlSurface, f64, f64)> {
1781 let surf = self.surfaces.get(surface_id)?;
1782 let sx = offset_x + surf.subsurface_position.0;
1783 let sy = offset_y + surf.subsurface_position.1;
1784
1785 for child_id in surf.children.iter().rev() {
1787 if let Some(hit) = self.hit_test_recursive(child_id, x, y, sx, sy) {
1788 return Some(hit);
1789 }
1790 }
1791
1792 if let Some(sm) = self.surface_meta.get(surface_id) {
1794 let s = sm.scale.max(1) as f64;
1795 let (w, h) = (sm.width, sm.height);
1796 let (lw, lh) = surf
1799 .viewport_destination
1800 .filter(|&(dw, dh)| dw > 0 && dh > 0)
1801 .map(|(dw, dh)| (dw as f64, dh as f64))
1802 .unwrap_or((w as f64 / s, h as f64 / s));
1803 let lx = x - sx as f64;
1804 let ly = y - sy as f64;
1805 if lx >= 0.0 && ly >= 0.0 && lx < lw && ly < lh {
1806 return Some((surf.wl_surface.clone(), lx, ly));
1807 }
1808 }
1809 None
1810 }
1811
1812 fn apply_pending_state(&mut self, surface_id: &ObjectId) {
1825 let (buffer, scale, is_cursor) = {
1826 let Some(surf) = self.surfaces.get_mut(surface_id) else {
1827 return;
1828 };
1829 let buffer = surf.pending_buffer.take();
1830 let scale = surf.pending_buffer_scale;
1831 surf.buffer_scale = scale;
1832 surf.viewport_destination = surf.pending_viewport_destination;
1833 surf.is_opaque = surf.pending_opaque;
1834 surf.pending_damage = false;
1835 if let Some(pos) = surf.pending_subsurface_position.take() {
1836 surf.subsurface_position = pos;
1837 }
1838 (buffer, scale, surf.is_cursor)
1839 };
1840 let Some(buf) = buffer else { return };
1841
1842 if let Some(old) = self.held_buffers.remove(surface_id) {
1845 old.release();
1846 }
1847
1848 if !is_cursor && let Some(shm) = buf.data::<ShmBufferData>() {
1854 let w = shm.width as u32;
1855 let h = shm.height as u32;
1856 let stride = shm.stride as usize;
1857 let offset = shm.offset as usize;
1858 let format = shm.format;
1859 if w > 0
1860 && h > 0
1861 && let Some(ref mut vk) = self.vulkan_renderer
1862 {
1863 let swap_rb =
1864 !matches!(format, wl_shm::Format::Abgr8888 | wl_shm::Format::Xbgr8888);
1865 let force_opaque =
1866 matches!(format, wl_shm::Format::Xrgb8888 | wl_shm::Format::Xbgr8888);
1867 let row_bytes = w as usize * 4;
1868 let uploaded = shm
1869 .pool
1870 .with_mmap(|slice| {
1871 if offset + stride * (h as usize - 1) + row_bytes > slice.len() {
1872 return false;
1873 }
1874 vk.upload_surface_shm_mmap(
1875 surface_id,
1876 slice,
1877 offset,
1878 stride,
1879 w,
1880 h,
1881 swap_rb,
1882 force_opaque,
1883 )
1884 })
1885 .unwrap_or(false);
1886 if uploaded {
1887 self.surface_meta.insert(
1888 surface_id.clone(),
1889 super::render::SurfaceMeta {
1890 width: w,
1891 height: h,
1892 scale,
1893 y_invert: false,
1894 },
1895 );
1896 buf.release();
1897 return;
1898 }
1899 }
1900 }
1901
1902 if let Some((w, h, pixels)) = self.read_buffer(&buf) {
1903 let y_invert = matches!(pixels, PixelData::DmaBuf { y_invert: true, .. });
1904
1905 if let Some(ref mut vk) = self.vulkan_renderer {
1907 vk.upload_surface(surface_id, &pixels, w, h);
1908 }
1909
1910 self.surface_meta.insert(
1912 surface_id.clone(),
1913 super::render::SurfaceMeta {
1914 width: w,
1915 height: h,
1916 scale,
1917 y_invert,
1918 },
1919 );
1920
1921 if is_cursor {
1924 let rgba = pixels.to_rgba(w, h);
1925 if !rgba.is_empty() {
1926 self.cursor_rgba.insert(surface_id.clone(), (w, h, rgba));
1927 }
1928 }
1929
1930 if pixels.is_dmabuf() {
1931 self.held_buffers.insert(surface_id.clone(), buf);
1934 } else {
1935 buf.release();
1938 }
1939 } else {
1940 buf.release();
1941 }
1942 }
1943
1944 fn fire_surface_frame_callbacks(&mut self, surface_id: &ObjectId) {
1945 let (callbacks, feedbacks) = {
1946 let Some(surf) = self.surfaces.get_mut(surface_id) else {
1947 return;
1948 };
1949 (
1950 std::mem::take(&mut surf.pending_frame_callbacks),
1951 std::mem::take(&mut surf.pending_presentation_feedbacks),
1952 )
1953 };
1954 let time = elapsed_ms();
1955 for cb in callbacks {
1956 cb.done(time);
1957 }
1958 if !feedbacks.is_empty() {
1959 let (sec, nsec) = monotonic_timespec();
1960 for fb in feedbacks {
1963 for output in &self.outputs {
1964 if same_client(&fb, output) {
1965 fb.sync_output(output);
1966 }
1967 }
1968 let refresh_ns = if self.output_refresh_mhz > 0 {
1970 (1_000_000_000_000u64 / self.output_refresh_mhz as u64) as u32
1971 } else {
1972 0
1973 };
1974 fb.presented(
1975 (sec >> 32) as u32,
1976 sec as u32,
1977 nsec as u32,
1978 refresh_ns,
1979 0, 0, WpPresentationFeedbackKind::empty(),
1982 );
1983 }
1984 }
1985 }
1986
1987 fn cleanup_dead_surfaces(&mut self) {
1992 self.fractional_scales.retain(|fs| fs.is_alive());
1994 self.outputs.retain(|o| o.is_alive());
1995 self.keyboards.retain(|k| k.is_alive());
1996 self.pointers.retain(|p| p.is_alive());
1997 self.data_devices.retain(|d| d.is_alive());
1998 self.primary_devices.retain(|d| d.is_alive());
1999 self.relative_pointers.retain(|p| p.is_alive());
2000 self.text_inputs.retain(|ti| ti.resource.is_alive());
2001 self.shm_pools.retain(|_, p| p.resource.is_alive());
2002 self.dmabuf_params.retain(|_, p| p.resource.is_alive());
2003 self.positioners.retain(|_, p| p.resource.is_alive());
2004
2005 let dead: Vec<ObjectId> = self
2006 .surfaces
2007 .iter()
2008 .filter(|(_, surf)| !surf.wl_surface.is_alive())
2009 .map(|(id, _)| id.clone())
2010 .collect();
2011
2012 for proto_id in &dead {
2013 self.surface_meta.remove(proto_id);
2014 if let Some(ref mut vk) = self.vulkan_renderer {
2015 vk.remove_surface(proto_id);
2016 }
2017 if let Some(held) = self.held_buffers.remove(proto_id) {
2018 held.release();
2019 }
2020 if let Some(surf) = self.surfaces.remove(proto_id) {
2021 for fb in surf.pending_presentation_feedbacks {
2024 fb.discarded();
2025 }
2026 if let Some(ref parent_id) = surf.parent_surface_id
2027 && let Some(parent) = self.surfaces.get_mut(parent_id)
2028 {
2029 parent.children.retain(|c| c != proto_id);
2030 }
2031 if surf.surface_id > 0 {
2032 self.toplevel_surface_ids.remove(&surf.surface_id);
2033 self.last_reported_size.remove(&surf.surface_id);
2034 self.surface_sizes.remove(&surf.surface_id);
2035 if let Some(ref mut vk) = self.vulkan_renderer {
2036 vk.destroy_external_outputs_for_surface(surf.surface_id as u32);
2037 }
2038 let _ = self.event_tx.send(CompositorEvent::SurfaceDestroyed {
2039 surface_id: surf.surface_id,
2040 });
2041 (self.event_notify)();
2042 }
2043 }
2044 }
2045 }
2046
2047 fn fire_frame_callbacks_for_toplevel(&mut self, toplevel_sid: u16) {
2048 let Some(root_id) = self.toplevel_surface_ids.get(&toplevel_sid).cloned() else {
2049 return;
2050 };
2051 let tree = self.collect_surface_tree(&root_id);
2052 for sid in &tree {
2053 self.fire_surface_frame_callbacks(sid);
2054 }
2055 let _ = self.display_handle.flush_clients();
2056 }
2057
2058 fn handle_cursor_commit(&mut self, surface_id: &ObjectId) {
2059 self.apply_pending_state(surface_id);
2060 let hotspot = self
2061 .surfaces
2062 .get(surface_id)
2063 .map(|s| s.cursor_hotspot)
2064 .unwrap_or((0, 0));
2065 if let Some((w, h, rgba)) = self.cursor_rgba.get(surface_id)
2066 && !rgba.is_empty()
2067 {
2068 let _ = self.event_tx.send(CompositorEvent::SurfaceCursor {
2069 surface_id: self.focused_surface_id,
2070 cursor: CursorImage::Custom {
2071 hotspot_x: hotspot.0 as u16,
2072 hotspot_y: hotspot.1 as u16,
2073 width: *w as u16,
2074 height: *h as u16,
2075 rgba: rgba.clone(),
2076 },
2077 });
2078 }
2079 self.fire_surface_frame_callbacks(surface_id);
2080 let _ = self.display_handle.flush_clients();
2081 }
2082
2083 fn handle_command(&mut self, cmd: CompositorCommand) {
2084 match cmd {
2085 CompositorCommand::KeyInput {
2086 surface_id: _,
2087 keycode,
2088 pressed,
2089 } => {
2090 let serial = self.next_serial();
2091 let time = elapsed_ms();
2092 let state = if pressed {
2093 wl_keyboard::KeyState::Pressed
2094 } else {
2095 wl_keyboard::KeyState::Released
2096 };
2097 let focused_wl = self
2098 .toplevel_surface_ids
2099 .get(&self.focused_surface_id)
2100 .and_then(|root_id| self.surfaces.get(root_id))
2101 .map(|s| s.wl_surface.clone());
2102 for kb in &self.keyboards {
2103 if let Some(ref wl) = focused_wl
2104 && same_client(kb, wl)
2105 {
2106 kb.key(serial, time, keycode, state);
2107 }
2108 }
2109 self.update_and_send_modifiers(keycode, pressed);
2114 let _ = self.display_handle.flush_clients();
2115 }
2116 CompositorCommand::TextInput { text } => {
2117 let focused_wl = self
2118 .toplevel_surface_ids
2119 .get(&self.focused_surface_id)
2120 .and_then(|root_id| self.surfaces.get(root_id))
2121 .map(|s| s.wl_surface.clone());
2122 let Some(focused_wl) = focused_wl else { return };
2123
2124 const KEY_LEFTSHIFT: u32 = 42;
2140 let saved_mods_depressed = self.mods_depressed;
2141 for ch in text.chars() {
2142 if let Some((kc, need_shift)) = char_to_keycode(ch) {
2143 let time = elapsed_ms();
2144 if need_shift {
2145 let serial = self.next_serial();
2146 for kb in &self.keyboards {
2147 if same_client(kb, &focused_wl) {
2148 kb.key(
2149 serial,
2150 time,
2151 KEY_LEFTSHIFT,
2152 wl_keyboard::KeyState::Pressed,
2153 );
2154 }
2155 }
2156 self.update_and_send_modifiers(KEY_LEFTSHIFT, true);
2157 }
2158 let serial = self.next_serial();
2159 for kb in &self.keyboards {
2160 if same_client(kb, &focused_wl) {
2161 kb.key(serial, time, kc, wl_keyboard::KeyState::Pressed);
2162 }
2163 }
2164 let serial = self.next_serial();
2165 for kb in &self.keyboards {
2166 if same_client(kb, &focused_wl) {
2167 kb.key(serial, time, kc, wl_keyboard::KeyState::Released);
2168 }
2169 }
2170 if need_shift {
2171 let serial = self.next_serial();
2172 for kb in &self.keyboards {
2173 if same_client(kb, &focused_wl) {
2174 kb.key(
2175 serial,
2176 time,
2177 KEY_LEFTSHIFT,
2178 wl_keyboard::KeyState::Released,
2179 );
2180 }
2181 }
2182 self.update_and_send_modifiers(KEY_LEFTSHIFT, false);
2183 }
2184 }
2185 }
2188 if self.mods_depressed != saved_mods_depressed {
2192 self.mods_depressed = saved_mods_depressed;
2193 let serial = self.next_serial();
2194 for kb in &self.keyboards {
2195 if same_client(kb, &focused_wl) {
2196 kb.modifiers(serial, self.mods_depressed, 0, self.mods_locked, 0);
2197 }
2198 }
2199 }
2200 let _ = self.display_handle.flush_clients();
2201 }
2202 CompositorCommand::PointerMotion { surface_id, x, y } => {
2203 let time = elapsed_ms();
2204 let (mut x, mut y) =
2209 if let Some(&(cw, ch, lw, lh)) = self.last_reported_size.get(&surface_id) {
2210 let sx = if cw > 0 { lw as f64 / cw as f64 } else { 1.0 };
2211 let sy = if ch > 0 { lh as f64 / ch as f64 } else { 1.0 };
2212 (x * sx, y * sy)
2213 } else {
2214 (x, y)
2215 };
2216 if let Some((gx, gy, _, _)) = self
2220 .toplevel_surface_ids
2221 .get(&surface_id)
2222 .and_then(|rid| self.surfaces.get(rid))
2223 .and_then(|s| s.xdg_geometry)
2224 {
2225 x += gx as f64;
2226 y += gy as f64;
2227 }
2228 let target_wl = self
2231 .toplevel_surface_ids
2232 .get(&surface_id)
2233 .and_then(|root_id| self.hit_test_surface_at(root_id, x, y))
2234 .map(|(wl_surface, lx, ly)| (wl_surface.id(), wl_surface, lx, ly));
2235
2236 static PTR_DBG: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
2237 let pn = PTR_DBG.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2238 if pn < 5 || pn.is_multiple_of(500) {
2239 let root = self.toplevel_surface_ids.get(&surface_id).cloned();
2240 let lrs = self.last_reported_size.get(&surface_id).copied();
2241 eprintln!(
2242 "[pointer #{pn}] sid={surface_id} logical=({x:.1},{y:.1}) lrs={lrs:?} root={root:?} hit={:?}",
2243 target_wl.as_ref().map(|(pid, _, lx, ly)| format!(
2244 "proto={pid:?} local=({lx:.1},{ly:.1})"
2245 ))
2246 );
2247 }
2248 if let Some((proto_id, wl_surface, lx, ly)) = target_wl {
2249 if self.pointer_entered_id.as_ref() != Some(&proto_id) {
2250 let serial = self.next_serial();
2251 let matching_ptrs = self
2252 .pointers
2253 .iter()
2254 .filter(|p| same_client(*p, &wl_surface))
2255 .count();
2256 eprintln!(
2257 "[pointer-enter] proto={proto_id:?} matching_ptrs={matching_ptrs} total_ptrs={}",
2258 self.pointers.len()
2259 );
2260 if self.pointer_entered_id.is_some() {
2262 let old_wl = self
2263 .surfaces
2264 .values()
2265 .find(|s| Some(s.wl_surface.id()) == self.pointer_entered_id)
2266 .map(|s| s.wl_surface.clone());
2267 if let Some(old_wl) = old_wl {
2268 for ptr in &self.pointers {
2269 if same_client(ptr, &old_wl) {
2270 ptr.leave(serial, &old_wl);
2271 ptr.frame();
2272 }
2273 }
2274 }
2275 }
2276 for ptr in &self.pointers {
2277 if same_client(ptr, &wl_surface) {
2278 ptr.enter(serial, &wl_surface, lx, ly);
2279 }
2280 }
2281 self.pointer_entered_id = Some(proto_id);
2282 }
2283 for ptr in &self.pointers {
2284 if same_client(ptr, &wl_surface) {
2285 ptr.motion(time, lx, ly);
2286 ptr.frame();
2287 }
2288 }
2289 }
2290 let _ = self.display_handle.flush_clients();
2293 }
2294 CompositorCommand::PointerButton {
2295 surface_id: _,
2296 button,
2297 pressed,
2298 } => {
2299 let serial = self.next_serial();
2300 let time = elapsed_ms();
2301 let state = if pressed {
2302 wl_pointer::ButtonState::Pressed
2303 } else {
2304 wl_pointer::ButtonState::Released
2305 };
2306
2307 if pressed && !self.popup_grab_stack.is_empty() {
2310 let click_on_grabbed = self.pointer_entered_id.as_ref().is_some_and(|eid| {
2311 self.popup_grab_stack.iter().any(|gid| {
2312 self.surfaces
2313 .get(gid)
2314 .is_some_and(|s| s.wl_surface.id() == *eid)
2315 })
2316 });
2317 if !click_on_grabbed {
2318 while let Some(grab_wl_id) = self.popup_grab_stack.pop() {
2320 if let Some(surf) = self.surfaces.get(&grab_wl_id)
2321 && let Some(ref popup) = surf.xdg_popup
2322 {
2323 popup.popup_done();
2324 }
2325 }
2326 let _ = self.display_handle.flush_clients();
2327 }
2328 }
2329
2330 let focused_wl = self
2331 .surfaces
2332 .values()
2333 .find(|s| Some(s.wl_surface.id()) == self.pointer_entered_id)
2334 .map(|s| s.wl_surface.clone());
2335 for ptr in &self.pointers {
2336 if let Some(ref wl) = focused_wl
2337 && same_client(ptr, wl)
2338 {
2339 ptr.button(serial, time, button, state);
2340 ptr.frame();
2341 }
2342 }
2343 let _ = self.display_handle.flush_clients();
2344 }
2345 CompositorCommand::PointerAxis {
2346 surface_id: _,
2347 axis,
2348 value,
2349 } => {
2350 let time = elapsed_ms();
2351 let wl_axis = if axis == 0 {
2352 wl_pointer::Axis::VerticalScroll
2353 } else {
2354 wl_pointer::Axis::HorizontalScroll
2355 };
2356 let focused_wl = self
2357 .surfaces
2358 .values()
2359 .find(|s| Some(s.wl_surface.id()) == self.pointer_entered_id)
2360 .map(|s| s.wl_surface.clone());
2361 for ptr in &self.pointers {
2362 if let Some(ref wl) = focused_wl
2363 && same_client(ptr, wl)
2364 {
2365 ptr.axis(time, wl_axis, value);
2366 ptr.frame();
2367 }
2368 }
2369 let _ = self.display_handle.flush_clients();
2370 }
2371 CompositorCommand::SurfaceResize {
2372 surface_id,
2373 width,
2374 height,
2375 scale_120,
2376 } => {
2377 let s_in = (scale_120 as i32).max(120);
2380 let w = (width as i32) * 120 / s_in;
2381 let h = (height as i32) * 120 / s_in;
2382 self.surface_sizes.insert(surface_id, (w, h));
2383
2384 let mut output_changed = false;
2387
2388 if scale_120 > 0 && scale_120 != self.output_scale_120 {
2390 self.output_scale_120 = scale_120;
2391 output_changed = true;
2392 }
2393
2394 let s120 = self.output_scale_120 as i32;
2395
2396 let (max_w, max_h) = self
2401 .surface_sizes
2402 .values()
2403 .fold((0i32, 0i32), |(mw, mh), &(sw, sh)| (mw.max(sw), mh.max(sh)));
2404 let max_w = max_w.max(1);
2406 let max_h = max_h.max(1);
2407 if max_w != self.output_width || max_h != self.output_height {
2408 self.output_width = max_w;
2409 self.output_height = max_h;
2410 output_changed = true;
2411 }
2412
2413 if output_changed {
2417 let int_scale = ((s120) + 119) / 120;
2418 for output in &self.outputs {
2419 output.geometry(
2420 0,
2421 0,
2422 0,
2423 0,
2424 wl_output::Subpixel::None,
2425 "blit".to_string(),
2426 "virtual".to_string(),
2427 wl_output::Transform::Normal,
2428 );
2429 let mode_w = self.output_width * s120 / 120;
2431 let mode_h = self.output_height * s120 / 120;
2432 output.mode(
2433 wl_output::Mode::Current | wl_output::Mode::Preferred,
2434 mode_w,
2435 mode_h,
2436 self.output_refresh_mhz as i32,
2437 );
2438 if output.version() >= 2 {
2439 output.scale(int_scale);
2440 }
2441 }
2442 for fs in &self.fractional_scales {
2443 fs.preferred_scale(s120 as u32);
2444 }
2445 }
2446
2447 if output_changed {
2450 for output in &self.outputs {
2451 if output.version() >= 2 {
2452 output.done();
2453 }
2454 }
2455 }
2456
2457 let states = xdg_toplevel_states(&[
2458 xdg_toplevel::State::Activated,
2459 xdg_toplevel::State::Maximized,
2460 ]);
2461
2462 if output_changed {
2463 for (&sid, root_id) in &self.toplevel_surface_ids {
2467 let (lw, lh) = self.surface_sizes.get(&sid).copied().unwrap_or((w, h));
2468 if let Some(surf) = self.surfaces.get(root_id) {
2469 if let Some(ref tl) = surf.xdg_toplevel {
2470 tl.configure(lw, lh, states.clone());
2471 }
2472 if let Some(ref xs) = surf.xdg_surface {
2473 let serial = self.serial.wrapping_add(1);
2474 self.serial = serial;
2475 xs.configure(serial);
2476 }
2477 }
2478 }
2479 let all_sids: Vec<u16> = self.toplevel_surface_ids.keys().copied().collect();
2482 for sid in all_sids {
2483 self.fire_frame_callbacks_for_toplevel(sid);
2484 }
2485
2486 self.pointer_entered_id = None;
2489 self.pending_kb_reenter = true;
2490 } else {
2491 if let Some(root_id) = self.toplevel_surface_ids.get(&surface_id)
2496 && let Some(surf) = self.surfaces.get(root_id)
2497 {
2498 if let Some(ref tl) = surf.xdg_toplevel {
2499 tl.configure(w, h, states);
2500 }
2501 if let Some(ref xs) = surf.xdg_surface {
2502 let serial = self.serial.wrapping_add(1);
2503 self.serial = serial;
2504 xs.configure(serial);
2505 }
2506 }
2507 self.fire_frame_callbacks_for_toplevel(surface_id);
2508 }
2509 let _ = self.display_handle.flush_clients();
2510 }
2511 CompositorCommand::SurfaceFocus { surface_id } => {
2512 self.set_keyboard_focus(surface_id);
2513 let _ = self.display_handle.flush_clients();
2514 }
2515 CompositorCommand::SurfaceClose { surface_id } => {
2516 if let Some(root_id) = self.toplevel_surface_ids.get(&surface_id)
2517 && let Some(surf) = self.surfaces.get(root_id)
2518 && let Some(ref tl) = surf.xdg_toplevel
2519 {
2520 tl.close();
2521 }
2522 let _ = self.display_handle.flush_clients();
2523 }
2524 CompositorCommand::ClipboardOffer { mime_type, data } => {
2525 self.external_clipboard = Some(ExternalClipboard { mime_type, data });
2526 if let Some(src) = self.selection_source.take() {
2532 src.cancelled();
2533 }
2534 self.offer_external_clipboard();
2535 }
2536 CompositorCommand::Capture {
2537 surface_id,
2538 scale_120,
2539 reply,
2540 } => {
2541 let cap_s120 = if scale_120 > 0 {
2544 scale_120
2545 } else {
2546 self.output_scale_120
2547 };
2548 let result = if let Some(root_id) = self.toplevel_surface_ids.get(&surface_id) {
2549 if let Some(ref mut vk) = self.vulkan_renderer {
2550 vk.render_tree_sized(
2557 root_id,
2558 &self.surfaces,
2559 &self.surface_meta,
2560 cap_s120,
2561 None,
2562 surface_id,
2563 )
2564 .into_iter()
2565 .next()
2566 .map(|(_sid, w, h, pixels)| {
2567 let rgba = pixels.to_rgba(w, h);
2568 (w, h, rgba)
2569 })
2570 } else {
2571 None
2572 }
2573 } else {
2574 None
2575 };
2576 let _ = reply.send(result);
2577 }
2578 CompositorCommand::RequestFrame { surface_id } => {
2579 self.fire_frame_callbacks_for_toplevel(surface_id);
2580 }
2581 CompositorCommand::ReleaseKeys { keycodes } => {
2582 let time = elapsed_ms();
2583 let focused_wl = self
2584 .toplevel_surface_ids
2585 .get(&self.focused_surface_id)
2586 .and_then(|root_id| self.surfaces.get(root_id))
2587 .map(|s| s.wl_surface.clone());
2588 for keycode in &keycodes {
2589 let serial = self.next_serial();
2590 for kb in &self.keyboards {
2591 if let Some(ref wl) = focused_wl
2592 && same_client(kb, wl)
2593 {
2594 kb.key(serial, time, *keycode, wl_keyboard::KeyState::Released);
2595 }
2596 }
2597 }
2598 for keycode in &keycodes {
2600 self.update_and_send_modifiers(*keycode, false);
2601 }
2602 let _ = self.display_handle.flush_clients();
2603 }
2604 CompositorCommand::ClipboardListMimes { reply } => {
2605 let mimes = self.collect_clipboard_mime_types();
2606 let _ = reply.send(mimes);
2607 }
2608 CompositorCommand::ClipboardGet { mime_type, reply } => {
2609 let data = self.get_clipboard_content(&mime_type);
2610 let _ = reply.send(data);
2611 }
2612 CompositorCommand::SetExternalOutputBuffers {
2613 surface_id,
2614 target_w,
2615 target_h,
2616 buffers,
2617 } => {
2618 let installed = !buffers.is_empty();
2619 if let Some(ref mut vk) = self.vulkan_renderer {
2620 vk.set_external_output_buffers(surface_id, target_w, target_h, buffers);
2621 }
2622 if installed && self.toplevel_surface_ids.contains_key(&(surface_id as u16)) {
2633 self.pending_recomposite_toplevels.insert(surface_id as u16);
2634 }
2635 }
2636 CompositorCommand::RegisterDownscaleTarget {
2637 surface_id,
2638 target_w,
2639 target_h,
2640 } => {
2641 if let Some(ref mut vk) = self.vulkan_renderer {
2642 vk.register_downscale_target(surface_id, target_w, target_h);
2643 }
2644 if self.toplevel_surface_ids.contains_key(&(surface_id as u16)) {
2647 self.pending_recomposite_toplevels.insert(surface_id as u16);
2648 }
2649 }
2650 CompositorCommand::ClearDownscaleTarget {
2651 surface_id,
2652 target_w,
2653 target_h,
2654 } => {
2655 if let Some(ref mut vk) = self.vulkan_renderer {
2656 vk.clear_downscale_target(surface_id, target_w, target_h);
2657 }
2658 }
2659 CompositorCommand::SetRefreshRate { mhz } => {
2660 let diff = (mhz as i64 - self.output_refresh_mhz as i64).unsigned_abs();
2664 if diff > 2000 && mhz > 0 {
2665 self.output_refresh_mhz = mhz;
2666 let s120 = self.output_scale_120 as i32;
2667 let mode_w = self.output_width * s120 / 120;
2668 let mode_h = self.output_height * s120 / 120;
2669 for output in &self.outputs {
2670 output.mode(
2671 wl_output::Mode::Current | wl_output::Mode::Preferred,
2672 mode_w,
2673 mode_h,
2674 mhz as i32,
2675 );
2676 if output.version() >= 2 {
2677 output.done();
2678 }
2679 }
2680 let _ = self.display_handle.flush_clients();
2681 }
2682 }
2683 CompositorCommand::SetVulkanEncoder {
2684 surface_id,
2685 codec,
2686 qp,
2687 width,
2688 height,
2689 } => {
2690 if let Some(ref mut vk) = self.vulkan_renderer {
2691 vk.create_vulkan_encoder(surface_id, codec, qp, width, height);
2692 }
2693 }
2694 CompositorCommand::RequestVulkanKeyframe { surface_id } => {
2695 if let Some(ref mut vk) = self.vulkan_renderer {
2696 vk.request_encoder_keyframe(surface_id);
2697 }
2698 }
2699 CompositorCommand::DestroyVulkanEncoder { surface_id } => {
2700 if let Some(ref mut vk) = self.vulkan_renderer {
2701 vk.destroy_vulkan_encoder(surface_id);
2702 }
2703 }
2704 CompositorCommand::Shutdown => {
2705 self.shutdown.store(true, Ordering::Relaxed);
2706 self.loop_signal.stop();
2707 }
2708 }
2709 }
2710
2711 fn send_dmabuf_feedback(&self, fb: &ZwpLinuxDmabufFeedbackV1) {
2715 use std::os::unix::fs::MetadataExt;
2716
2717 let modifiers: &[(u32, u64)] = self
2719 .vulkan_renderer
2720 .as_ref()
2721 .map(|vk| vk.supported_dmabuf_modifiers.as_slice())
2722 .unwrap_or(&[]);
2723
2724 let entry_size = 16usize;
2726 let table_size = modifiers.len() * entry_size;
2727 let mut table_data = vec![0u8; table_size];
2728 for (i, &(fmt, modifier)) in modifiers.iter().enumerate() {
2729 let off = i * entry_size;
2730 table_data[off..off + 4].copy_from_slice(&fmt.to_ne_bytes());
2731 table_data[off + 8..off + 16].copy_from_slice(&modifier.to_ne_bytes());
2733 }
2734
2735 let name = c"dmabuf-feedback-table";
2737 let raw_fd = unsafe { libc::memfd_create(name.as_ptr(), libc::MFD_CLOEXEC) };
2738 if raw_fd < 0 {
2739 eprintln!("[compositor] memfd_create for dmabuf feedback failed");
2740 fb.done();
2741 return;
2742 }
2743 let table_fd = unsafe { OwnedFd::from_raw_fd(raw_fd) };
2744 if !table_data.is_empty() {
2745 use std::io::Write;
2746 let mut file = std::fs::File::from(table_fd.try_clone().unwrap());
2747 if file.write_all(&table_data).is_err() {
2748 eprintln!("[compositor] failed to write dmabuf feedback table");
2749 fb.done();
2750 return;
2751 }
2752 }
2753 fb.format_table(table_fd.as_fd(), table_size as u32);
2754
2755 let dev = std::fs::metadata(&self.gpu_device)
2757 .map(|m| m.rdev())
2758 .unwrap_or(0);
2759 let dev_bytes = dev.to_ne_bytes().to_vec();
2760 fb.main_device(dev_bytes.clone());
2761
2762 fb.tranche_target_device(dev_bytes);
2764
2765 let indices: Vec<u8> = (0..modifiers.len() as u16)
2767 .flat_map(|i| i.to_ne_bytes())
2768 .collect();
2769 fb.tranche_formats(indices);
2770
2771 fb.tranche_flags(
2772 wayland_protocols::wp::linux_dmabuf::zv1::server::zwp_linux_dmabuf_feedback_v1::TrancheFlags::empty(),
2773 );
2774 fb.tranche_done();
2775 fb.done();
2776 }
2777}
2778
2779impl Compositor {
2780 fn collect_clipboard_mime_types(&self) -> Vec<String> {
2782 if let Some(ref src) = self.selection_source {
2784 let data = src.data::<DataSourceData>().unwrap();
2785 return data.mime_types.lock().unwrap().clone();
2786 }
2787 if let Some(ref cb) = self.external_clipboard
2789 && !cb.mime_type.is_empty()
2790 {
2791 let mut mimes = vec![cb.mime_type.clone()];
2792 if cb.mime_type.starts_with("text/plain") {
2794 if cb.mime_type != "text/plain" {
2795 mimes.push("text/plain".to_string());
2796 }
2797 if cb.mime_type != "text/plain;charset=utf-8" {
2798 mimes.push("text/plain;charset=utf-8".to_string());
2799 }
2800 mimes.push("UTF8_STRING".to_string());
2801 }
2802 return mimes;
2803 }
2804 Vec::new()
2805 }
2806
2807 fn get_clipboard_content(&mut self, mime_type: &str) -> Option<Vec<u8>> {
2809 if let Some(ref cb) = self.external_clipboard
2811 && self.selection_source.is_none()
2812 {
2813 let matches = cb.mime_type == mime_type
2815 || (cb.mime_type.starts_with("text/plain")
2816 && (mime_type == "text/plain"
2817 || mime_type == "text/plain;charset=utf-8"
2818 || mime_type == "UTF8_STRING"));
2819 if matches {
2820 return Some(cb.data.clone());
2821 }
2822 return None;
2823 }
2824 if let Some(src) = self.selection_source.clone() {
2826 return self.read_data_source_sync(&src, mime_type);
2827 }
2828 None
2829 }
2830
2831 fn read_data_source_sync(&mut self, source: &WlDataSource, mime_type: &str) -> Option<Vec<u8>> {
2833 let mut fds = [0i32; 2];
2834 if unsafe { libc::pipe(fds.as_mut_ptr()) } != 0 {
2835 return None;
2836 }
2837 let read_fd = unsafe { OwnedFd::from_raw_fd(fds[0]) };
2838 let write_fd = unsafe { OwnedFd::from_raw_fd(fds[1]) };
2839 source.send(mime_type.to_string(), write_fd.as_fd());
2840 let _ = self.display_handle.flush_clients();
2841 drop(write_fd); unsafe {
2844 libc::fcntl(read_fd.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
2845 }
2846 std::thread::sleep(std::time::Duration::from_millis(5));
2847 let mut buf = Vec::new();
2848 let mut tmp = [0u8; 8192];
2849 loop {
2850 let n = unsafe {
2851 libc::read(
2852 read_fd.as_raw_fd(),
2853 tmp.as_mut_ptr() as *mut libc::c_void,
2854 tmp.len(),
2855 )
2856 };
2857 if n <= 0 {
2858 break;
2859 }
2860 buf.extend_from_slice(&tmp[..n as usize]);
2861 if buf.len() > 1024 * 1024 {
2862 break; }
2864 }
2865 if buf.is_empty() { None } else { Some(buf) }
2866 }
2867}
2868
2869fn monotonic_timespec() -> (i64, i64) {
2875 let mut ts = libc::timespec {
2876 tv_sec: 0,
2877 tv_nsec: 0,
2878 };
2879 unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts) };
2881 (ts.tv_sec, ts.tv_nsec)
2882}
2883
2884fn elapsed_ms() -> u32 {
2885 let (sec, nsec) = monotonic_timespec();
2890 (sec as u32)
2891 .wrapping_mul(1000)
2892 .wrapping_add(nsec as u32 / 1_000_000)
2893}
2894
2895fn same_client<R1: Resource, R2: Resource>(a: &R1, b: &R2) -> bool {
2897 match (a.client(), b.client()) {
2898 (Some(ca), Some(cb)) => ca.id() == cb.id(),
2899 _ => false,
2900 }
2901}
2902
2903fn yuv420_to_rgb(y: u8, u: u8, v: u8) -> [u8; 3] {
2904 let y = (y as i32 - 16).max(0);
2905 let u = u as i32 - 128;
2906 let v = v as i32 - 128;
2907 let r = ((298 * y + 409 * v + 128) >> 8).clamp(0, 255) as u8;
2908 let g = ((298 * y - 100 * u - 208 * v + 128) >> 8).clamp(0, 255) as u8;
2909 let b = ((298 * y + 516 * u + 128) >> 8).clamp(0, 255) as u8;
2910 [r, g, b]
2911}
2912
2913fn xdg_toplevel_states(states: &[xdg_toplevel::State]) -> Vec<u8> {
2915 let mut bytes = Vec::with_capacity(states.len() * 4);
2916 for state in states {
2917 bytes.extend_from_slice(&(*state as u32).to_ne_bytes());
2918 }
2919 bytes
2920}
2921
2922fn create_keymap_fd(keymap_data: &[u8]) -> Option<OwnedFd> {
2923 use std::io::Write;
2924 let name = c"blit-keymap";
2925 let raw_fd = unsafe { libc::memfd_create(name.as_ptr(), libc::MFD_CLOEXEC) };
2926 if raw_fd < 0 {
2927 return None;
2928 }
2929 let fd = unsafe { OwnedFd::from_raw_fd(raw_fd) };
2930 let mut file = std::fs::File::from(fd);
2931 file.write_all(keymap_data).ok()?;
2932 Some(file.into())
2933}
2934
2935impl GlobalDispatch<WlCompositor, ()> for Compositor {
2942 fn bind(
2943 _state: &mut Self,
2944 _handle: &DisplayHandle,
2945 _client: &Client,
2946 resource: New<WlCompositor>,
2947 _data: &(),
2948 data_init: &mut DataInit<'_, Self>,
2949 ) {
2950 data_init.init(resource, ());
2951 }
2952}
2953
2954impl Dispatch<WlCompositor, ()> for Compositor {
2955 fn request(
2956 state: &mut Self,
2957 _client: &Client,
2958 _resource: &WlCompositor,
2959 request: <WlCompositor as Resource>::Request,
2960 _data: &(),
2961 _dh: &DisplayHandle,
2962 data_init: &mut DataInit<'_, Self>,
2963 ) {
2964 use wayland_server::protocol::wl_compositor::Request;
2965 match request {
2966 Request::CreateSurface { id } => {
2967 let surface = data_init.init(id, ());
2968 let proto_id = surface.id();
2969 state.surfaces.insert(
2970 proto_id,
2971 Surface {
2972 surface_id: 0,
2973 wl_surface: surface,
2974 pending_buffer: None,
2975 pending_buffer_scale: 1,
2976 pending_damage: false,
2977 pending_frame_callbacks: Vec::new(),
2978 pending_presentation_feedbacks: Vec::new(),
2979 pending_opaque: false,
2980 buffer_scale: 1,
2981 is_opaque: false,
2982 parent_surface_id: None,
2983 pending_subsurface_position: None,
2984 subsurface_position: (0, 0),
2985 children: Vec::new(),
2986 xdg_surface: None,
2987 xdg_toplevel: None,
2988 xdg_popup: None,
2989 xdg_geometry: None,
2990 title: String::new(),
2991 app_id: String::new(),
2992 pending_viewport_destination: None,
2993 viewport_destination: None,
2994 is_cursor: false,
2995 cursor_hotspot: (0, 0),
2996 },
2997 );
2998 }
2999 Request::CreateRegion { id } => {
3000 data_init.init(id, ());
3001 }
3002 _ => {}
3003 }
3004 }
3005}
3006
3007impl Dispatch<WlSurface, ()> for Compositor {
3010 fn request(
3011 state: &mut Self,
3012 _client: &Client,
3013 resource: &WlSurface,
3014 request: <WlSurface as Resource>::Request,
3015 _data: &(),
3016 _dh: &DisplayHandle,
3017 data_init: &mut DataInit<'_, Self>,
3018 ) {
3019 use wayland_server::protocol::wl_surface::Request;
3020 let sid = resource.id();
3021 match request {
3022 Request::Attach { buffer, x: _, y: _ } => {
3023 if let Some(surf) = state.surfaces.get_mut(&sid) {
3024 surf.pending_buffer = buffer;
3025 }
3026 }
3027 Request::Damage { .. } | Request::DamageBuffer { .. } => {
3028 if let Some(surf) = state.surfaces.get_mut(&sid) {
3029 surf.pending_damage = true;
3030 }
3031 }
3032 Request::Frame { callback } => {
3033 let cb = data_init.init(callback, ());
3034 if let Some(surf) = state.surfaces.get_mut(&sid) {
3035 surf.pending_frame_callbacks.push(cb);
3036 }
3037 }
3038 Request::SetBufferScale { scale } => {
3039 if let Some(surf) = state.surfaces.get_mut(&sid) {
3040 surf.pending_buffer_scale = scale;
3041 }
3042 }
3043 Request::SetOpaqueRegion { region: _ } => {
3044 if let Some(surf) = state.surfaces.get_mut(&sid) {
3045 surf.pending_opaque = true;
3046 }
3047 }
3048 Request::SetInputRegion { .. } => {}
3049 Request::Commit => {
3050 let is_cursor = state.surfaces.get(&sid).is_some_and(|s| s.is_cursor);
3051 if is_cursor {
3052 state.handle_cursor_commit(&sid);
3053 } else {
3054 state.handle_surface_commit(&sid);
3055 }
3056 }
3057 Request::SetBufferTransform { .. } => {}
3058 Request::Offset { .. } => {}
3059 Request::Destroy => {
3060 state.surface_meta.remove(&sid);
3061 state.cursor_rgba.remove(&sid);
3062 if let Some(ref mut vk) = state.vulkan_renderer {
3063 vk.remove_surface(&sid);
3064 }
3065 if let Some(held) = state.held_buffers.remove(&sid) {
3066 held.release();
3067 }
3068 if let Some(parent_id) = state
3069 .surfaces
3070 .get(&sid)
3071 .and_then(|s| s.parent_surface_id.clone())
3072 && let Some(parent) = state.surfaces.get_mut(&parent_id)
3073 {
3074 parent.children.retain(|c| *c != sid);
3075 }
3076 if let Some(surf) = state.surfaces.remove(&sid) {
3077 for fb in surf.pending_presentation_feedbacks {
3078 fb.discarded();
3079 }
3080 if surf.surface_id > 0 {
3081 state.toplevel_surface_ids.remove(&surf.surface_id);
3082 state.last_reported_size.remove(&surf.surface_id);
3083 state.surface_sizes.remove(&surf.surface_id);
3084 if let Some(ref mut vk) = state.vulkan_renderer {
3085 vk.destroy_external_outputs_for_surface(surf.surface_id as u32);
3086 }
3087 let _ = state.event_tx.send(CompositorEvent::SurfaceDestroyed {
3088 surface_id: surf.surface_id,
3089 });
3090 (state.event_notify)();
3091 }
3092 }
3093 }
3094 _ => {}
3095 }
3096 }
3097}
3098
3099impl Dispatch<WlCallback, ()> for Compositor {
3101 fn request(
3102 _: &mut Self,
3103 _: &Client,
3104 _: &WlCallback,
3105 _: <WlCallback as Resource>::Request,
3106 _: &(),
3107 _: &DisplayHandle,
3108 _: &mut DataInit<'_, Self>,
3109 ) {
3110 }
3111}
3112
3113impl GlobalDispatch<WpPresentation, ()> for Compositor {
3115 fn bind(
3116 _: &mut Self,
3117 _: &DisplayHandle,
3118 _: &Client,
3119 resource: New<WpPresentation>,
3120 _: &(),
3121 data_init: &mut DataInit<'_, Self>,
3122 ) {
3123 let pres = data_init.init(resource, ());
3124 pres.clock_id(libc::CLOCK_MONOTONIC as u32);
3126 }
3127}
3128
3129impl Dispatch<WpPresentation, ()> for Compositor {
3130 fn request(
3131 state: &mut Self,
3132 _: &Client,
3133 _: &WpPresentation,
3134 request: <WpPresentation as Resource>::Request,
3135 _: &(),
3136 _: &DisplayHandle,
3137 data_init: &mut DataInit<'_, Self>,
3138 ) {
3139 use wp_presentation::Request;
3140 match request {
3141 Request::Feedback { surface, callback } => {
3142 let fb = data_init.init(callback, ());
3143 let sid = surface.id();
3144 if let Some(surf) = state.surfaces.get_mut(&sid) {
3145 surf.pending_presentation_feedbacks.push(fb);
3146 }
3147 }
3148 Request::Destroy => {}
3149 _ => {}
3150 }
3151 }
3152}
3153
3154impl Dispatch<WpPresentationFeedback, ()> for Compositor {
3156 fn request(
3157 _: &mut Self,
3158 _: &Client,
3159 _: &WpPresentationFeedback,
3160 _: <WpPresentationFeedback as Resource>::Request,
3161 _: &(),
3162 _: &DisplayHandle,
3163 _: &mut DataInit<'_, Self>,
3164 ) {
3165 }
3166}
3167
3168impl Dispatch<WlRegion, ()> for Compositor {
3170 fn request(
3171 _: &mut Self,
3172 _: &Client,
3173 _: &WlRegion,
3174 _: <WlRegion as Resource>::Request,
3175 _: &(),
3176 _: &DisplayHandle,
3177 _: &mut DataInit<'_, Self>,
3178 ) {
3179 }
3180}
3181
3182impl GlobalDispatch<WlSubcompositor, ()> for Compositor {
3184 fn bind(
3185 _: &mut Self,
3186 _: &DisplayHandle,
3187 _: &Client,
3188 resource: New<WlSubcompositor>,
3189 _: &(),
3190 data_init: &mut DataInit<'_, Self>,
3191 ) {
3192 data_init.init(resource, ());
3193 }
3194}
3195
3196impl Dispatch<WlSubcompositor, ()> for Compositor {
3197 fn request(
3198 state: &mut Self,
3199 _: &Client,
3200 _: &WlSubcompositor,
3201 request: <WlSubcompositor as Resource>::Request,
3202 _: &(),
3203 _: &DisplayHandle,
3204 data_init: &mut DataInit<'_, Self>,
3205 ) {
3206 use wayland_server::protocol::wl_subcompositor::Request;
3207 match request {
3208 Request::GetSubsurface {
3209 id,
3210 surface,
3211 parent,
3212 } => {
3213 let child_id = surface.id();
3214 let parent_id = parent.id();
3215 data_init.init(
3216 id,
3217 SubsurfaceData {
3218 wl_surface_id: child_id.clone(),
3219 parent_surface_id: parent_id.clone(),
3220 },
3221 );
3222 if let Some(surf) = state.surfaces.get_mut(&child_id) {
3223 surf.parent_surface_id = Some(parent_id.clone());
3224 }
3225 if let Some(parent_surf) = state.surfaces.get_mut(&parent_id)
3226 && !parent_surf.children.contains(&child_id)
3227 {
3228 parent_surf.children.push(child_id);
3229 }
3230 }
3231 Request::Destroy => {}
3232 _ => {}
3233 }
3234 }
3235}
3236
3237impl Dispatch<WlSubsurface, SubsurfaceData> for Compositor {
3239 fn request(
3240 state: &mut Self,
3241 _: &Client,
3242 _: &WlSubsurface,
3243 request: <WlSubsurface as Resource>::Request,
3244 data: &SubsurfaceData,
3245 _: &DisplayHandle,
3246 _: &mut DataInit<'_, Self>,
3247 ) {
3248 use wayland_server::protocol::wl_subsurface::Request;
3249 match request {
3250 Request::SetPosition { x, y } => {
3251 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3252 surf.pending_subsurface_position = Some((x, y));
3253 }
3254 }
3255 Request::PlaceAbove { sibling } => {
3256 let sibling_id = sibling.id();
3257 if let Some(parent) = state.surfaces.get_mut(&data.parent_surface_id) {
3258 let child_id = &data.wl_surface_id;
3259 parent.children.retain(|c| c != child_id);
3260 let pos = parent
3261 .children
3262 .iter()
3263 .position(|c| *c == sibling_id)
3264 .map(|p| p + 1)
3265 .unwrap_or(parent.children.len());
3266 parent.children.insert(pos, child_id.clone());
3267 }
3268 }
3269 Request::PlaceBelow { sibling } => {
3270 let sibling_id = sibling.id();
3271 if let Some(parent) = state.surfaces.get_mut(&data.parent_surface_id) {
3272 let child_id = &data.wl_surface_id;
3273 parent.children.retain(|c| c != child_id);
3274 let pos = parent
3275 .children
3276 .iter()
3277 .position(|c| *c == sibling_id)
3278 .unwrap_or(0);
3279 parent.children.insert(pos, child_id.clone());
3280 }
3281 }
3282 Request::SetSync | Request::SetDesync => {}
3283 Request::Destroy => {
3284 let child_id = &data.wl_surface_id;
3285 if let Some(parent) = state.surfaces.get_mut(&data.parent_surface_id) {
3286 parent.children.retain(|c| c != child_id);
3287 }
3288 if let Some(surf) = state.surfaces.get_mut(child_id) {
3289 surf.parent_surface_id = None;
3290 }
3291 }
3292 _ => {}
3293 }
3294 }
3295}
3296
3297impl GlobalDispatch<XdgWmBase, ()> for Compositor {
3299 fn bind(
3300 _: &mut Self,
3301 _: &DisplayHandle,
3302 _: &Client,
3303 resource: New<XdgWmBase>,
3304 _: &(),
3305 data_init: &mut DataInit<'_, Self>,
3306 ) {
3307 data_init.init(resource, ());
3308 }
3309}
3310
3311impl Dispatch<XdgWmBase, ()> for Compositor {
3312 fn request(
3313 state: &mut Self,
3314 _: &Client,
3315 _: &XdgWmBase,
3316 request: <XdgWmBase as Resource>::Request,
3317 _: &(),
3318 _: &DisplayHandle,
3319 data_init: &mut DataInit<'_, Self>,
3320 ) {
3321 use xdg_wm_base::Request;
3322 match request {
3323 Request::GetXdgSurface { id, surface } => {
3324 let wl_surface_id = surface.id();
3325 let xdg_surface = data_init.init(
3326 id,
3327 XdgSurfaceData {
3328 wl_surface_id: wl_surface_id.clone(),
3329 },
3330 );
3331 if let Some(surf) = state.surfaces.get_mut(&wl_surface_id) {
3332 surf.xdg_surface = Some(xdg_surface);
3333 }
3334 }
3335 Request::CreatePositioner { id } => {
3336 let positioner = data_init.init(id, ());
3337 let pos_id = positioner.id();
3338 state.positioners.insert(
3339 pos_id,
3340 PositionerState {
3341 resource: positioner,
3342 geometry: PositionerGeometry {
3343 size: (0, 0),
3344 anchor_rect: (0, 0, 0, 0),
3345 anchor: 0,
3346 gravity: 0,
3347 constraint_adjustment: 0,
3348 offset: (0, 0),
3349 },
3350 },
3351 );
3352 }
3353 Request::Pong { .. } => {}
3354 Request::Destroy => {}
3355 _ => {}
3356 }
3357 }
3358}
3359
3360impl Dispatch<XdgSurface, XdgSurfaceData> for Compositor {
3362 fn request(
3363 state: &mut Self,
3364 _: &Client,
3365 resource: &XdgSurface,
3366 request: <XdgSurface as Resource>::Request,
3367 data: &XdgSurfaceData,
3368 _: &DisplayHandle,
3369 data_init: &mut DataInit<'_, Self>,
3370 ) {
3371 use xdg_surface::Request;
3372 match request {
3373 Request::GetToplevel { id } => {
3374 let toplevel = data_init.init(
3375 id,
3376 XdgToplevelData {
3377 wl_surface_id: data.wl_surface_id.clone(),
3378 },
3379 );
3380 let surface_id = state.allocate_surface_id();
3381 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3382 surf.xdg_toplevel = Some(toplevel.clone());
3383 surf.surface_id = surface_id;
3384 }
3385 state
3386 .toplevel_surface_ids
3387 .insert(surface_id, data.wl_surface_id.clone());
3388
3389 let (cw, ch) = state
3394 .surface_sizes
3395 .get(&surface_id)
3396 .copied()
3397 .unwrap_or((state.output_width, state.output_height));
3398 let states = xdg_toplevel_states(&[
3399 xdg_toplevel::State::Activated,
3400 xdg_toplevel::State::Maximized,
3401 ]);
3402 toplevel.configure(cw, ch, states);
3403 let serial = state.next_serial();
3404 resource.configure(serial);
3405
3406 state.set_keyboard_focus(surface_id);
3409 if let Some(surf) = state.surfaces.get(&data.wl_surface_id) {
3412 for output in &state.outputs {
3413 if same_client(output, &surf.wl_surface) {
3414 surf.wl_surface.enter(output);
3415 }
3416 }
3417 }
3418 let _ = state.display_handle.flush_clients();
3419
3420 let _ = state.event_tx.send(CompositorEvent::SurfaceCreated {
3421 surface_id,
3422 title: String::new(),
3423 app_id: String::new(),
3424 parent_id: 0,
3425 width: 0,
3426 height: 0,
3427 });
3428 (state.event_notify)();
3429 if state.verbose {
3430 eprintln!("[compositor] new_toplevel sid={surface_id}");
3431 }
3432 }
3433 Request::GetPopup {
3434 id,
3435 parent,
3436 positioner,
3437 } => {
3438 let popup = data_init.init(
3439 id,
3440 XdgPopupData {
3441 wl_surface_id: data.wl_surface_id.clone(),
3442 },
3443 );
3444
3445 let parent_wl_id: Option<ObjectId> = parent
3448 .as_ref()
3449 .and_then(|p| p.data::<XdgSurfaceData>())
3450 .map(|d| d.wl_surface_id.clone());
3451
3452 let parent_geom_offset = parent_wl_id
3457 .as_ref()
3458 .and_then(|pid| state.surfaces.get(pid))
3459 .and_then(|s| s.xdg_geometry)
3460 .map(|(gx, gy, _, _)| (gx, gy))
3461 .unwrap_or((0, 0));
3462
3463 let parent_abs = parent_wl_id
3468 .as_ref()
3469 .map(|pid| {
3470 let abs = state.surface_absolute_position(pid);
3471 (abs.0 + parent_geom_offset.0, abs.1 + parent_geom_offset.1)
3472 })
3473 .unwrap_or((0, 0));
3474 let (_, toplevel_root) = parent_wl_id
3477 .as_ref()
3478 .map(|pid| state.find_toplevel_root(pid))
3479 .unwrap_or_else(|| {
3480 (data.wl_surface_id.clone(), None)
3482 });
3483 let bounds = toplevel_root
3484 .and_then(|_| {
3485 let root_wl_id = parent_wl_id.as_ref().map(|pid| {
3486 let (rid, _) = state.find_toplevel_root(pid);
3487 rid
3488 })?;
3489 let surf = state.surfaces.get(&root_wl_id)?;
3490 if let Some((gx, gy, gw, gh)) = surf.xdg_geometry
3491 && gw > 0
3492 && gh > 0
3493 {
3494 return Some((gx, gy, gw, gh));
3495 }
3496
3497 let sm = state.surface_meta.get(&root_wl_id)?;
3500 let s = (sm.scale).max(1);
3501 let (lw, lh) = surf
3502 .viewport_destination
3503 .filter(|&(dw, dh)| dw > 0 && dh > 0)
3504 .unwrap_or((sm.width as i32 / s, sm.height as i32 / s));
3505 Some((0, 0, lw, lh))
3506 })
3507 .unwrap_or((0, 0, state.output_width, state.output_height));
3508
3509 eprintln!(
3510 "[popup] parent_abs={parent_abs:?} bounds={bounds:?} parent_wl={parent_wl_id:?} geom_off={parent_geom_offset:?}"
3511 );
3512 let pos_id = positioner.id();
3514 let (px, py, pw, ph) = state
3515 .positioners
3516 .get(&pos_id)
3517 .map(|p| p.geometry.compute_position(parent_abs, bounds))
3518 .unwrap_or((0, 0, 200, 200));
3519 eprintln!("[popup] result=({px},{py},{pw},{ph})");
3520
3521 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3522 surf.xdg_popup = Some(popup.clone());
3523 surf.parent_surface_id = parent_wl_id.clone();
3524 surf.subsurface_position =
3529 (parent_geom_offset.0 + px, parent_geom_offset.1 + py);
3530 }
3531 if let Some(ref parent_id) = parent_wl_id
3532 && let Some(parent_surf) = state.surfaces.get_mut(parent_id)
3533 && !parent_surf.children.contains(&data.wl_surface_id)
3534 {
3535 parent_surf.children.push(data.wl_surface_id.clone());
3536 }
3537
3538 popup.configure(px, py, pw, ph);
3539 let serial = state.next_serial();
3540 resource.configure(serial);
3541 let _ = state.display_handle.flush_clients();
3542 }
3543 Request::SetWindowGeometry {
3544 x,
3545 y,
3546 width,
3547 height,
3548 } => {
3549 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3550 if surf.xdg_popup.is_some() {
3558 let (old_gx, old_gy) = surf
3559 .xdg_geometry
3560 .map(|(gx, gy, _, _)| (gx, gy))
3561 .unwrap_or((0, 0));
3562 surf.subsurface_position.0 += old_gx - x;
3563 surf.subsurface_position.1 += old_gy - y;
3564 }
3565 surf.xdg_geometry = Some((x, y, width, height));
3566 }
3567 }
3568 Request::AckConfigure { .. } => {}
3569 Request::Destroy => {}
3570 _ => {}
3571 }
3572 }
3573}
3574
3575impl Dispatch<XdgToplevel, XdgToplevelData> for Compositor {
3577 fn request(
3578 state: &mut Self,
3579 _: &Client,
3580 _: &XdgToplevel,
3581 request: <XdgToplevel as Resource>::Request,
3582 data: &XdgToplevelData,
3583 _: &DisplayHandle,
3584 _: &mut DataInit<'_, Self>,
3585 ) {
3586 use xdg_toplevel::Request;
3587 match request {
3588 Request::SetTitle { title } => {
3589 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id)
3590 && surf.title != title
3591 {
3592 surf.title = title.clone();
3593 if surf.surface_id > 0 {
3594 let _ = state.event_tx.send(CompositorEvent::SurfaceTitle {
3595 surface_id: surf.surface_id,
3596 title,
3597 });
3598 (state.event_notify)();
3599 }
3600 }
3601 }
3602 Request::SetAppId { app_id } => {
3603 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id)
3604 && surf.app_id != app_id
3605 {
3606 surf.app_id = app_id.clone();
3607 if surf.surface_id > 0 {
3608 let _ = state.event_tx.send(CompositorEvent::SurfaceAppId {
3609 surface_id: surf.surface_id,
3610 app_id,
3611 });
3612 (state.event_notify)();
3613 }
3614 }
3615 }
3616 Request::Destroy => {
3617 let wl_surface_id = &data.wl_surface_id;
3618 state.surface_meta.remove(wl_surface_id);
3619 state.cursor_rgba.remove(wl_surface_id);
3620 if let Some(ref mut vk) = state.vulkan_renderer {
3621 vk.remove_surface(wl_surface_id);
3622 }
3623 if let Some(held) = state.held_buffers.remove(wl_surface_id) {
3624 held.release();
3625 }
3626 if let Some(surf) = state.surfaces.get_mut(wl_surface_id) {
3627 let sid = surf.surface_id;
3628 surf.xdg_toplevel = None;
3629 if sid > 0 {
3630 state.toplevel_surface_ids.remove(&sid);
3631 state.last_reported_size.remove(&sid);
3632 state.surface_sizes.remove(&sid);
3633 if let Some(ref mut vk) = state.vulkan_renderer {
3634 vk.destroy_external_outputs_for_surface(sid as u32);
3635 }
3636 let _ = state
3637 .event_tx
3638 .send(CompositorEvent::SurfaceDestroyed { surface_id: sid });
3639 (state.event_notify)();
3640 surf.surface_id = 0;
3641 }
3642 }
3643 }
3644 _ => {}
3645 }
3646 }
3647}
3648
3649impl Dispatch<XdgPopup, XdgPopupData> for Compositor {
3651 fn request(
3652 state: &mut Self,
3653 _: &Client,
3654 _: &XdgPopup,
3655 request: <XdgPopup as Resource>::Request,
3656 data: &XdgPopupData,
3657 _: &DisplayHandle,
3658 _: &mut DataInit<'_, Self>,
3659 ) {
3660 use xdg_popup::Request;
3661 match request {
3662 Request::Grab { seat: _, serial: _ } => {
3663 state
3666 .popup_grab_stack
3667 .retain(|id| *id != data.wl_surface_id);
3668 state.popup_grab_stack.push(data.wl_surface_id.clone());
3669 }
3670 Request::Reposition { positioner, token } => {
3671 let pos_id = positioner.id();
3673 if let Some(surf) = state.surfaces.get(&data.wl_surface_id)
3674 && let Some(parent_id) = surf.parent_surface_id.clone()
3675 {
3676 let parent_geom_offset = state
3677 .surfaces
3678 .get(&parent_id)
3679 .and_then(|s| s.xdg_geometry)
3680 .map(|(gx, gy, _, _)| (gx, gy))
3681 .unwrap_or((0, 0));
3682 let parent_abs = {
3683 let abs = state.surface_absolute_position(&parent_id);
3684 (abs.0 + parent_geom_offset.0, abs.1 + parent_geom_offset.1)
3685 };
3686 let (root_id, toplevel_root) = state.find_toplevel_root(&parent_id);
3687 let bounds = toplevel_root
3688 .and_then(|_| {
3689 let surf = state.surfaces.get(&root_id)?;
3690 if let Some((gx, gy, gw, gh)) = surf.xdg_geometry
3691 && gw > 0
3692 && gh > 0
3693 {
3694 return Some((gx, gy, gw, gh));
3695 }
3696 let sm = state.surface_meta.get(&root_id)?;
3697 let s = (sm.scale).max(1);
3698 let (lw, lh) = surf
3699 .viewport_destination
3700 .filter(|&(dw, dh)| dw > 0 && dh > 0)
3701 .unwrap_or((sm.width as i32 / s, sm.height as i32 / s));
3702 Some((0, 0, lw, lh))
3703 })
3704 .unwrap_or((0, 0, state.output_width, state.output_height));
3705 let (px, py, pw, ph) = state
3706 .positioners
3707 .get(&pos_id)
3708 .map(|p| p.geometry.compute_position(parent_abs, bounds))
3709 .unwrap_or((0, 0, 200, 200));
3710 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3711 let old_gx = surf.xdg_geometry.map(|(gx, _, _, _)| gx).unwrap_or(0);
3714 let old_gy = surf.xdg_geometry.map(|(_, gy, _, _)| gy).unwrap_or(0);
3715 surf.subsurface_position = (
3716 parent_geom_offset.0 + px - old_gx,
3717 parent_geom_offset.1 + py - old_gy,
3718 );
3719 if let Some(ref popup) = surf.xdg_popup {
3720 popup.configure(px, py, pw, ph);
3721 popup.repositioned(token);
3722 }
3723 if let Some(ref xs) = surf.xdg_surface {
3724 let serial = state.serial.wrapping_add(1);
3725 state.serial = serial;
3726 xs.configure(serial);
3727 }
3728 }
3729 }
3730 }
3731 Request::Destroy => {
3732 state
3734 .popup_grab_stack
3735 .retain(|id| *id != data.wl_surface_id);
3736 if let Some(parent_id) = state
3738 .surfaces
3739 .get(&data.wl_surface_id)
3740 .and_then(|s| s.parent_surface_id.clone())
3741 && let Some(parent) = state.surfaces.get_mut(&parent_id)
3742 {
3743 parent.children.retain(|c| *c != data.wl_surface_id);
3744 }
3745 if let Some(surf) = state.surfaces.get_mut(&data.wl_surface_id) {
3746 surf.xdg_popup = None;
3747 surf.parent_surface_id = None;
3748 }
3749 }
3750 _ => {}
3751 }
3752 }
3753}
3754
3755use wayland_protocols::xdg::shell::server::xdg_positioner;
3757impl Dispatch<XdgPositioner, ()> for Compositor {
3758 fn request(
3759 state: &mut Self,
3760 _: &Client,
3761 resource: &XdgPositioner,
3762 request: <XdgPositioner as Resource>::Request,
3763 _: &(),
3764 _: &DisplayHandle,
3765 _: &mut DataInit<'_, Self>,
3766 ) {
3767 use xdg_positioner::Request;
3768 let pos_id = resource.id();
3769 let Some(pos) = state.positioners.get_mut(&pos_id) else {
3770 return;
3771 };
3772 match request {
3773 Request::SetSize { width, height } => {
3774 pos.geometry.size = (width, height);
3775 }
3776 Request::SetAnchorRect {
3777 x,
3778 y,
3779 width,
3780 height,
3781 } => {
3782 pos.geometry.anchor_rect = (x, y, width, height);
3783 }
3784 Request::SetAnchor {
3785 anchor: wayland_server::WEnum::Value(v),
3786 } => {
3787 pos.geometry.anchor = v as u32;
3788 }
3789 Request::SetGravity {
3790 gravity: wayland_server::WEnum::Value(v),
3791 } => {
3792 pos.geometry.gravity = v as u32;
3793 }
3794 Request::SetOffset { x, y } => {
3795 pos.geometry.offset = (x, y);
3796 }
3797 Request::SetConstraintAdjustment {
3798 constraint_adjustment,
3799 } => {
3800 pos.geometry.constraint_adjustment = constraint_adjustment.into();
3801 }
3802 Request::Destroy => {
3803 state.positioners.remove(&pos_id);
3804 }
3805 _ => {}
3806 }
3807 }
3808}
3809
3810impl GlobalDispatch<ZxdgDecorationManagerV1, ()> for Compositor {
3812 fn bind(
3813 _: &mut Self,
3814 _: &DisplayHandle,
3815 _: &Client,
3816 resource: New<ZxdgDecorationManagerV1>,
3817 _: &(),
3818 data_init: &mut DataInit<'_, Self>,
3819 ) {
3820 data_init.init(resource, ());
3821 }
3822}
3823
3824impl Dispatch<ZxdgDecorationManagerV1, ()> for Compositor {
3825 fn request(
3826 _: &mut Self,
3827 _: &Client,
3828 _: &ZxdgDecorationManagerV1,
3829 request: <ZxdgDecorationManagerV1 as Resource>::Request,
3830 _: &(),
3831 _: &DisplayHandle,
3832 data_init: &mut DataInit<'_, Self>,
3833 ) {
3834 use zxdg_decoration_manager_v1::Request;
3835 match request {
3836 Request::GetToplevelDecoration { id, toplevel: _ } => {
3837 let decoration = data_init.init(id, ());
3838 decoration.configure(zxdg_toplevel_decoration_v1::Mode::ServerSide);
3840 }
3841 Request::Destroy => {}
3842 _ => {}
3843 }
3844 }
3845}
3846
3847impl Dispatch<ZxdgToplevelDecorationV1, ()> for Compositor {
3848 fn request(
3849 _: &mut Self,
3850 _: &Client,
3851 resource: &ZxdgToplevelDecorationV1,
3852 request: <ZxdgToplevelDecorationV1 as Resource>::Request,
3853 _: &(),
3854 _: &DisplayHandle,
3855 _: &mut DataInit<'_, Self>,
3856 ) {
3857 use zxdg_toplevel_decoration_v1::Request;
3858 match request {
3859 Request::SetMode { .. } | Request::UnsetMode => {
3860 resource.configure(zxdg_toplevel_decoration_v1::Mode::ServerSide);
3861 }
3862 Request::Destroy => {}
3863 _ => {}
3864 }
3865 }
3866}
3867
3868impl GlobalDispatch<WlShm, ()> for Compositor {
3870 fn bind(
3871 _: &mut Self,
3872 _: &DisplayHandle,
3873 _: &Client,
3874 resource: New<WlShm>,
3875 _: &(),
3876 data_init: &mut DataInit<'_, Self>,
3877 ) {
3878 let shm = data_init.init(resource, ());
3879 shm.format(wl_shm::Format::Argb8888);
3880 shm.format(wl_shm::Format::Xrgb8888);
3881 shm.format(wl_shm::Format::Abgr8888);
3882 shm.format(wl_shm::Format::Xbgr8888);
3883 }
3884}
3885
3886impl Dispatch<WlShm, ()> for Compositor {
3887 fn request(
3888 state: &mut Self,
3889 _: &Client,
3890 _: &WlShm,
3891 request: <WlShm as Resource>::Request,
3892 _: &(),
3893 _: &DisplayHandle,
3894 data_init: &mut DataInit<'_, Self>,
3895 ) {
3896 use wayland_server::protocol::wl_shm::Request;
3897 if let Request::CreatePool { id, fd, size } = request {
3898 let pool = data_init.init(id, ());
3899 let pool_id = pool.id();
3900 state
3901 .shm_pools
3902 .insert(pool_id, Arc::new(ShmPool::new(pool, fd, size)));
3903 }
3904 }
3905}
3906
3907impl Dispatch<WlShmPool, ()> for Compositor {
3909 fn request(
3910 state: &mut Self,
3911 _: &Client,
3912 resource: &WlShmPool,
3913 request: <WlShmPool as Resource>::Request,
3914 _: &(),
3915 _: &DisplayHandle,
3916 data_init: &mut DataInit<'_, Self>,
3917 ) {
3918 use wayland_server::protocol::wl_shm_pool::Request;
3919 let pool_id = resource.id();
3920 match request {
3921 Request::CreateBuffer {
3922 id,
3923 offset,
3924 width,
3925 height,
3926 stride,
3927 format,
3928 } => {
3929 let fmt = match format {
3931 wayland_server::WEnum::Value(f) => f,
3932 _ => wl_shm::Format::Argb8888, };
3934 let Some(pool) = state.shm_pools.get(&pool_id).cloned() else {
3935 return;
3936 };
3937 data_init.init(
3938 id,
3939 ShmBufferData {
3940 pool,
3941 offset,
3942 width,
3943 height,
3944 stride,
3945 format: fmt,
3946 },
3947 );
3948 }
3949 Request::Resize { size } => {
3950 if let Some(pool) = state.shm_pools.get(&pool_id) {
3951 pool.resize(size);
3952 }
3953 }
3954 Request::Destroy => {
3955 state.shm_pools.remove(&pool_id);
3958 }
3959 _ => {}
3960 }
3961 }
3962}
3963
3964impl Dispatch<WlBuffer, ShmBufferData> for Compositor {
3966 fn request(
3967 _: &mut Self,
3968 _: &Client,
3969 _: &WlBuffer,
3970 _: <WlBuffer as Resource>::Request,
3971 _: &ShmBufferData,
3972 _: &DisplayHandle,
3973 _: &mut DataInit<'_, Self>,
3974 ) {
3975 }
3976}
3977
3978impl Dispatch<WlBuffer, DmaBufBufferData> for Compositor {
3980 fn request(
3981 _: &mut Self,
3982 _: &Client,
3983 _: &WlBuffer,
3984 _: <WlBuffer as Resource>::Request,
3985 _: &DmaBufBufferData,
3986 _: &DisplayHandle,
3987 _: &mut DataInit<'_, Self>,
3988 ) {
3989 }
3990}
3991
3992impl GlobalDispatch<WlOutput, ()> for Compositor {
3994 fn bind(
3995 state: &mut Self,
3996 _: &DisplayHandle,
3997 _: &Client,
3998 resource: New<WlOutput>,
3999 _: &(),
4000 data_init: &mut DataInit<'_, Self>,
4001 ) {
4002 let output = data_init.init(resource, ());
4003 output.geometry(
4004 0,
4005 0,
4006 0,
4007 0,
4008 wl_output::Subpixel::Unknown,
4009 "Virtual".to_string(),
4010 "Headless".to_string(),
4011 wl_output::Transform::Normal,
4012 );
4013 let s120 = state.output_scale_120 as i32;
4014 let mode_w = state.output_width * s120 / 120;
4015 let mode_h = state.output_height * s120 / 120;
4016 output.mode(
4017 wl_output::Mode::Current | wl_output::Mode::Preferred,
4018 mode_w,
4019 mode_h,
4020 state.output_refresh_mhz as i32,
4021 );
4022 if output.version() >= 2 {
4023 output.scale(((state.output_scale_120 as i32) + 119) / 120);
4024 }
4025 if output.version() >= 2 {
4026 output.done();
4027 }
4028 state.outputs.push(output);
4029 }
4030}
4031
4032impl Dispatch<WlOutput, ()> for Compositor {
4033 fn request(
4034 state: &mut Self,
4035 _: &Client,
4036 resource: &WlOutput,
4037 request: <WlOutput as Resource>::Request,
4038 _: &(),
4039 _: &DisplayHandle,
4040 _: &mut DataInit<'_, Self>,
4041 ) {
4042 use wayland_server::protocol::wl_output::Request;
4043 if let Request::Release = request {
4044 state.outputs.retain(|o| o.id() != resource.id());
4045 }
4046 }
4047}
4048
4049impl GlobalDispatch<WlSeat, ()> for Compositor {
4051 fn bind(
4052 _: &mut Self,
4053 _: &DisplayHandle,
4054 _: &Client,
4055 resource: New<WlSeat>,
4056 _: &(),
4057 data_init: &mut DataInit<'_, Self>,
4058 ) {
4059 let seat = data_init.init(resource, ());
4060 seat.capabilities(wl_seat::Capability::Keyboard | wl_seat::Capability::Pointer);
4061 if seat.version() >= 2 {
4062 seat.name("headless".to_string());
4063 }
4064 }
4065}
4066
4067impl Dispatch<WlSeat, ()> for Compositor {
4068 fn request(
4069 state: &mut Self,
4070 _: &Client,
4071 _: &WlSeat,
4072 request: <WlSeat as Resource>::Request,
4073 _: &(),
4074 _: &DisplayHandle,
4075 data_init: &mut DataInit<'_, Self>,
4076 ) {
4077 use wayland_server::protocol::wl_seat::Request;
4078 match request {
4079 Request::GetKeyboard { id } => {
4080 let kb = data_init.init(id, ());
4081 if let Some(fd) = create_keymap_fd(&state.keyboard_keymap_data) {
4082 kb.keymap(
4083 wl_keyboard::KeymapFormat::XkbV1,
4084 fd.as_fd(),
4085 state.keyboard_keymap_data.len() as u32,
4086 );
4087 }
4088 if kb.version() >= 4 {
4089 kb.repeat_info(25, 200);
4090 }
4091 state.keyboards.push(kb);
4092 }
4093 Request::GetPointer { id } => {
4094 let ptr = data_init.init(id, ());
4095 state.pointers.push(ptr);
4096 }
4097 Request::GetTouch { id } => {
4098 data_init.init(id, ());
4099 }
4100 Request::Release => {}
4101 _ => {}
4102 }
4103 }
4104}
4105
4106impl Dispatch<WlKeyboard, ()> for Compositor {
4108 fn request(
4109 state: &mut Self,
4110 _: &Client,
4111 resource: &WlKeyboard,
4112 request: <WlKeyboard as Resource>::Request,
4113 _: &(),
4114 _: &DisplayHandle,
4115 _: &mut DataInit<'_, Self>,
4116 ) {
4117 if let wl_keyboard::Request::Release = request {
4118 state.keyboards.retain(|k| k.id() != resource.id());
4119 }
4120 }
4121}
4122
4123impl Dispatch<WlPointer, ()> for Compositor {
4125 fn request(
4126 state: &mut Self,
4127 _: &Client,
4128 resource: &WlPointer,
4129 request: <WlPointer as Resource>::Request,
4130 _: &(),
4131 _: &DisplayHandle,
4132 _: &mut DataInit<'_, Self>,
4133 ) {
4134 use wl_pointer::Request;
4135 match request {
4136 Request::SetCursor {
4137 serial: _,
4138 surface,
4139 hotspot_x,
4140 hotspot_y,
4141 } => {
4142 if let Some(surface) = surface {
4143 let sid = surface.id();
4144 if let Some(surf) = state.surfaces.get_mut(&sid) {
4145 surf.is_cursor = true;
4146 surf.cursor_hotspot = (hotspot_x, hotspot_y);
4147 }
4148 } else {
4149 let _ = state.event_tx.send(CompositorEvent::SurfaceCursor {
4150 surface_id: state.focused_surface_id,
4151 cursor: CursorImage::Hidden,
4152 });
4153 }
4154 }
4155 Request::Release => {
4156 state.pointers.retain(|p| p.id() != resource.id());
4157 }
4158 _ => {}
4159 }
4160 }
4161}
4162
4163impl Dispatch<wayland_server::protocol::wl_touch::WlTouch, ()> for Compositor {
4165 fn request(
4166 _: &mut Self,
4167 _: &Client,
4168 _: &wayland_server::protocol::wl_touch::WlTouch,
4169 _: <wayland_server::protocol::wl_touch::WlTouch as Resource>::Request,
4170 _: &(),
4171 _: &DisplayHandle,
4172 _: &mut DataInit<'_, Self>,
4173 ) {
4174 }
4175}
4176
4177impl GlobalDispatch<ZwpLinuxDmabufV1, ()> for Compositor {
4179 fn bind(
4180 state: &mut Self,
4181 _: &DisplayHandle,
4182 _: &Client,
4183 resource: New<ZwpLinuxDmabufV1>,
4184 _: &(),
4185 data_init: &mut DataInit<'_, Self>,
4186 ) {
4187 let dmabuf = data_init.init(resource, ());
4188 if dmabuf.version() >= 4 {
4191 return;
4192 }
4193 if dmabuf.version() >= 3 {
4194 if let Some(ref vk) = state.vulkan_renderer
4200 && !vk.supported_dmabuf_modifiers.is_empty()
4201 {
4202 for &(drm_fmt, modifier) in &vk.supported_dmabuf_modifiers {
4203 let mod_hi = (modifier >> 32) as u32;
4204 let mod_lo = (modifier & 0xFFFFFFFF) as u32;
4205 dmabuf.modifier(drm_fmt, mod_hi, mod_lo);
4206 }
4207 }
4208 } else if state
4212 .vulkan_renderer
4213 .as_ref()
4214 .is_some_and(|vk| vk.has_dmabuf())
4215 {
4216 dmabuf.format(drm_fourcc::ARGB8888);
4217 dmabuf.format(drm_fourcc::XRGB8888);
4218 dmabuf.format(drm_fourcc::ABGR8888);
4219 dmabuf.format(drm_fourcc::XBGR8888);
4220 }
4221 }
4222}
4223
4224impl Dispatch<ZwpLinuxDmabufV1, ()> for Compositor {
4225 fn request(
4226 state: &mut Self,
4227 _: &Client,
4228 _: &ZwpLinuxDmabufV1,
4229 request: <ZwpLinuxDmabufV1 as Resource>::Request,
4230 _: &(),
4231 _: &DisplayHandle,
4232 data_init: &mut DataInit<'_, Self>,
4233 ) {
4234 use zwp_linux_dmabuf_v1::Request;
4235 match request {
4236 Request::CreateParams { params_id } => {
4237 data_init.init(params_id, ());
4238 }
4239 Request::GetDefaultFeedback { id } => {
4240 let fb = data_init.init(id, ());
4241 state.send_dmabuf_feedback(&fb);
4242 }
4243 Request::GetSurfaceFeedback { id, .. } => {
4244 let fb = data_init.init(id, ());
4245 state.send_dmabuf_feedback(&fb);
4246 }
4247 Request::Destroy => {}
4248 _ => {}
4249 }
4250 }
4251}
4252
4253impl Dispatch<ZwpLinuxDmabufFeedbackV1, ()> for Compositor {
4254 fn request(
4255 _: &mut Self,
4256 _: &Client,
4257 _: &ZwpLinuxDmabufFeedbackV1,
4258 _request: <ZwpLinuxDmabufFeedbackV1 as Resource>::Request,
4259 _: &(),
4260 _: &DisplayHandle,
4261 _data_init: &mut DataInit<'_, Self>,
4262 ) {
4263 }
4265}
4266
4267impl Dispatch<ZwpLinuxBufferParamsV1, ()> for Compositor {
4269 fn request(
4270 state: &mut Self,
4271 client: &Client,
4272 resource: &ZwpLinuxBufferParamsV1,
4273 request: <ZwpLinuxBufferParamsV1 as Resource>::Request,
4274 _: &(),
4275 dh: &DisplayHandle,
4276 data_init: &mut DataInit<'_, Self>,
4277 ) {
4278 use zwp_linux_buffer_params_v1::Request;
4279 let params_id = resource.id();
4280 match request {
4281 Request::Add {
4282 fd,
4283 plane_idx: _,
4284 offset,
4285 stride,
4286 modifier_hi,
4287 modifier_lo,
4288 } => {
4289 let modifier = ((modifier_hi as u64) << 32) | (modifier_lo as u64);
4290 let entry = state
4291 .dmabuf_params
4292 .entry(params_id.clone())
4293 .or_insert_with(|| DmaBufParamsPending {
4294 resource: resource.clone(),
4295 planes: Vec::new(),
4296 modifier,
4297 });
4298 entry.modifier = modifier;
4299 entry.planes.push(DmaBufPlane { fd, offset, stride });
4300 }
4301 Request::Create {
4302 width,
4303 height,
4304 format,
4305 flags,
4306 } => {
4307 let pending = state.dmabuf_params.remove(¶ms_id);
4308 let (planes, modifier) = match pending {
4309 Some(p) => (p.planes, p.modifier),
4310 None => {
4311 resource.failed();
4312 return;
4313 }
4314 };
4315 let y_invert = flags
4316 .into_result()
4317 .ok()
4318 .is_some_and(|f| f.contains(zwp_linux_buffer_params_v1::Flags::YInvert));
4319 match client.create_resource::<WlBuffer, DmaBufBufferData, Compositor>(
4320 dh,
4321 1,
4322 DmaBufBufferData {
4323 width,
4324 height,
4325 fourcc: format,
4326 modifier,
4327 planes,
4328 y_invert,
4329 },
4330 ) {
4331 Ok(buffer) => resource.created(&buffer),
4332 Err(_) => resource.failed(),
4333 }
4334 }
4335 Request::CreateImmed {
4336 buffer_id,
4337 width,
4338 height,
4339 format,
4340 flags,
4341 } => {
4342 let (planes, modifier) = state
4343 .dmabuf_params
4344 .remove(¶ms_id)
4345 .map(|p| (p.planes, p.modifier))
4346 .unwrap_or_default();
4347 let y_invert = flags
4348 .into_result()
4349 .ok()
4350 .is_some_and(|f| f.contains(zwp_linux_buffer_params_v1::Flags::YInvert));
4351 data_init.init(
4352 buffer_id,
4353 DmaBufBufferData {
4354 width,
4355 height,
4356 fourcc: format,
4357 modifier,
4358 planes,
4359 y_invert,
4360 },
4361 );
4362 }
4363 Request::Destroy => {
4364 state.dmabuf_params.remove(¶ms_id);
4365 }
4366 _ => {}
4367 }
4368 }
4369}
4370
4371impl GlobalDispatch<WpFractionalScaleManagerV1, ()> for Compositor {
4373 fn bind(
4374 _: &mut Self,
4375 _: &DisplayHandle,
4376 _: &Client,
4377 resource: New<WpFractionalScaleManagerV1>,
4378 _: &(),
4379 data_init: &mut DataInit<'_, Self>,
4380 ) {
4381 data_init.init(resource, ());
4382 }
4383}
4384
4385impl Dispatch<WpFractionalScaleManagerV1, ()> for Compositor {
4386 fn request(
4387 state: &mut Self,
4388 _: &Client,
4389 _: &WpFractionalScaleManagerV1,
4390 request: <WpFractionalScaleManagerV1 as Resource>::Request,
4391 _: &(),
4392 _: &DisplayHandle,
4393 data_init: &mut DataInit<'_, Self>,
4394 ) {
4395 use wp_fractional_scale_manager_v1::Request;
4396 match request {
4397 Request::GetFractionalScale { id, surface: _ } => {
4398 let fs = data_init.init(id, ());
4399 fs.preferred_scale(state.output_scale_120 as u32);
4401 state.fractional_scales.push(fs);
4402 }
4403 Request::Destroy => {}
4404 _ => {}
4405 }
4406 }
4407}
4408
4409impl Dispatch<WpFractionalScaleV1, ()> for Compositor {
4411 fn request(
4412 state: &mut Self,
4413 _: &Client,
4414 resource: &WpFractionalScaleV1,
4415 _: <WpFractionalScaleV1 as Resource>::Request,
4416 _: &(),
4417 _: &DisplayHandle,
4418 _: &mut DataInit<'_, Self>,
4419 ) {
4420 state
4422 .fractional_scales
4423 .retain(|fs| fs.id() != resource.id());
4424 }
4425}
4426
4427impl GlobalDispatch<WpViewporter, ()> for Compositor {
4429 fn bind(
4430 _: &mut Self,
4431 _: &DisplayHandle,
4432 _: &Client,
4433 resource: New<WpViewporter>,
4434 _: &(),
4435 data_init: &mut DataInit<'_, Self>,
4436 ) {
4437 data_init.init(resource, ());
4438 }
4439}
4440
4441impl Dispatch<WpViewporter, ()> for Compositor {
4442 fn request(
4443 _: &mut Self,
4444 _: &Client,
4445 _: &WpViewporter,
4446 request: <WpViewporter as Resource>::Request,
4447 _: &(),
4448 _: &DisplayHandle,
4449 data_init: &mut DataInit<'_, Self>,
4450 ) {
4451 use wp_viewporter::Request;
4452 match request {
4453 Request::GetViewport { id, surface } => {
4454 let obj_id = surface.id();
4457 data_init.init(id, obj_id);
4458 }
4459 Request::Destroy => {}
4460 _ => {}
4461 }
4462 }
4463}
4464
4465impl Dispatch<WpViewport, ObjectId> for Compositor {
4467 fn request(
4468 state: &mut Self,
4469 _: &Client,
4470 _: &WpViewport,
4471 request: <WpViewport as Resource>::Request,
4472 surface_obj_id: &ObjectId,
4473 _: &DisplayHandle,
4474 _: &mut DataInit<'_, Self>,
4475 ) {
4476 use wayland_protocols::wp::viewporter::server::wp_viewport::Request;
4477 match request {
4478 Request::SetDestination { width, height } => {
4479 if let Some(surf) = state.surfaces.get_mut(surface_obj_id) {
4480 if width > 0 && height > 0 {
4482 surf.pending_viewport_destination = Some((width, height));
4483 } else {
4484 surf.pending_viewport_destination = None;
4485 }
4486 }
4487 }
4488 Request::SetSource { .. } => {
4489 }
4491 Request::Destroy => {}
4492 _ => {}
4493 }
4494 }
4495}
4496
4497impl GlobalDispatch<WlDataDeviceManager, ()> for Compositor {
4504 fn bind(
4505 _: &mut Self,
4506 _: &DisplayHandle,
4507 _: &Client,
4508 resource: New<WlDataDeviceManager>,
4509 _: &(),
4510 data_init: &mut DataInit<'_, Self>,
4511 ) {
4512 data_init.init(resource, ());
4513 }
4514}
4515
4516impl Dispatch<WlDataDeviceManager, ()> for Compositor {
4517 fn request(
4518 state: &mut Self,
4519 _: &Client,
4520 _: &WlDataDeviceManager,
4521 request: <WlDataDeviceManager as Resource>::Request,
4522 _: &(),
4523 _: &DisplayHandle,
4524 data_init: &mut DataInit<'_, Self>,
4525 ) {
4526 use wl_data_device_manager::Request;
4527 match request {
4528 Request::CreateDataSource { id } => {
4529 data_init.init(
4530 id,
4531 DataSourceData {
4532 mime_types: std::sync::Mutex::new(Vec::new()),
4533 },
4534 );
4535 }
4536 Request::GetDataDevice { id, seat: _ } => {
4537 let dd = data_init.init(id, ());
4538 state.data_devices.push(dd);
4539 }
4540 _ => {}
4541 }
4542 }
4543}
4544
4545impl Dispatch<WlDataSource, DataSourceData> for Compositor {
4546 fn request(
4547 _: &mut Self,
4548 _: &Client,
4549 _: &WlDataSource,
4550 request: <WlDataSource as Resource>::Request,
4551 data: &DataSourceData,
4552 _: &DisplayHandle,
4553 _: &mut DataInit<'_, Self>,
4554 ) {
4555 use wl_data_source::Request;
4556 match request {
4557 Request::Offer { mime_type } => {
4558 data.mime_types.lock().unwrap().push(mime_type);
4559 }
4560 Request::Destroy => {}
4561 _ => {} }
4563 }
4564
4565 fn destroyed(
4566 state: &mut Self,
4567 _: wayland_server::backend::ClientId,
4568 resource: &WlDataSource,
4569 _: &DataSourceData,
4570 ) {
4571 if state
4572 .selection_source
4573 .as_ref()
4574 .is_some_and(|s| s.id() == resource.id())
4575 {
4576 state.selection_source = None;
4577 }
4578 }
4579}
4580
4581impl Dispatch<WlDataDevice, ()> for Compositor {
4582 fn request(
4583 state: &mut Self,
4584 _: &Client,
4585 _: &WlDataDevice,
4586 request: <WlDataDevice as Resource>::Request,
4587 _: &(),
4588 _: &DisplayHandle,
4589 _: &mut DataInit<'_, Self>,
4590 ) {
4591 use wl_data_device::Request;
4592 match request {
4593 Request::SetSelection { source, serial: _ } => {
4594 state.selection_source = source.clone();
4595 if let Some(ref src) = source {
4597 let data = src.data::<DataSourceData>().unwrap();
4598 let mimes = data.mime_types.lock().unwrap();
4599 let text_mime = mimes
4600 .iter()
4601 .find(|m| {
4602 m.as_str() == "text/plain;charset=utf-8"
4603 || m.as_str() == "text/plain"
4604 || m.as_str() == "UTF8_STRING"
4605 })
4606 .cloned();
4607 drop(mimes);
4608 if let Some(mime) = text_mime {
4609 state.read_data_source_and_emit(src, &mime);
4610 }
4611 }
4612 }
4613 Request::Release => {}
4614 _ => {} }
4616 }
4617
4618 fn destroyed(
4619 state: &mut Self,
4620 _: wayland_server::backend::ClientId,
4621 resource: &WlDataDevice,
4622 _: &(),
4623 ) {
4624 state.data_devices.retain(|d| d.id() != resource.id());
4625 }
4626}
4627
4628impl Dispatch<WlDataOffer, DataOfferData> for Compositor {
4629 fn request(
4630 state: &mut Self,
4631 _: &Client,
4632 _: &WlDataOffer,
4633 request: <WlDataOffer as Resource>::Request,
4634 data: &DataOfferData,
4635 _: &DisplayHandle,
4636 _: &mut DataInit<'_, Self>,
4637 ) {
4638 use wl_data_offer::Request;
4639 match request {
4640 Request::Receive { mime_type, fd } => {
4641 if data.external {
4642 if let Some(ref cb) = state.external_clipboard
4644 && (cb.mime_type == mime_type
4645 || mime_type == "text/plain"
4646 || mime_type == "text/plain;charset=utf-8"
4647 || mime_type == "UTF8_STRING")
4648 {
4649 use std::io::Write;
4650 let mut f = std::fs::File::from(fd);
4651 let _ = f.write_all(&cb.data);
4652 }
4653 } else if let Some(ref src) = state.selection_source {
4654 src.send(mime_type, fd.as_fd());
4656 }
4657 }
4658 Request::Destroy => {}
4659 _ => {} }
4661 }
4662}
4663
4664impl Compositor {
4665 fn read_data_source_and_emit(&mut self, source: &WlDataSource, mime_type: &str) {
4668 let mut fds = [0i32; 2];
4669 if unsafe { libc::pipe(fds.as_mut_ptr()) } != 0 {
4670 return;
4671 }
4672 let read_fd = unsafe { OwnedFd::from_raw_fd(fds[0]) };
4673 let write_fd = unsafe { OwnedFd::from_raw_fd(fds[1]) };
4674 source.send(mime_type.to_string(), write_fd.as_fd());
4675 let _ = self.display_handle.flush_clients();
4676 unsafe {
4678 libc::fcntl(read_fd.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
4679 }
4680 std::thread::sleep(std::time::Duration::from_millis(5));
4682 let mut buf = Vec::new();
4683 let mut tmp = [0u8; 8192];
4684 loop {
4685 let n = unsafe {
4686 libc::read(
4687 read_fd.as_raw_fd(),
4688 tmp.as_mut_ptr() as *mut libc::c_void,
4689 tmp.len(),
4690 )
4691 };
4692 if n <= 0 {
4693 break;
4694 }
4695 buf.extend_from_slice(&tmp[..n as usize]);
4696 if buf.len() > 1024 * 1024 {
4697 break; }
4699 }
4700 if !buf.is_empty() {
4701 let _ = self.event_tx.send(CompositorEvent::ClipboardContent {
4702 mime_type: mime_type.to_string(),
4703 data: buf,
4704 });
4705 (self.event_notify)();
4706 }
4707 }
4708
4709 fn offer_external_clipboard(&mut self) {
4711 let Some(ref cb) = self.external_clipboard else {
4712 return;
4713 };
4714 let mime = cb.mime_type.clone();
4715 for dd in &self.data_devices {
4716 if let Some(client) = dd.client() {
4717 let offer = client
4718 .create_resource::<WlDataOffer, DataOfferData, Compositor>(
4719 &self.display_handle,
4720 dd.version(),
4721 DataOfferData { external: true },
4722 )
4723 .unwrap();
4724 dd.data_offer(&offer);
4725 offer.offer(mime.clone());
4726 if mime.starts_with("text/plain") {
4728 if mime != "text/plain" {
4729 offer.offer("text/plain".to_string());
4730 }
4731 if mime != "text/plain;charset=utf-8" {
4732 offer.offer("text/plain;charset=utf-8".to_string());
4733 }
4734 offer.offer("UTF8_STRING".to_string());
4735 }
4736 dd.selection(Some(&offer));
4737 }
4738 }
4739 let _ = self.display_handle.flush_clients();
4740 }
4741}
4742
4743impl GlobalDispatch<ZwpPrimarySelectionDeviceManagerV1, ()> for Compositor {
4746 fn bind(
4747 _: &mut Self,
4748 _: &DisplayHandle,
4749 _: &Client,
4750 resource: New<ZwpPrimarySelectionDeviceManagerV1>,
4751 _: &(),
4752 data_init: &mut DataInit<'_, Self>,
4753 ) {
4754 data_init.init(resource, ());
4755 }
4756}
4757
4758impl Dispatch<ZwpPrimarySelectionDeviceManagerV1, ()> for Compositor {
4759 fn request(
4760 state: &mut Self,
4761 _: &Client,
4762 _: &ZwpPrimarySelectionDeviceManagerV1,
4763 request: <ZwpPrimarySelectionDeviceManagerV1 as Resource>::Request,
4764 _: &(),
4765 _: &DisplayHandle,
4766 data_init: &mut DataInit<'_, Self>,
4767 ) {
4768 use zwp_primary_selection_device_manager_v1::Request;
4769 match request {
4770 Request::CreateSource { id } => {
4771 data_init.init(
4772 id,
4773 PrimarySourceData {
4774 mime_types: std::sync::Mutex::new(Vec::new()),
4775 },
4776 );
4777 }
4778 Request::GetDevice { id, seat: _ } => {
4779 let pd = data_init.init(id, ());
4780 state.primary_devices.push(pd);
4781 }
4782 Request::Destroy => {}
4783 _ => {}
4784 }
4785 }
4786}
4787
4788impl Dispatch<ZwpPrimarySelectionSourceV1, PrimarySourceData> for Compositor {
4789 fn request(
4790 _: &mut Self,
4791 _: &Client,
4792 _: &ZwpPrimarySelectionSourceV1,
4793 request: <ZwpPrimarySelectionSourceV1 as Resource>::Request,
4794 data: &PrimarySourceData,
4795 _: &DisplayHandle,
4796 _: &mut DataInit<'_, Self>,
4797 ) {
4798 use zwp_primary_selection_source_v1::Request;
4799 match request {
4800 Request::Offer { mime_type } => {
4801 data.mime_types.lock().unwrap().push(mime_type);
4802 }
4803 Request::Destroy => {}
4804 _ => {}
4805 }
4806 }
4807
4808 fn destroyed(
4809 state: &mut Self,
4810 _: wayland_server::backend::ClientId,
4811 resource: &ZwpPrimarySelectionSourceV1,
4812 _: &PrimarySourceData,
4813 ) {
4814 if state
4815 .primary_source
4816 .as_ref()
4817 .is_some_and(|s| s.id() == resource.id())
4818 {
4819 state.primary_source = None;
4820 }
4821 }
4822}
4823
4824impl Dispatch<ZwpPrimarySelectionDeviceV1, ()> for Compositor {
4825 fn request(
4826 state: &mut Self,
4827 _: &Client,
4828 _: &ZwpPrimarySelectionDeviceV1,
4829 request: <ZwpPrimarySelectionDeviceV1 as Resource>::Request,
4830 _: &(),
4831 _: &DisplayHandle,
4832 _: &mut DataInit<'_, Self>,
4833 ) {
4834 use zwp_primary_selection_device_v1::Request;
4835 match request {
4836 Request::SetSelection { source, serial: _ } => {
4837 state.primary_source = source;
4838 }
4839 Request::Destroy => {}
4840 _ => {}
4841 }
4842 }
4843
4844 fn destroyed(
4845 state: &mut Self,
4846 _: wayland_server::backend::ClientId,
4847 resource: &ZwpPrimarySelectionDeviceV1,
4848 _: &(),
4849 ) {
4850 state.primary_devices.retain(|d| d.id() != resource.id());
4851 }
4852}
4853
4854impl Dispatch<ZwpPrimarySelectionOfferV1, PrimaryOfferData> for Compositor {
4855 fn request(
4856 state: &mut Self,
4857 _: &Client,
4858 _: &ZwpPrimarySelectionOfferV1,
4859 request: <ZwpPrimarySelectionOfferV1 as Resource>::Request,
4860 data: &PrimaryOfferData,
4861 _: &DisplayHandle,
4862 _: &mut DataInit<'_, Self>,
4863 ) {
4864 use zwp_primary_selection_offer_v1::Request;
4865 match request {
4866 Request::Receive { mime_type, fd } => {
4867 if data.external {
4868 if let Some(ref cb) = state.external_primary {
4869 use std::io::Write;
4870 let mut f = std::fs::File::from(fd);
4871 let _ = f.write_all(&cb.data);
4872 let _ = mime_type; }
4874 } else if let Some(ref src) = state.primary_source {
4875 src.send(mime_type, fd.as_fd());
4876 }
4877 }
4878 Request::Destroy => {}
4879 _ => {}
4880 }
4881 }
4882}
4883
4884impl GlobalDispatch<ZwpPointerConstraintsV1, ()> for Compositor {
4887 fn bind(
4888 _: &mut Self,
4889 _: &DisplayHandle,
4890 _: &Client,
4891 resource: New<ZwpPointerConstraintsV1>,
4892 _: &(),
4893 data_init: &mut DataInit<'_, Self>,
4894 ) {
4895 data_init.init(resource, ());
4896 }
4897}
4898
4899impl Dispatch<ZwpPointerConstraintsV1, ()> for Compositor {
4900 fn request(
4901 _: &mut Self,
4902 _: &Client,
4903 _: &ZwpPointerConstraintsV1,
4904 request: <ZwpPointerConstraintsV1 as Resource>::Request,
4905 _: &(),
4906 _: &DisplayHandle,
4907 data_init: &mut DataInit<'_, Self>,
4908 ) {
4909 use zwp_pointer_constraints_v1::Request;
4910 match request {
4911 Request::LockPointer {
4912 id,
4913 surface: _,
4914 pointer: _,
4915 region: _,
4916 lifetime: _,
4917 } => {
4918 let lp = data_init.init(id, ());
4919 lp.locked();
4921 }
4922 Request::ConfinePointer {
4923 id,
4924 surface: _,
4925 pointer: _,
4926 region: _,
4927 lifetime: _,
4928 } => {
4929 let cp = data_init.init(id, ());
4930 cp.confined();
4931 }
4932 Request::Destroy => {}
4933 _ => {}
4934 }
4935 }
4936}
4937
4938impl Dispatch<ZwpLockedPointerV1, ()> for Compositor {
4939 fn request(
4940 _: &mut Self,
4941 _: &Client,
4942 _: &ZwpLockedPointerV1,
4943 _: <ZwpLockedPointerV1 as Resource>::Request,
4944 _: &(),
4945 _: &DisplayHandle,
4946 _: &mut DataInit<'_, Self>,
4947 ) {
4948 }
4950}
4951
4952impl Dispatch<ZwpConfinedPointerV1, ()> for Compositor {
4953 fn request(
4954 _: &mut Self,
4955 _: &Client,
4956 _: &ZwpConfinedPointerV1,
4957 _: <ZwpConfinedPointerV1 as Resource>::Request,
4958 _: &(),
4959 _: &DisplayHandle,
4960 _: &mut DataInit<'_, Self>,
4961 ) {
4962 }
4964}
4965
4966impl GlobalDispatch<ZwpRelativePointerManagerV1, ()> for Compositor {
4969 fn bind(
4970 _: &mut Self,
4971 _: &DisplayHandle,
4972 _: &Client,
4973 resource: New<ZwpRelativePointerManagerV1>,
4974 _: &(),
4975 data_init: &mut DataInit<'_, Self>,
4976 ) {
4977 data_init.init(resource, ());
4978 }
4979}
4980
4981impl Dispatch<ZwpRelativePointerManagerV1, ()> for Compositor {
4982 fn request(
4983 state: &mut Self,
4984 _: &Client,
4985 _: &ZwpRelativePointerManagerV1,
4986 request: <ZwpRelativePointerManagerV1 as Resource>::Request,
4987 _: &(),
4988 _: &DisplayHandle,
4989 data_init: &mut DataInit<'_, Self>,
4990 ) {
4991 use zwp_relative_pointer_manager_v1::Request;
4992 match request {
4993 Request::GetRelativePointer { id, pointer: _ } => {
4994 let rp = data_init.init(id, ());
4995 state.relative_pointers.push(rp);
4996 }
4997 Request::Destroy => {}
4998 _ => {}
4999 }
5000 }
5001}
5002
5003impl Dispatch<ZwpRelativePointerV1, ()> for Compositor {
5004 fn request(
5005 state: &mut Self,
5006 _: &Client,
5007 resource: &ZwpRelativePointerV1,
5008 _: <ZwpRelativePointerV1 as Resource>::Request,
5009 _: &(),
5010 _: &DisplayHandle,
5011 _: &mut DataInit<'_, Self>,
5012 ) {
5013 state
5015 .relative_pointers
5016 .retain(|rp| rp.id() != resource.id());
5017 }
5018}
5019
5020impl GlobalDispatch<ZwpTextInputManagerV3, ()> for Compositor {
5023 fn bind(
5024 _: &mut Self,
5025 _: &DisplayHandle,
5026 _: &Client,
5027 resource: New<ZwpTextInputManagerV3>,
5028 _: &(),
5029 data_init: &mut DataInit<'_, Self>,
5030 ) {
5031 data_init.init(resource, ());
5032 }
5033}
5034
5035impl Dispatch<ZwpTextInputManagerV3, ()> for Compositor {
5036 fn request(
5037 state: &mut Self,
5038 _: &Client,
5039 _: &ZwpTextInputManagerV3,
5040 request: <ZwpTextInputManagerV3 as Resource>::Request,
5041 _: &(),
5042 _: &DisplayHandle,
5043 data_init: &mut DataInit<'_, Self>,
5044 ) {
5045 use zwp_text_input_manager_v3::Request;
5046 match request {
5047 Request::GetTextInput { id, seat: _ } => {
5048 let ti = data_init.init(id, ());
5049 state.text_inputs.push(TextInputState {
5050 resource: ti,
5051 enabled: false,
5052 });
5053 }
5054 Request::Destroy => {}
5055 _ => {}
5056 }
5057 }
5058}
5059
5060impl Dispatch<ZwpTextInputV3, ()> for Compositor {
5061 fn request(
5062 state: &mut Self,
5063 _: &Client,
5064 resource: &ZwpTextInputV3,
5065 request: <ZwpTextInputV3 as Resource>::Request,
5066 _: &(),
5067 _: &DisplayHandle,
5068 _: &mut DataInit<'_, Self>,
5069 ) {
5070 use zwp_text_input_v3::Request;
5071 match request {
5072 Request::Enable => {
5073 if let Some(ti) = state
5074 .text_inputs
5075 .iter_mut()
5076 .find(|t| t.resource.id() == resource.id())
5077 {
5078 ti.enabled = true;
5079 }
5080 }
5081 Request::Disable => {
5082 if let Some(ti) = state
5083 .text_inputs
5084 .iter_mut()
5085 .find(|t| t.resource.id() == resource.id())
5086 {
5087 ti.enabled = false;
5088 }
5089 }
5090 Request::Commit => {
5091 }
5093 Request::Destroy => {
5094 state
5095 .text_inputs
5096 .retain(|t| t.resource.id() != resource.id());
5097 }
5098 _ => {}
5101 }
5102 }
5103}
5104
5105impl GlobalDispatch<XdgActivationV1, ()> for Compositor {
5108 fn bind(
5109 _: &mut Self,
5110 _: &DisplayHandle,
5111 _: &Client,
5112 resource: New<XdgActivationV1>,
5113 _: &(),
5114 data_init: &mut DataInit<'_, Self>,
5115 ) {
5116 data_init.init(resource, ());
5117 }
5118}
5119
5120impl Dispatch<XdgActivationV1, ()> for Compositor {
5121 fn request(
5122 state: &mut Self,
5123 _: &Client,
5124 _: &XdgActivationV1,
5125 request: <XdgActivationV1 as Resource>::Request,
5126 _: &(),
5127 _: &DisplayHandle,
5128 data_init: &mut DataInit<'_, Self>,
5129 ) {
5130 use xdg_activation_v1::Request;
5131 match request {
5132 Request::GetActivationToken { id } => {
5133 let serial = state.next_activation_token;
5134 state.next_activation_token = serial.wrapping_add(1);
5135 data_init.init(id, ActivationTokenData { serial });
5136 }
5137 Request::Activate {
5138 token: _,
5139 surface: _,
5140 } => {
5141 }
5144 Request::Destroy => {}
5145 _ => {}
5146 }
5147 }
5148}
5149
5150impl Dispatch<XdgActivationTokenV1, ActivationTokenData> for Compositor {
5151 fn request(
5152 _: &mut Self,
5153 _: &Client,
5154 resource: &XdgActivationTokenV1,
5155 request: <XdgActivationTokenV1 as Resource>::Request,
5156 data: &ActivationTokenData,
5157 _: &DisplayHandle,
5158 _: &mut DataInit<'_, Self>,
5159 ) {
5160 use xdg_activation_token_v1::Request;
5161 match request {
5162 Request::Commit => {
5163 resource.done(format!("blit-token-{}", data.serial));
5166 }
5167 Request::SetSerial { .. } | Request::SetAppId { .. } | Request::SetSurface { .. } => {}
5168 Request::Destroy => {}
5169 _ => {}
5170 }
5171 }
5172}
5173
5174impl GlobalDispatch<WpCursorShapeManagerV1, ()> for Compositor {
5177 fn bind(
5178 _: &mut Self,
5179 _: &DisplayHandle,
5180 _: &Client,
5181 resource: New<WpCursorShapeManagerV1>,
5182 _: &(),
5183 data_init: &mut DataInit<'_, Self>,
5184 ) {
5185 data_init.init(resource, ());
5186 }
5187}
5188
5189impl Dispatch<WpCursorShapeManagerV1, ()> for Compositor {
5190 fn request(
5191 _: &mut Self,
5192 _: &Client,
5193 _: &WpCursorShapeManagerV1,
5194 request: <WpCursorShapeManagerV1 as Resource>::Request,
5195 _: &(),
5196 _: &DisplayHandle,
5197 data_init: &mut DataInit<'_, Self>,
5198 ) {
5199 use wp_cursor_shape_manager_v1::Request;
5200 match request {
5201 Request::GetPointer {
5202 cursor_shape_device,
5203 pointer: _,
5204 } => {
5205 data_init.init(cursor_shape_device, ());
5206 }
5207 Request::GetTabletToolV2 {
5208 cursor_shape_device,
5209 tablet_tool: _,
5210 } => {
5211 data_init.init(cursor_shape_device, ());
5212 }
5213 Request::Destroy => {}
5214 _ => {}
5215 }
5216 }
5217}
5218
5219impl Dispatch<WpCursorShapeDeviceV1, ()> for Compositor {
5220 fn request(
5221 state: &mut Self,
5222 _: &Client,
5223 _: &WpCursorShapeDeviceV1,
5224 request: <WpCursorShapeDeviceV1 as Resource>::Request,
5225 _: &(),
5226 _: &DisplayHandle,
5227 _: &mut DataInit<'_, Self>,
5228 ) {
5229 use wp_cursor_shape_device_v1::Request;
5230 match request {
5231 Request::SetShape { serial: _, shape } => {
5232 use wayland_server::WEnum;
5233 use wp_cursor_shape_device_v1::Shape;
5234 let name = match shape {
5235 WEnum::Value(Shape::Default) => "default",
5236 WEnum::Value(Shape::ContextMenu) => "context-menu",
5237 WEnum::Value(Shape::Help) => "help",
5238 WEnum::Value(Shape::Pointer) => "pointer",
5239 WEnum::Value(Shape::Progress) => "progress",
5240 WEnum::Value(Shape::Wait) => "wait",
5241 WEnum::Value(Shape::Cell) => "cell",
5242 WEnum::Value(Shape::Crosshair) => "crosshair",
5243 WEnum::Value(Shape::Text) => "text",
5244 WEnum::Value(Shape::VerticalText) => "vertical-text",
5245 WEnum::Value(Shape::Alias) => "alias",
5246 WEnum::Value(Shape::Copy) => "copy",
5247 WEnum::Value(Shape::Move) => "move",
5248 WEnum::Value(Shape::NoDrop) => "no-drop",
5249 WEnum::Value(Shape::NotAllowed) => "not-allowed",
5250 WEnum::Value(Shape::Grab) => "grab",
5251 WEnum::Value(Shape::Grabbing) => "grabbing",
5252 WEnum::Value(Shape::EResize) => "e-resize",
5253 WEnum::Value(Shape::NResize) => "n-resize",
5254 WEnum::Value(Shape::NeResize) => "ne-resize",
5255 WEnum::Value(Shape::NwResize) => "nw-resize",
5256 WEnum::Value(Shape::SResize) => "s-resize",
5257 WEnum::Value(Shape::SeResize) => "se-resize",
5258 WEnum::Value(Shape::SwResize) => "sw-resize",
5259 WEnum::Value(Shape::WResize) => "w-resize",
5260 WEnum::Value(Shape::EwResize) => "ew-resize",
5261 WEnum::Value(Shape::NsResize) => "ns-resize",
5262 WEnum::Value(Shape::NeswResize) => "nesw-resize",
5263 WEnum::Value(Shape::NwseResize) => "nwse-resize",
5264 WEnum::Value(Shape::ColResize) => "col-resize",
5265 WEnum::Value(Shape::RowResize) => "row-resize",
5266 WEnum::Value(Shape::AllScroll) => "all-scroll",
5267 WEnum::Value(Shape::ZoomIn) => "zoom-in",
5268 WEnum::Value(Shape::ZoomOut) => "zoom-out",
5269 _ => "default",
5270 };
5271 let _ = state.event_tx.send(CompositorEvent::SurfaceCursor {
5272 surface_id: state.focused_surface_id,
5273 cursor: CursorImage::Named(name.to_string()),
5274 });
5275 (state.event_notify)();
5276 }
5277 Request::Destroy => {}
5278 _ => {}
5279 }
5280 }
5281}
5282
5283impl wayland_server::backend::ClientData for ClientState {
5285 fn initialized(&self, _: wayland_server::backend::ClientId) {}
5286 fn disconnected(
5287 &self,
5288 _: wayland_server::backend::ClientId,
5289 _: wayland_server::backend::DisconnectReason,
5290 ) {
5291 }
5292}
5293
5294pub struct CompositorHandle {
5299 pub event_rx: mpsc::Receiver<CompositorEvent>,
5300 pub command_tx: mpsc::Sender<CompositorCommand>,
5301 pub socket_name: String,
5302 pub thread: std::thread::JoinHandle<()>,
5303 pub shutdown: Arc<AtomicBool>,
5304 pub vulkan_video_encode: bool,
5306 pub vulkan_video_encode_av1: bool,
5308 loop_signal: LoopSignal,
5309}
5310
5311impl CompositorHandle {
5312 pub fn wake(&self) {
5313 self.loop_signal.wakeup();
5314 }
5315}
5316
5317pub fn spawn_compositor(
5318 verbose: bool,
5319 event_notify: Arc<dyn Fn() + Send + Sync>,
5320 gpu_device: &str,
5321) -> CompositorHandle {
5322 let _gpu_device = gpu_device.to_string();
5323 let (event_tx, event_rx) = mpsc::channel();
5324 let (command_tx, command_rx) = mpsc::channel();
5325 let (socket_tx, socket_rx) = mpsc::sync_channel(1);
5326 let (signal_tx, signal_rx) = mpsc::sync_channel::<LoopSignal>(1);
5327 let (caps_tx, caps_rx) = mpsc::sync_channel::<(bool, bool)>(1);
5328 let shutdown = Arc::new(AtomicBool::new(false));
5329 let shutdown_clone = shutdown.clone();
5330
5331 let runtime_dir = std::env::var_os("XDG_RUNTIME_DIR")
5332 .map(std::path::PathBuf::from)
5333 .filter(|p| {
5334 let probe = p.join(".blit-probe");
5335 if std::fs::write(&probe, b"").is_ok() {
5336 let _ = std::fs::remove_file(&probe);
5337 true
5338 } else {
5339 false
5340 }
5341 })
5342 .unwrap_or_else(std::env::temp_dir);
5343
5344 let runtime_dir_clone = runtime_dir.clone();
5345 let thread = std::thread::Builder::new()
5346 .name("compositor".into())
5347 .spawn(move || {
5348 unsafe { std::env::set_var("XDG_RUNTIME_DIR", &runtime_dir_clone) };
5349 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
5350 run_compositor(
5351 event_tx,
5352 command_rx,
5353 socket_tx,
5354 signal_tx,
5355 caps_tx,
5356 event_notify,
5357 shutdown_clone,
5358 verbose,
5359 _gpu_device,
5360 );
5361 }));
5362 if let Err(e) = result {
5363 let msg = if let Some(s) = e.downcast_ref::<&str>() {
5364 s.to_string()
5365 } else if let Some(s) = e.downcast_ref::<String>() {
5366 s.clone()
5367 } else {
5368 "unknown panic".to_string()
5369 };
5370 eprintln!("[compositor] PANIC: {msg}");
5371 }
5372 })
5373 .expect("failed to spawn compositor thread");
5374
5375 let socket_name = socket_rx.recv().expect("compositor failed to start");
5376 let socket_name = runtime_dir
5377 .join(&socket_name)
5378 .to_string_lossy()
5379 .into_owned();
5380 let loop_signal = signal_rx
5381 .recv()
5382 .expect("compositor failed to send loop signal");
5383 let (vulkan_video_encode, vulkan_video_encode_av1) = caps_rx.recv().unwrap_or((false, false));
5384
5385 CompositorHandle {
5386 event_rx,
5387 command_tx,
5388 socket_name,
5389 thread,
5390 shutdown,
5391 vulkan_video_encode,
5392 vulkan_video_encode_av1,
5393 loop_signal,
5394 }
5395}
5396
5397#[allow(clippy::too_many_arguments)]
5398fn run_compositor(
5399 event_tx: mpsc::Sender<CompositorEvent>,
5400 command_rx: mpsc::Receiver<CompositorCommand>,
5401 socket_tx: mpsc::SyncSender<String>,
5402 signal_tx: mpsc::SyncSender<LoopSignal>,
5403 caps_tx: mpsc::SyncSender<(bool, bool)>,
5404 event_notify: Arc<dyn Fn() + Send + Sync>,
5405 shutdown: Arc<AtomicBool>,
5406 verbose: bool,
5407 gpu_device: String,
5408) {
5409 let mut event_loop: EventLoop<Compositor> =
5410 EventLoop::try_new().expect("failed to create event loop");
5411 let loop_signal = event_loop.get_signal();
5412
5413 let display: Display<Compositor> = Display::new().expect("failed to create display");
5414 let dh = display.handle();
5415
5416 eprintln!("[compositor] trying Vulkan renderer for {gpu_device}");
5419 let vulkan_renderer = super::vulkan_render::VulkanRenderer::try_new(&gpu_device);
5420 let has_dmabuf = vulkan_renderer.as_ref().is_some_and(|vk| vk.has_dmabuf());
5421 eprintln!(
5422 "[compositor] Vulkan renderer: {} (dmabuf={})",
5423 vulkan_renderer.is_some(),
5424 has_dmabuf,
5425 );
5426
5427 dh.create_global::<Compositor, WlCompositor, ()>(6, ());
5429 dh.create_global::<Compositor, WlSubcompositor, ()>(1, ());
5430 dh.create_global::<Compositor, XdgWmBase, ()>(6, ());
5431 dh.create_global::<Compositor, WlShm, ()>(1, ());
5432 dh.create_global::<Compositor, WlOutput, ()>(4, ());
5433 dh.create_global::<Compositor, WlSeat, ()>(9, ());
5434 if has_dmabuf {
5439 dh.create_global::<Compositor, ZwpLinuxDmabufV1, ()>(4, ());
5440 }
5441 dh.create_global::<Compositor, WpViewporter, ()>(1, ());
5442 dh.create_global::<Compositor, WpFractionalScaleManagerV1, ()>(1, ());
5443 dh.create_global::<Compositor, ZxdgDecorationManagerV1, ()>(1, ());
5444 dh.create_global::<Compositor, WlDataDeviceManager, ()>(3, ());
5445 dh.create_global::<Compositor, ZwpPointerConstraintsV1, ()>(1, ());
5446 dh.create_global::<Compositor, ZwpRelativePointerManagerV1, ()>(1, ());
5447 dh.create_global::<Compositor, XdgActivationV1, ()>(1, ());
5448 dh.create_global::<Compositor, WpCursorShapeManagerV1, ()>(1, ());
5449 dh.create_global::<Compositor, ZwpPrimarySelectionDeviceManagerV1, ()>(1, ());
5450 dh.create_global::<Compositor, WpPresentation, ()>(1, ());
5451 dh.create_global::<Compositor, ZwpTextInputManagerV3, ()>(1, ());
5452
5453 let keymap_string = include_str!("../data/us-qwerty.xkb");
5455 let mut keymap_data = keymap_string.as_bytes().to_vec();
5456 keymap_data.push(0); let listening_socket = wayland_server::ListeningSocket::bind_auto("wayland", 0..33)
5460 .unwrap_or_else(|e| {
5461 let dir = std::env::var("XDG_RUNTIME_DIR").unwrap_or_else(|_| "(unset)".into());
5462 panic!("failed to create wayland socket in XDG_RUNTIME_DIR={dir}: {e}\nhint: ensure the directory exists and is writable by the current user");
5463 });
5464 let socket_name = listening_socket
5465 .socket_name()
5466 .unwrap()
5467 .to_string_lossy()
5468 .into_owned();
5469 socket_tx.send(socket_name).unwrap();
5470 let _ = signal_tx.send(loop_signal.clone());
5471
5472 let mut compositor = Compositor {
5473 display_handle: dh,
5474 surfaces: HashMap::new(),
5475 toplevel_surface_ids: HashMap::new(),
5476 next_surface_id: 1,
5477 shm_pools: HashMap::new(),
5478 surface_meta: HashMap::new(),
5479 dmabuf_params: HashMap::new(),
5480 vulkan_renderer,
5481 output_width: 1920,
5482 output_height: 1080,
5483 output_refresh_mhz: 60_000,
5484 output_scale_120: 120,
5485 outputs: Vec::new(),
5486 keyboards: Vec::new(),
5487 pointers: Vec::new(),
5488 keyboard_keymap_data: keymap_data,
5489 mods_depressed: 0,
5490 mods_locked: 0,
5491 serial: 0,
5492 event_tx,
5493 event_notify,
5494 loop_signal: loop_signal.clone(),
5495 pending_commits: HashMap::new(),
5496 pending_native_sizes: HashMap::new(),
5497 pending_recomposite_toplevels: HashSet::new(),
5498 focused_surface_id: 0,
5499 pointer_entered_id: None,
5500 pending_kb_reenter: false,
5501 gpu_device,
5502 verbose,
5503 shutdown: shutdown.clone(),
5504 last_reported_size: HashMap::new(),
5505 surface_sizes: HashMap::new(),
5506 positioners: HashMap::new(),
5507 fractional_scales: Vec::new(),
5508 data_devices: Vec::new(),
5509 selection_source: None,
5510 external_clipboard: None,
5511 primary_devices: Vec::new(),
5512 primary_source: None,
5513 external_primary: None,
5514 relative_pointers: Vec::new(),
5515 text_inputs: Vec::new(),
5516 text_input_serial: 0,
5517 next_activation_token: 1,
5518 popup_grab_stack: Vec::new(),
5519 held_buffers: HashMap::new(),
5520 cursor_rgba: HashMap::new(),
5521 };
5522
5523 {
5525 let (vve, vve_av1) = compositor
5526 .vulkan_renderer
5527 .as_ref()
5528 .map(|vk| (vk.has_video_encode(), vk.has_video_encode_av1()))
5529 .unwrap_or((false, false));
5530 let _ = caps_tx.send((vve, vve_av1));
5531 }
5532
5533 let handle = event_loop.handle();
5534
5535 let display_source = Generic::new(display, Interest::READ, calloop::Mode::Level);
5537 handle
5538 .insert_source(display_source, |_, display, state| {
5539 let d = unsafe { display.get_mut() };
5540 if let Err(e) = d.dispatch_clients(state)
5541 && state.verbose
5542 {
5543 eprintln!("[compositor] dispatch_clients error: {e}");
5544 }
5545 state.cleanup_dead_surfaces();
5546 if let Err(e) = d.flush_clients()
5547 && state.verbose
5548 {
5549 eprintln!("[compositor] flush_clients error: {e}");
5550 }
5551 Ok(PostAction::Continue)
5552 })
5553 .expect("failed to insert display source");
5554
5555 let socket_source = Generic::new(listening_socket, Interest::READ, calloop::Mode::Level);
5557 handle
5558 .insert_source(socket_source, |_, socket, state| {
5559 let ls = unsafe { socket.get_mut() };
5560 if let Some(client_stream) = ls.accept().ok().flatten()
5561 && let Err(e) = state
5562 .display_handle
5563 .insert_client(client_stream, Arc::new(ClientState))
5564 && state.verbose
5565 {
5566 eprintln!("[compositor] insert_client error: {e}");
5567 }
5568 Ok(PostAction::Continue)
5569 })
5570 .expect("failed to insert listening socket");
5571
5572 if verbose {
5573 eprintln!("[compositor] entering event loop");
5574 }
5575
5576 while !shutdown.load(Ordering::Relaxed) {
5577 while let Ok(cmd) = command_rx.try_recv() {
5579 match cmd {
5580 CompositorCommand::Shutdown => {
5581 shutdown.store(true, Ordering::Relaxed);
5582 return;
5583 }
5584 other => compositor.handle_command(other),
5585 }
5586 }
5587
5588 let poll_timeout = if compositor
5591 .vulkan_renderer
5592 .as_ref()
5593 .is_some_and(|vk| vk.has_pending())
5594 {
5595 std::time::Duration::from_millis(1)
5596 } else {
5597 std::time::Duration::from_secs(1)
5598 };
5599
5600 if let Err(e) = event_loop.dispatch(Some(poll_timeout), &mut compositor)
5601 && verbose
5602 {
5603 eprintln!("[compositor] event loop error: {e}");
5604 }
5605
5606 if let Some(ref mut vk) = compositor.vulkan_renderer {
5612 let retired = vk.try_retire_pending();
5613 if !retired.is_empty() {
5614 let s120_u32 = (compositor.output_scale_120 as u32).max(120);
5615 if let Some(&(sid, nw, nh, _)) = retired
5619 .iter()
5620 .max_by_key(|&&(_, w, h, _)| (w as u64) * (h as u64))
5621 {
5622 let log_w = (nw * 120).div_ceil(s120_u32);
5623 let log_h = (nh * 120).div_ceil(s120_u32);
5624 compositor
5625 .pending_native_sizes
5626 .entry(sid)
5627 .or_insert((nw, nh, log_w, log_h));
5628 }
5629 for (sid, w, h, pixels) in retired {
5630 let log_w = (w * 120).div_ceil(s120_u32);
5631 let log_h = (h * 120).div_ceil(s120_u32);
5632 compositor
5633 .pending_commits
5634 .insert((sid, w, h), (log_w, log_h, pixels));
5635 }
5636 }
5637 }
5638
5639 let can_recomposite = compositor
5648 .vulkan_renderer
5649 .as_ref()
5650 .is_some_and(|vk| !vk.has_pending());
5651 if can_recomposite
5652 && let Some(&sid) = compositor.pending_recomposite_toplevels.iter().next()
5653 {
5654 compositor.pending_recomposite_toplevels.remove(&sid);
5655 if let Some(root_id) = compositor.toplevel_surface_ids.get(&sid).cloned() {
5656 compositor.composite_toplevel_into_pending(&root_id, sid);
5657 loop_signal.wakeup();
5662 }
5663 }
5664
5665 if !compositor.pending_commits.is_empty() || !compositor.pending_native_sizes.is_empty() {
5666 compositor.flush_pending_commits();
5667 }
5668
5669 if let Err(e) = compositor.display_handle.flush_clients()
5670 && verbose
5671 {
5672 eprintln!("[compositor] flush error: {e}");
5673 }
5674 }
5675
5676 if verbose {
5677 eprintln!("[compositor] event loop exited");
5678 }
5679}