Skip to main content

smithay_client_toolkit/
output.rs

1use std::{
2    any::Any,
3    fmt::{self, Display, Formatter},
4    sync::{Arc, Mutex, Weak},
5};
6
7use log::warn;
8use wayland_client::{
9    globals::GlobalList,
10    protocol::wl_output::{self, Subpixel, Transform},
11    Connection, Dispatch, Proxy, QueueHandle, WEnum,
12};
13use wayland_protocols::xdg::xdg_output::zv1::client::{
14    zxdg_output_manager_v1::{self, ZxdgOutputManagerV1},
15    zxdg_output_v1,
16};
17
18use crate::{
19    dispatch2::Dispatch2,
20    globals::GlobalData,
21    registry::{GlobalProxy, ProvidesRegistryState, RegistryHandler},
22};
23
24/// Simplified event handler for [`wl_output::WlOutput`].
25/// See [`OutputState`].
26pub trait OutputHandler: Sized {
27    fn output_state(&mut self) -> &mut OutputState;
28
29    /// A new output has been advertised.
30    fn new_output(
31        &mut self,
32        conn: &Connection,
33        qh: &QueueHandle<Self>,
34        output: wl_output::WlOutput,
35    );
36
37    /// An existing output has changed.
38    fn update_output(
39        &mut self,
40        conn: &Connection,
41        qh: &QueueHandle<Self>,
42        output: wl_output::WlOutput,
43    );
44
45    /// An output is no longer advertised.
46    ///
47    /// The info passed to this function was the state of the output before destruction.
48    fn output_destroyed(
49        &mut self,
50        conn: &Connection,
51        qh: &QueueHandle<Self>,
52        output: wl_output::WlOutput,
53    );
54}
55
56type ScaleWatcherFn =
57    dyn Fn(&mut dyn Any, &Connection, &dyn Any, &wl_output::WlOutput) + Send + Sync;
58
59/// A handler for delegating [`wl_output::WlOutput`].
60///
61/// When implementing [`ProvidesRegistryState`],
62/// [`registry_handlers!`](crate::registry_handlers) may be used to delegate all
63/// output events to an instance of this type. It will internally store the internal state of all
64/// outputs and allow querying them via the `OutputState::outputs` and `OutputState::info` methods.
65///
66/// ## Example
67///
68/// ```
69/// use smithay_client_toolkit::output::{OutputHandler,OutputState};
70/// use smithay_client_toolkit::registry::{ProvidesRegistryState,RegistryHandler};
71/// # use smithay_client_toolkit::registry::RegistryState;
72/// use smithay_client_toolkit::{registry_handlers, delegate_registry};
73/// use wayland_client::{Connection,QueueHandle,protocol::wl_output};
74///
75/// struct ExampleState {
76/// #    registry_state: RegistryState,
77///     // The state is usually kept as an attribute of the application state.
78///     output_state: OutputState,
79/// }
80///
81/// // When using `OutputState`, an implementation of `OutputHandler` should be supplied:
82/// impl OutputHandler for ExampleState {
83///      // Custom implementation handling output events here ...
84/// #    fn output_state(&mut self) -> &mut OutputState {
85/// #        &mut self.output_state
86/// #    }
87/// #
88/// #    fn new_output(&mut self, _: &Connection, _: &QueueHandle<Self>, _: wl_output::WlOutput) {
89/// #    }
90/// #
91/// #    fn update_output(&mut self, _: &Connection, _: &QueueHandle<Self>, _: wl_output::WlOutput) {
92/// #    }
93/// #
94/// #    fn output_destroyed(&mut self, _: &Connection, _: &QueueHandle<Self>, _: wl_output::WlOutput) {
95/// #    }
96/// }
97///
98/// // Delegating to the registry is required to use `OutputState`.
99/// delegate_registry!(ExampleState);
100/// smithay_client_toolkit::delegate_dispatch2!(ExampleState);
101///
102/// impl ProvidesRegistryState for ExampleState {
103/// #    fn registry(&mut self) -> &mut RegistryState {
104/// #        &mut self.registry_state
105/// #    }
106///     // ...
107///
108///     registry_handlers!(OutputState);
109/// }
110/// ```
111pub struct OutputState {
112    xdg: GlobalProxy<ZxdgOutputManagerV1>,
113    outputs: Vec<OutputInner>,
114    callbacks: Vec<Weak<ScaleWatcherFn>>,
115}
116
117impl fmt::Debug for OutputState {
118    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
119        fmt.debug_struct("OutputState")
120            .field("xdg", &self.xdg)
121            .field("outputs", &self.outputs)
122            .field("callbacks", &self.callbacks.len())
123            .finish()
124    }
125}
126
127pub struct ScaleWatcherHandle(Arc<ScaleWatcherFn>);
128
129impl fmt::Debug for ScaleWatcherHandle {
130    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
131        fmt.debug_struct("ScaleWatcherHandle").finish_non_exhaustive()
132    }
133}
134
135impl OutputState {
136    pub fn new<
137        D: Dispatch<wl_output::WlOutput, OutputData>
138            + Dispatch<zxdg_output_v1::ZxdgOutputV1, OutputData>
139            + Dispatch<zxdg_output_manager_v1::ZxdgOutputManagerV1, GlobalData>
140            + 'static,
141    >(
142        global_list: &GlobalList,
143        qh: &QueueHandle<D>,
144    ) -> OutputState {
145        let (outputs, xdg) = global_list.contents().with_list(|globals| {
146            let outputs: Vec<wl_output::WlOutput> = crate::registry::bind_all(
147                global_list.registry(),
148                globals,
149                qh,
150                1..=4,
151                OutputData::new,
152            )
153            .expect("Failed to bind global");
154            let xdg =
155                crate::registry::bind_one(global_list.registry(), globals, qh, 1..=3, GlobalData)
156                    .into();
157            (outputs, xdg)
158        });
159
160        let mut output_state = OutputState { xdg, outputs: vec![], callbacks: vec![] };
161        for wl_output in outputs {
162            output_state.setup(wl_output, qh);
163        }
164        output_state
165    }
166
167    /// Returns an iterator over all outputs.
168    pub fn outputs(&self) -> impl Iterator<Item = wl_output::WlOutput> {
169        self.outputs.iter().map(|output| &output.wl_output).cloned().collect::<Vec<_>>().into_iter()
170    }
171
172    /// Returns information about an output.
173    ///
174    /// This may be none if the output has been destroyed or the compositor has not sent information about the
175    /// output yet.
176    pub fn info(&self, output: &wl_output::WlOutput) -> Option<OutputInfo> {
177        self.outputs
178            .iter()
179            .find(|inner| &inner.wl_output == output)
180            .and_then(|inner| inner.current_info.clone())
181    }
182
183    pub fn add_scale_watcher<F, D>(data: &mut D, f: F) -> ScaleWatcherHandle
184    where
185        D: OutputHandler + 'static,
186        F: Fn(&mut D, &Connection, &QueueHandle<D>, &wl_output::WlOutput) + Send + Sync + 'static,
187    {
188        let state = data.output_state();
189        let rv = ScaleWatcherHandle(Arc::new(move |data, conn, qh, output| {
190            if let (Some(data), Some(qh)) = (data.downcast_mut(), qh.downcast_ref()) {
191                f(data, conn, qh, output);
192            }
193        }));
194        state.callbacks.retain(|f| f.upgrade().is_some());
195        state.callbacks.push(Arc::downgrade(&rv.0));
196        rv
197    }
198
199    fn setup<D>(&mut self, wl_output: wl_output::WlOutput, qh: &QueueHandle<D>)
200    where
201        D: Dispatch<zxdg_output_v1::ZxdgOutputV1, OutputData> + 'static,
202    {
203        let data = wl_output.data::<OutputData>().unwrap().clone();
204
205        let pending_info = data.0.lock().unwrap().clone();
206        let name = pending_info.id;
207
208        let version = wl_output.version();
209        let pending_xdg = self.xdg.get().is_ok();
210
211        let xdg_output = if pending_xdg {
212            let xdg = self.xdg.get().unwrap();
213
214            Some(xdg.get_xdg_output(&wl_output, qh, data))
215        } else {
216            None
217        };
218
219        let inner = OutputInner {
220            name,
221            wl_output,
222            xdg_output,
223            just_created: true,
224            // wl_output::done was added in version 2.
225            // If we have an output at version 1, assume the data was already sent.
226            current_info: if version > 1 { None } else { Some(OutputInfo::new(name)) },
227
228            pending_info,
229            pending_wl: true,
230            pending_xdg,
231        };
232
233        self.outputs.push(inner);
234    }
235}
236
237#[derive(Debug, Clone)]
238pub struct OutputData(Arc<Mutex<OutputInfo>>);
239
240impl OutputData {
241    pub fn new(name: u32) -> OutputData {
242        OutputData(Arc::new(Mutex::new(OutputInfo::new(name))))
243    }
244
245    /// Get the output scale factor.
246    pub fn scale_factor(&self) -> i32 {
247        let guard = self.0.lock().unwrap();
248
249        guard.scale_factor
250    }
251
252    /// Get the output transform.
253    pub fn transform(&self) -> wl_output::Transform {
254        let guard = self.0.lock().unwrap();
255
256        guard.transform
257    }
258
259    /// Access the underlying [`OutputInfo`].
260    ///
261    /// Reentrant calls within the `callback` will deadlock.
262    pub fn with_output_info<T, F: FnOnce(&OutputInfo) -> T>(&self, callback: F) -> T {
263        let guard = self.0.lock().unwrap();
264        callback(&guard)
265    }
266}
267
268#[derive(Debug, Clone)]
269pub struct Mode {
270    /// Number of pixels of this mode in format `(width, height)`
271    ///
272    /// for example `(1920, 1080)`
273    pub dimensions: (i32, i32),
274
275    /// Refresh rate for this mode.
276    ///
277    /// The refresh rate is specified in terms of millihertz (mHz). To convert approximately to Hertz,
278    /// divide the value by 1000.
279    ///
280    /// This value could be zero if an output has no correct refresh rate, such as a virtual output.
281    pub refresh_rate: i32,
282
283    /// Whether this is the current mode for this output.
284    ///
285    /// Per the Wayland protocol, non-current modes are deprecated and clients should not rely on deprecated
286    /// modes.
287    pub current: bool,
288
289    /// Whether this is the preferred mode for this output.
290    pub preferred: bool,
291}
292
293impl Display for Mode {
294    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
295        if self.current {
296            write!(f, "(current) ")?;
297        }
298
299        if self.preferred {
300            write!(f, "(preferred) ")?;
301        }
302
303        write!(
304            f,
305            "{}×{}px @ {}.{:03} Hz",
306            self.dimensions.0,
307            self.dimensions.1,
308            // Print the refresh rate in hertz since it is more familiar unit.
309            self.refresh_rate / 1000,
310            self.refresh_rate % 1000
311        )
312    }
313}
314
315/// Information about an output.
316#[derive(Debug, Clone)]
317#[non_exhaustive]
318pub struct OutputInfo {
319    /// The id of the output.
320    ///
321    /// This corresponds to the global `name` of the wl_output.
322    pub id: u32,
323
324    /// The model name of this output as advertised by the server.
325    pub model: String,
326
327    /// The make name of this output as advertised by the server.
328    pub make: String,
329
330    /// Location of the top-left corner of this output in compositor space.
331    ///
332    /// Note that the compositor may decide to always report (0,0) if it decides clients are not allowed to
333    /// know this information.
334    pub location: (i32, i32),
335
336    /// Physical dimensions of this output, in millimeters.
337    ///
338    /// This value may be set to (0, 0) if a physical size does not make sense for the output (e.g. projectors
339    /// and virtual outputs).
340    pub physical_size: (i32, i32),
341
342    /// The subpixel layout for this output.
343    pub subpixel: Subpixel,
344
345    /// The current transformation applied to this output
346    ///
347    /// You can pre-render your buffers taking this information into account and advertising it via
348    /// `wl_buffer.set_transform` for better performance.
349    pub transform: Transform,
350
351    /// The scaling factor of this output
352    ///
353    /// Any buffer whose scaling factor does not match the one of the output it is displayed on will be
354    /// rescaled accordingly.
355    ///
356    /// For example, a buffer of scaling factor 1 will be doubled in size if the output scaling factor is 2.
357    ///
358    /// You can pre-render your buffers taking this information into account and advertising it via
359    /// `wl_surface.set_buffer_scale` so you may advertise a higher detail image.
360    pub scale_factor: i32,
361
362    /// Possible modes for an output.
363    pub modes: Vec<Mode>,
364
365    /// Logical position in global compositor space
366    pub logical_position: Option<(i32, i32)>,
367
368    /// Logical size in global compositor space
369    pub logical_size: Option<(i32, i32)>,
370
371    /// The name of the this output as advertised by the surface.
372    ///
373    /// Examples of names include 'HDMI-A-1', 'WL-1', 'X11-1', etc. However, do not assume that the name is a
374    /// reflection of an underlying DRM connector, X11 connection, etc.
375    ///
376    /// Compositors are not required to provide a name for the output and the value may be [`None`].
377    ///
378    /// The name will be [`None`] if the compositor does not support version 4 of the wl-output protocol or
379    /// version 2 of the zxdg-output-v1 protocol.
380    pub name: Option<String>,
381
382    /// The description of this output as advertised by the server
383    ///
384    /// The description is a UTF-8 string with no convention defined for its contents. The description is not
385    /// guaranteed to be unique among all wl_output globals. Examples might include 'Foocorp 11" Display' or
386    /// 'Virtual X11 output via :1'.
387    ///
388    /// Compositors are not required to provide a description of the output and the value may be [`None`].
389    ///
390    /// The value will be [`None`] if the compositor does not support version 4 of the wl-output
391    /// protocol, version 2 of the zxdg-output-v1 protocol.
392    pub description: Option<String>,
393}
394
395impl<D> Dispatch2<wl_output::WlOutput, D> for OutputData
396where
397    D: OutputHandler + 'static,
398{
399    fn event(
400        &self,
401        state: &mut D,
402        output: &wl_output::WlOutput,
403        event: wl_output::Event,
404        conn: &Connection,
405        qh: &QueueHandle<D>,
406    ) {
407        let inner = match state
408            .output_state()
409            .outputs
410            .iter_mut()
411            .find(|inner| &inner.wl_output == output)
412        {
413            Some(inner) => inner,
414            None => {
415                warn!("Received {event:?} for dead wl_output");
416                return;
417            }
418        };
419
420        match event {
421            wl_output::Event::Geometry {
422                x,
423                y,
424                physical_width,
425                physical_height,
426                subpixel,
427                make,
428                model,
429                transform,
430            } => {
431                inner.pending_info.location = (x, y);
432                inner.pending_info.physical_size = (physical_width, physical_height);
433                inner.pending_info.subpixel = match subpixel {
434                    WEnum::Value(subpixel) => subpixel,
435                    WEnum::Unknown(_) => todo!("Warn about invalid subpixel value"),
436                };
437                inner.pending_info.make = make;
438                inner.pending_info.model = model;
439                inner.pending_info.transform = match transform {
440                    WEnum::Value(subpixel) => subpixel,
441                    WEnum::Unknown(_) => todo!("Warn about invalid transform value"),
442                };
443                inner.pending_wl = true;
444            }
445
446            wl_output::Event::Mode { flags, width, height, refresh } => {
447                // Remove the old mode
448                inner.pending_info.modes.retain(|mode| {
449                    mode.dimensions != (width, height) || mode.refresh_rate != refresh
450                });
451
452                let flags = match flags {
453                    WEnum::Value(flags) => flags,
454                    WEnum::Unknown(_) => panic!("Invalid flags"),
455                };
456
457                let current = flags.contains(wl_output::Mode::Current);
458                let preferred = flags.contains(wl_output::Mode::Preferred);
459
460                // Any mode that isn't current is deprecated, let's deprecate any existing modes that may be
461                // marked as current.
462                //
463                // If a new mode is advertised as preferred, then mark the existing preferred mode as not.
464                for mode in &mut inner.pending_info.modes {
465                    // This mode is no longer preferred.
466                    if preferred {
467                        mode.preferred = false;
468                    }
469
470                    // This mode is no longer current.
471                    if current {
472                        mode.current = false;
473                    }
474                }
475
476                // Now create the new mode.
477                inner.pending_info.modes.push(Mode {
478                    dimensions: (width, height),
479                    refresh_rate: refresh,
480                    current,
481                    preferred,
482                });
483
484                inner.pending_wl = true;
485            }
486
487            wl_output::Event::Scale { factor } => {
488                inner.pending_info.scale_factor = factor;
489                inner.pending_wl = true;
490            }
491
492            wl_output::Event::Name { name } => {
493                inner.pending_info.name = Some(name);
494                inner.pending_wl = true;
495            }
496
497            wl_output::Event::Description { description } => {
498                inner.pending_info.description = Some(description);
499                inner.pending_wl = true;
500            }
501
502            wl_output::Event::Done => {
503                let info = inner.pending_info.clone();
504                inner.current_info = Some(info.clone());
505                inner.pending_wl = false;
506
507                // Set the user data, see if we need to run scale callbacks
508                let run_callbacks = self.set(info);
509
510                // Don't call `new_output` until we have xdg output info
511                if !inner.pending_xdg {
512                    if inner.just_created {
513                        inner.just_created = false;
514                        state.new_output(conn, qh, output.clone());
515                    } else {
516                        state.update_output(conn, qh, output.clone());
517                    }
518                }
519
520                if run_callbacks {
521                    let callbacks = state.output_state().callbacks.clone();
522                    for cb in callbacks {
523                        if let Some(cb) = cb.upgrade() {
524                            cb(state, conn, qh, output);
525                        }
526                    }
527                }
528            }
529            _ => unreachable!(),
530        }
531    }
532}
533
534impl<D> Dispatch2<zxdg_output_manager_v1::ZxdgOutputManagerV1, D> for GlobalData
535where
536    D: OutputHandler,
537{
538    fn event(
539        &self,
540        _: &mut D,
541        _: &zxdg_output_manager_v1::ZxdgOutputManagerV1,
542        _: zxdg_output_manager_v1::Event,
543        _: &Connection,
544        _: &QueueHandle<D>,
545    ) {
546        unreachable!("zxdg_output_manager_v1 has no events")
547    }
548}
549
550impl<D> Dispatch2<zxdg_output_v1::ZxdgOutputV1, D> for OutputData
551where
552    D: OutputHandler,
553{
554    fn event(
555        &self,
556        state: &mut D,
557        output: &zxdg_output_v1::ZxdgOutputV1,
558        event: zxdg_output_v1::Event,
559        conn: &Connection,
560        qh: &QueueHandle<D>,
561    ) {
562        let inner = match state
563            .output_state()
564            .outputs
565            .iter_mut()
566            .find(|inner| inner.xdg_output.as_ref() == Some(output))
567        {
568            Some(inner) => inner,
569            None => {
570                warn!("Received {event:?} for dead xdg_output");
571                return;
572            }
573        };
574
575        // zxdg_output_v1::done is deprecated in version 3. So we only need
576        // to wait for wl_output::done, once we get any xdg output info.
577        if output.version() >= 3 {
578            inner.pending_xdg = false;
579        }
580
581        match event {
582            zxdg_output_v1::Event::LogicalPosition { x, y } => {
583                inner.pending_info.logical_position = Some((x, y));
584                if output.version() < 3 {
585                    inner.pending_xdg = true;
586                }
587            }
588            zxdg_output_v1::Event::LogicalSize { width, height } => {
589                inner.pending_info.logical_size = Some((width, height));
590                if output.version() < 3 {
591                    inner.pending_xdg = true;
592                }
593            }
594            zxdg_output_v1::Event::Name { name } => {
595                if inner.wl_output.version() < 4 {
596                    inner.pending_info.name = Some(name);
597                }
598                if output.version() < 3 {
599                    inner.pending_xdg = true;
600                }
601            }
602
603            zxdg_output_v1::Event::Description { description } => {
604                if inner.wl_output.version() < 4 {
605                    inner.pending_info.description = Some(description);
606                }
607                if output.version() < 3 {
608                    inner.pending_xdg = true;
609                }
610            }
611
612            zxdg_output_v1::Event::Done => {
613                // This event is deprecated starting in version 3, wl_output::done should be sent instead.
614                if output.version() < 3 {
615                    let info = inner.pending_info.clone();
616                    inner.current_info = Some(info.clone());
617                    inner.pending_xdg = false;
618
619                    // Set the user data
620                    self.set(info);
621
622                    let pending_wl = inner.pending_wl;
623                    let just_created = inner.just_created;
624                    let output = inner.wl_output.clone();
625
626                    if just_created {
627                        inner.just_created = false;
628                    }
629
630                    if !pending_wl {
631                        if just_created {
632                            state.new_output(conn, qh, output);
633                        } else {
634                            state.update_output(conn, qh, output);
635                        }
636                    }
637                }
638            }
639
640            _ => unreachable!(),
641        }
642    }
643}
644
645impl<D> RegistryHandler<D> for OutputState
646where
647    D: Dispatch<wl_output::WlOutput, OutputData>
648        + Dispatch<zxdg_output_v1::ZxdgOutputV1, OutputData>
649        + Dispatch<zxdg_output_manager_v1::ZxdgOutputManagerV1, GlobalData>
650        + OutputHandler
651        + ProvidesRegistryState
652        + 'static,
653{
654    fn new_global(
655        data: &mut D,
656        _: &Connection,
657        qh: &QueueHandle<D>,
658        name: u32,
659        interface: &str,
660        _version: u32,
661    ) {
662        if interface == "wl_output" {
663            let output = data
664                .registry()
665                .bind_specific(qh, name, 1..=4, OutputData::new(name))
666                .expect("Failed to bind global");
667            data.output_state().setup(output, qh);
668        }
669    }
670
671    fn remove_global(
672        data: &mut D,
673        conn: &Connection,
674        qh: &QueueHandle<D>,
675        name: u32,
676        interface: &str,
677    ) {
678        if interface == "wl_output" {
679            let output = data
680                .output_state()
681                .outputs
682                .iter()
683                .position(|o| o.name == name)
684                .expect("Removed non-existing output");
685
686            let wl_output = data.output_state().outputs[output].wl_output.clone();
687            data.output_destroyed(conn, qh, wl_output);
688
689            let output = data.output_state().outputs.remove(output);
690            if let Some(xdg_output) = &output.xdg_output {
691                xdg_output.destroy();
692            }
693            if output.wl_output.version() >= 3 {
694                output.wl_output.release();
695            }
696        }
697    }
698}
699
700impl OutputInfo {
701    fn new(id: u32) -> OutputInfo {
702        OutputInfo {
703            id,
704            model: String::new(),
705            make: String::new(),
706            location: (0, 0),
707            physical_size: (0, 0),
708            subpixel: Subpixel::Unknown,
709            transform: Transform::Normal,
710            scale_factor: 1,
711            modes: vec![],
712            logical_position: None,
713            logical_size: None,
714            name: None,
715            description: None,
716        }
717    }
718}
719
720impl OutputData {
721    pub(crate) fn set(&self, info: OutputInfo) -> bool {
722        let mut guard = self.0.lock().unwrap();
723
724        let rv = guard.scale_factor != info.scale_factor;
725
726        *guard = info;
727
728        rv
729    }
730}
731
732#[derive(Debug)]
733struct OutputInner {
734    /// The name of the wl_output global.
735    name: u32,
736    wl_output: wl_output::WlOutput,
737    xdg_output: Option<zxdg_output_v1::ZxdgOutputV1>,
738    /// Whether this output was just created and has not an event yet.
739    just_created: bool,
740
741    current_info: Option<OutputInfo>,
742    pending_info: OutputInfo,
743    pending_wl: bool,
744    pending_xdg: bool,
745}