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