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