dim_screen/
dim.rs

1use log::{debug, error};
2use smithay_client_toolkit::{
3    compositor::{CompositorHandler, CompositorState, Region},
4    delegate_compositor, delegate_keyboard, delegate_layer, delegate_output, delegate_pointer,
5    delegate_registry, delegate_seat, delegate_simple, delegate_touch,
6    output::{OutputHandler, OutputState},
7    reexports::{
8        client::{
9            globals::GlobalList,
10            protocol::{
11                wl_buffer::{self, WlBuffer},
12                wl_keyboard,
13                wl_output::WlOutput,
14                wl_pointer, wl_touch,
15            },
16            Connection, Dispatch, QueueHandle,
17        },
18        protocols::wp::{
19            single_pixel_buffer::v1::client::wp_single_pixel_buffer_manager_v1::{
20                self, WpSinglePixelBufferManagerV1,
21            },
22            viewporter::client::{
23                wp_viewport::{self, WpViewport},
24                wp_viewporter::{self, WpViewporter},
25            },
26        },
27    },
28    registry::{ProvidesRegistryState, RegistryState, SimpleGlobal},
29    registry_handlers,
30    seat::{
31        keyboard::KeyboardHandler,
32        pointer::{PointerEvent, PointerEventKind, PointerHandler},
33        touch::TouchHandler,
34        Capability, SeatHandler, SeatState,
35    },
36    shell::{
37        wlr_layer::{KeyboardInteractivity, Layer, LayerShell, LayerShellHandler, LayerSurface},
38        WaylandSurface,
39    },
40};
41
42use crate::INIT_SIZE;
43
44pub struct DimData {
45    compositor: CompositorState,
46    registry_state: RegistryState,
47    seat_state: SeatState,
48    output_state: OutputState,
49    layer_shell: LayerShell,
50    pixel_buffer_mgr: SimpleGlobal<WpSinglePixelBufferManagerV1, 1>,
51    viewporter: SimpleGlobal<WpViewporter, 1>,
52
53    alpha: f32,
54    passthrough: bool,
55    views: Vec<DimView>,
56
57    keyboard: Option<wl_keyboard::WlKeyboard>,
58    pointer: Option<wl_pointer::WlPointer>,
59    touch: Option<wl_touch::WlTouch>,
60    exit: bool,
61}
62
63struct DimView {
64    first_configure: bool,
65    width: u32,
66    height: u32,
67    buffer: WlBuffer,
68    viewport: WpViewport,
69    layer: LayerSurface,
70    output: WlOutput,
71}
72
73impl DimData {
74    pub fn new(
75        compositor: CompositorState,
76        globals: &GlobalList,
77        qh: &QueueHandle<Self>,
78        layer_shell: LayerShell,
79        alpha: f32,
80        passthrough: bool,
81    ) -> Self {
82        Self {
83            compositor,
84            registry_state: RegistryState::new(globals),
85            seat_state: SeatState::new(globals, qh),
86            output_state: OutputState::new(globals, qh),
87            layer_shell,
88            pixel_buffer_mgr: SimpleGlobal::<WpSinglePixelBufferManagerV1, 1>::bind(globals, qh)
89                .expect("wp_single_pixel_buffer_manager_v1 not available!"),
90            viewporter: SimpleGlobal::<wp_viewporter::WpViewporter, 1>::bind(globals, qh)
91                .expect("wp_viewporter not available"),
92
93            alpha,
94            passthrough,
95            views: Vec::new(),
96
97            exit: false,
98            keyboard: None,
99            pointer: None,
100            touch: None,
101        }
102    }
103
104    pub fn should_exit(&self) -> bool {
105        self.exit
106    }
107
108    fn create_view(&self, qh: &QueueHandle<Self>, output: WlOutput) -> DimView {
109        let layer = self.layer_shell.create_layer_surface(
110            qh,
111            self.compositor.create_surface(qh),
112            Layer::Overlay,
113            Some("dim_layer"),
114            Some(&output),
115        );
116
117        let (width, height) = if let Some((width, height)) = self
118            .output_state
119            .info(&output)
120            .and_then(|info| info.logical_size)
121        {
122            (width as u32, height as u32)
123        } else {
124            (INIT_SIZE, INIT_SIZE)
125        };
126
127        if self.passthrough {
128            let input_region = Region::new(&self.compositor).expect("Failed to get a wl_region");
129            layer.set_keyboard_interactivity(KeyboardInteractivity::None);
130            layer.set_input_region(Some(input_region.wl_region()));
131        } else {
132            layer.set_keyboard_interactivity(KeyboardInteractivity::Exclusive);
133        }
134
135        layer.set_exclusive_zone(-1);
136        layer.set_size(width, height);
137        layer.commit();
138
139        let viewport = self
140            .viewporter
141            .get()
142            .expect("wp_viewporter failed")
143            .get_viewport(layer.wl_surface(), qh, ());
144
145        // pre-multiply alpha
146        let alpha = (u32::MAX as f32 * self.alpha) as u32;
147        let buffer = self
148            .pixel_buffer_mgr
149            .get()
150            .expect("failed to get buffer")
151            .create_u32_rgba_buffer(0, 0, 0, alpha, qh, ());
152
153        DimView::new(qh, buffer, viewport, layer, output)
154    }
155}
156
157impl DimView {
158    fn new(
159        _qh: &QueueHandle<DimData>,
160        buffer: WlBuffer,
161        viewport: WpViewport,
162        layer: LayerSurface,
163        output: WlOutput,
164    ) -> Self {
165        Self {
166            first_configure: true,
167            width: INIT_SIZE,
168            height: INIT_SIZE,
169            buffer,
170            viewport,
171            layer,
172            output,
173        }
174    }
175
176    fn draw(&mut self, _qh: &QueueHandle<DimData>) {
177        debug!("Requesting draw");
178        if !self.first_configure {
179            // we only need to draw once as it is a static color
180            return;
181        }
182
183        self.layer.wl_surface().attach(Some(&self.buffer), 0, 0);
184        self.layer.commit();
185
186        debug!("Drawn");
187    }
188}
189
190impl LayerShellHandler for DimData {
191    fn closed(
192        &mut self,
193        _conn: &smithay_client_toolkit::reexports::client::Connection,
194        _qh: &QueueHandle<Self>,
195        _layer: &LayerSurface,
196    ) {
197        debug!("Closed");
198        self.exit = true;
199    }
200
201    fn configure(
202        &mut self,
203        _conn: &smithay_client_toolkit::reexports::client::Connection,
204        qh: &QueueHandle<Self>,
205        layer: &LayerSurface,
206        configure: smithay_client_toolkit::shell::wlr_layer::LayerSurfaceConfigure,
207        _serial: u32,
208    ) {
209        let Some(view) = self.views.iter_mut().find(|view| &view.layer == layer) else {
210            error!("Configuring layer not in self.views?");
211            return;
212        };
213
214        (view.width, view.height) = configure.new_size;
215
216        view.viewport
217            .set_destination(view.width as _, view.height as _);
218
219        if view.first_configure {
220            view.draw(qh);
221            view.first_configure = false;
222        }
223    }
224}
225
226impl CompositorHandler for DimData {
227    fn scale_factor_changed(
228        &mut self,
229        _conn: &smithay_client_toolkit::reexports::client::Connection,
230        _qh: &QueueHandle<Self>,
231        _surface: &smithay_client_toolkit::reexports::client::protocol::wl_surface::WlSurface,
232        _new_factor: i32,
233    ) {
234    }
235
236    fn transform_changed(
237        &mut self,
238        _conn: &smithay_client_toolkit::reexports::client::Connection,
239        _qh: &QueueHandle<Self>,
240        _surface: &smithay_client_toolkit::reexports::client::protocol::wl_surface::WlSurface,
241        _new_transform: smithay_client_toolkit::reexports::client::protocol::wl_output::Transform,
242    ) {
243    }
244
245    fn frame(
246        &mut self,
247        _conn: &smithay_client_toolkit::reexports::client::Connection,
248        _qh: &QueueHandle<Self>,
249        _surface: &smithay_client_toolkit::reexports::client::protocol::wl_surface::WlSurface,
250        _time: u32,
251    ) {
252    }
253
254    fn surface_enter(
255        &mut self,
256        _conn: &Connection,
257        _qh: &QueueHandle<Self>,
258        _surface: &smithay_client_toolkit::reexports::client::protocol::wl_surface::WlSurface,
259        _output: &smithay_client_toolkit::reexports::client::protocol::wl_output::WlOutput,
260    ) {
261    }
262
263    fn surface_leave(
264        &mut self,
265        _conn: &Connection,
266        _qh: &QueueHandle<Self>,
267        _surface: &smithay_client_toolkit::reexports::client::protocol::wl_surface::WlSurface,
268        _output: &smithay_client_toolkit::reexports::client::protocol::wl_output::WlOutput,
269    ) {
270    }
271}
272
273impl OutputHandler for DimData {
274    fn output_state(&mut self) -> &mut OutputState {
275        &mut self.output_state
276    }
277
278    fn new_output(
279        &mut self,
280        _conn: &smithay_client_toolkit::reexports::client::Connection,
281        qh: &QueueHandle<Self>,
282        output: smithay_client_toolkit::reexports::client::protocol::wl_output::WlOutput,
283    ) {
284        self.views.push(self.create_view(qh, output));
285    }
286
287    fn update_output(
288        &mut self,
289        _conn: &smithay_client_toolkit::reexports::client::Connection,
290        qh: &QueueHandle<Self>,
291        output: smithay_client_toolkit::reexports::client::protocol::wl_output::WlOutput,
292    ) {
293        let new_view = self.create_view(qh, output);
294
295        if let Some(view) = self.views.iter_mut().find(|v| v.output == new_view.output) {
296            *view = new_view;
297        } else {
298            error!("Updating output not in views list??");
299        }
300    }
301
302    fn output_destroyed(
303        &mut self,
304        _conn: &smithay_client_toolkit::reexports::client::Connection,
305        _qh: &QueueHandle<Self>,
306        output: smithay_client_toolkit::reexports::client::protocol::wl_output::WlOutput,
307    ) {
308        self.views.retain(|v| v.output != output);
309    }
310}
311
312impl SeatHandler for DimData {
313    fn seat_state(&mut self) -> &mut SeatState {
314        &mut self.seat_state
315    }
316
317    fn new_seat(
318        &mut self,
319        _conn: &smithay_client_toolkit::reexports::client::Connection,
320        _qh: &QueueHandle<Self>,
321        _seat: smithay_client_toolkit::reexports::client::protocol::wl_seat::WlSeat,
322    ) {
323    }
324
325    fn new_capability(
326        &mut self,
327        _conn: &smithay_client_toolkit::reexports::client::Connection,
328        qh: &QueueHandle<Self>,
329        seat: smithay_client_toolkit::reexports::client::protocol::wl_seat::WlSeat,
330        capability: Capability,
331    ) {
332        match capability {
333            Capability::Keyboard => {
334                self.keyboard = Some(
335                    self.seat_state
336                        .get_keyboard(qh, &seat, None)
337                        .expect("Failed to get keyboard"),
338                )
339            }
340            Capability::Pointer => {
341                self.pointer = Some(
342                    self.seat_state
343                        .get_pointer(qh, &seat)
344                        .expect("Failed to get pointer"),
345                )
346            }
347            Capability::Touch => {
348                self.touch = Some(
349                    self.seat_state
350                        .get_touch(qh, &seat)
351                        .expect("Failed to get touch device!"),
352                )
353            }
354            _ => debug!("Unknown capability found: {capability}"),
355        }
356    }
357
358    fn remove_capability(
359        &mut self,
360        _conn: &smithay_client_toolkit::reexports::client::Connection,
361        _qh: &QueueHandle<Self>,
362        _seat: smithay_client_toolkit::reexports::client::protocol::wl_seat::WlSeat,
363        capability: Capability,
364    ) {
365        match capability {
366            Capability::Keyboard => self
367                .keyboard
368                .take()
369                .expect("Failed to remove keyboard!")
370                .release(),
371            Capability::Pointer => self
372                .pointer
373                .take()
374                .expect("Failed to remove pointer!")
375                .release(),
376            Capability::Touch => self
377                .touch
378                .take()
379                .expect("Failed to remove touch device!")
380                .release(),
381            _ => debug!("Unknown capability removed: {capability}"),
382        }
383    }
384
385    fn remove_seat(
386        &mut self,
387        _conn: &smithay_client_toolkit::reexports::client::Connection,
388        _qh: &QueueHandle<Self>,
389        _seat: smithay_client_toolkit::reexports::client::protocol::wl_seat::WlSeat,
390    ) {
391    }
392}
393
394impl KeyboardHandler for DimData {
395    fn enter(
396        &mut self,
397        _conn: &smithay_client_toolkit::reexports::client::Connection,
398        _qh: &QueueHandle<Self>,
399        _keyboard: &wl_keyboard::WlKeyboard,
400        _surface: &smithay_client_toolkit::reexports::client::protocol::wl_surface::WlSurface,
401        _serial: u32,
402        _raw: &[u32],
403        _keysyms: &[smithay_client_toolkit::seat::keyboard::Keysym],
404    ) {
405    }
406
407    fn leave(
408        &mut self,
409        _conn: &smithay_client_toolkit::reexports::client::Connection,
410        _qh: &QueueHandle<Self>,
411        _keyboard: &wl_keyboard::WlKeyboard,
412        _surface: &smithay_client_toolkit::reexports::client::protocol::wl_surface::WlSurface,
413        _serial: u32,
414    ) {
415    }
416
417    fn press_key(
418        &mut self,
419        _conn: &smithay_client_toolkit::reexports::client::Connection,
420        _qh: &QueueHandle<Self>,
421        _keyboard: &wl_keyboard::WlKeyboard,
422        _serial: u32,
423        _event: smithay_client_toolkit::seat::keyboard::KeyEvent,
424    ) {
425        debug!("Key pressed");
426        self.exit = true;
427    }
428
429    fn release_key(
430        &mut self,
431        _conn: &smithay_client_toolkit::reexports::client::Connection,
432        _qh: &QueueHandle<Self>,
433        _keyboard: &wl_keyboard::WlKeyboard,
434        _serial: u32,
435        _event: smithay_client_toolkit::seat::keyboard::KeyEvent,
436    ) {
437        debug!("Key released");
438    }
439
440    fn update_modifiers(
441        &mut self,
442        _conn: &smithay_client_toolkit::reexports::client::Connection,
443        _qh: &QueueHandle<Self>,
444        _keyboard: &wl_keyboard::WlKeyboard,
445        _serial: u32,
446        _modifiers: smithay_client_toolkit::seat::keyboard::Modifiers,
447        _layout: u32,
448    ) {
449        debug!("Modifiers updated");
450    }
451}
452
453impl PointerHandler for DimData {
454    fn pointer_frame(
455        &mut self,
456        _conn: &smithay_client_toolkit::reexports::client::Connection,
457        _qh: &QueueHandle<Self>,
458        pointer: &wl_pointer::WlPointer,
459        events: &[PointerEvent],
460    ) {
461        for e in events {
462            match e.kind {
463                PointerEventKind::Enter { serial } => {
464                    if self.alpha == 1.0 {
465                        pointer.set_cursor(serial, None, 0, 0);
466                    }
467                }
468                PointerEventKind::Leave { .. } => {}
469                _ => {
470                    debug!("Mouse event");
471                    self.exit = true;
472                }
473            }
474        }
475    }
476}
477
478impl TouchHandler for DimData {
479    fn down(
480        &mut self,
481        _conn: &Connection,
482        _qh: &QueueHandle<Self>,
483        _touch: &wl_touch::WlTouch,
484        _serial: u32,
485        _time: u32,
486        _surface: smithay_client_toolkit::reexports::client::protocol::wl_surface::WlSurface,
487        _id: i32,
488        _position: (f64, f64),
489    ) {
490        self.exit = true;
491    }
492
493    fn up(
494        &mut self,
495        _conn: &Connection,
496        _qh: &QueueHandle<Self>,
497        _touch: &wl_touch::WlTouch,
498        _serial: u32,
499        _time: u32,
500        _id: i32,
501    ) {
502    }
503
504    fn motion(
505        &mut self,
506        _conn: &Connection,
507        _qh: &QueueHandle<Self>,
508        _touch: &wl_touch::WlTouch,
509        _time: u32,
510        _id: i32,
511        _position: (f64, f64),
512    ) {
513    }
514
515    fn shape(
516        &mut self,
517        _conn: &Connection,
518        _qh: &QueueHandle<Self>,
519        _touch: &wl_touch::WlTouch,
520        _id: i32,
521        _major: f64,
522        _minor: f64,
523    ) {
524    }
525
526    fn orientation(
527        &mut self,
528        _conn: &Connection,
529        _qh: &QueueHandle<Self>,
530        _touch: &wl_touch::WlTouch,
531        _id: i32,
532        _orientation: f64,
533    ) {
534    }
535
536    fn cancel(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, _touch: &wl_touch::WlTouch) {}
537}
538
539delegate_compositor!(DimData);
540delegate_touch!(DimData);
541delegate_layer!(DimData);
542delegate_registry!(DimData);
543delegate_pointer!(DimData);
544delegate_keyboard!(DimData);
545delegate_output!(DimData);
546delegate_seat!(DimData);
547delegate_simple!(DimData, WpViewporter, 1);
548
549impl ProvidesRegistryState for DimData {
550    fn registry(&mut self) -> &mut RegistryState {
551        &mut self.registry_state
552    }
553
554    registry_handlers![OutputState, SeatState];
555}
556
557impl Dispatch<WpViewport, ()> for DimData {
558    fn event(
559        _: &mut Self,
560        _: &WpViewport,
561        _: wp_viewport::Event,
562        _: &(),
563        _: &Connection,
564        _: &QueueHandle<Self>,
565    ) {
566        unreachable!("wp_single_pixel_buffer_manager_v1::Event is empty in version 1")
567    }
568}
569
570impl Dispatch<WpSinglePixelBufferManagerV1, ()> for DimData {
571    fn event(
572        _: &mut Self,
573        _: &WpSinglePixelBufferManagerV1,
574        _: wp_single_pixel_buffer_manager_v1::Event,
575        _: &(),
576        _: &Connection,
577        _: &QueueHandle<Self>,
578    ) {
579        unreachable!("wp_single_pixel_buffer_manager_v1::Event is empty in version 1")
580    }
581}
582impl Dispatch<WlBuffer, ()> for DimData {
583    fn event(
584        _: &mut Self,
585        _: &WlBuffer,
586        event: wl_buffer::Event,
587        _: &(),
588        _: &Connection,
589        _: &QueueHandle<Self>,
590    ) {
591        match event {
592            wl_buffer::Event::Release => debug!("WlBuffer released"),
593            _ => unreachable!("WlBuffer only has Release event"),
594        }
595    }
596}
597
598impl Drop for DimView {
599    fn drop(&mut self) {
600        self.viewport.destroy();
601        self.buffer.destroy();
602    }
603}