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