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