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
23pub trait OutputHandler: Sized {
26 fn output_state(&mut self) -> &mut OutputState;
27
28 fn new_output(
30 &mut self,
31 conn: &Connection,
32 qh: &QueueHandle<Self>,
33 output: wl_output::WlOutput,
34 );
35
36 fn update_output(
38 &mut self,
39 conn: &Connection,
40 qh: &QueueHandle<Self>,
41 output: wl_output::WlOutput,
42 );
43
44 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
58pub 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 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 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 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 pub fn scale_factor(&self) -> i32 {
246 let guard = self.0.lock().unwrap();
247
248 guard.scale_factor
249 }
250
251 pub fn transform(&self) -> wl_output::Transform {
253 let guard = self.0.lock().unwrap();
254
255 guard.transform
256 }
257
258 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 pub dimensions: (i32, i32),
273
274 pub refresh_rate: i32,
281
282 pub current: bool,
287
288 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 self.refresh_rate / 1000,
309 self.refresh_rate % 1000
310 )
311 }
312}
313
314#[derive(Debug, Clone)]
316#[non_exhaustive]
317pub struct OutputInfo {
318 pub id: u32,
322
323 pub model: String,
325
326 pub make: String,
328
329 pub location: (i32, i32),
334
335 pub physical_size: (i32, i32),
340
341 pub subpixel: Subpixel,
343
344 pub transform: Transform,
349
350 pub scale_factor: i32,
360
361 pub modes: Vec<Mode>,
363
364 pub logical_position: Option<(i32, i32)>,
366
367 pub logical_size: Option<(i32, i32)>,
369
370 pub name: Option<String>,
380
381 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 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 for mode in &mut inner.pending_info.modes {
479 if preferred {
481 mode.preferred = false;
482 }
483
484 if current {
486 mode.current = false;
487 }
488 }
489
490 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 let run_callbacks = data.set(info);
523
524 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 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 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 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 name: u32,
750 wl_output: wl_output::WlOutput,
751 xdg_output: Option<zxdg_output_v1::ZxdgOutputV1>,
752 just_created: bool,
754
755 current_info: Option<OutputInfo>,
756 pending_info: OutputInfo,
757 pending_wl: bool,
758 pending_xdg: bool,
759}