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
24pub trait OutputHandler: Sized {
27 fn output_state(&mut self) -> &mut OutputState;
28
29 fn new_output(
31 &mut self,
32 conn: &Connection,
33 qh: &QueueHandle<Self>,
34 output: wl_output::WlOutput,
35 );
36
37 fn update_output(
39 &mut self,
40 conn: &Connection,
41 qh: &QueueHandle<Self>,
42 output: wl_output::WlOutput,
43 );
44
45 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
59pub 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 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 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 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 pub fn scale_factor(&self) -> i32 {
247 let guard = self.0.lock().unwrap();
248
249 guard.scale_factor
250 }
251
252 pub fn transform(&self) -> wl_output::Transform {
254 let guard = self.0.lock().unwrap();
255
256 guard.transform
257 }
258
259 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 pub dimensions: (i32, i32),
274
275 pub refresh_rate: i32,
282
283 pub current: bool,
288
289 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 self.refresh_rate / 1000,
310 self.refresh_rate % 1000
311 )
312 }
313}
314
315#[derive(Debug, Clone)]
317#[non_exhaustive]
318pub struct OutputInfo {
319 pub id: u32,
323
324 pub model: String,
326
327 pub make: String,
329
330 pub location: (i32, i32),
335
336 pub physical_size: (i32, i32),
341
342 pub subpixel: Subpixel,
344
345 pub transform: Transform,
350
351 pub scale_factor: i32,
361
362 pub modes: Vec<Mode>,
364
365 pub logical_position: Option<(i32, i32)>,
367
368 pub logical_size: Option<(i32, i32)>,
370
371 pub name: Option<String>,
381
382 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 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 for mode in &mut inner.pending_info.modes {
465 if preferred {
467 mode.preferred = false;
468 }
469
470 if current {
472 mode.current = false;
473 }
474 }
475
476 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 let run_callbacks = self.set(info);
509
510 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 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 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 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 name: u32,
736 wl_output: wl_output::WlOutput,
737 xdg_output: Option<zxdg_output_v1::ZxdgOutputV1>,
738 just_created: bool,
740
741 current_info: Option<OutputInfo>,
742 pending_info: OutputInfo,
743 pending_wl: bool,
744 pending_xdg: bool,
745}