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&mut DebugStruct<'_, '_>.debug_struct("OutputState")
119 .field("xdg", &self.xdg)
120 .field("outputs", &self.outputs)
121 .field(name:"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(name:"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: MutexGuard<'_, OutputInfo> = self.0.lock().unwrap();
737
738 let rv: bool = 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}
760