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