dim_screen/
dim.rs

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