1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
use anyhow::{anyhow, Result};
use smithay_client_toolkit::output::{OutputHandler, OutputInfo, OutputState};
use smithay_client_toolkit::reexports::client::globals::registry_queue_init;
use smithay_client_toolkit::reexports::client::protocol::wl_output;
use smithay_client_toolkit::reexports::client::{Connection, QueueHandle};
use smithay_client_toolkit::registry::{ProvidesRegistryState, RegistryState};
use smithay_client_toolkit::{delegate_output, delegate_registry, registry_handlers};
use xcb::XidNew;

use crate::DisplayInfo;

impl From<&OutputInfo> for DisplayInfo {
    fn from(info: &OutputInfo) -> Self {
        let scale_factor = info.scale_factor as f32;
        let rotation = match info.transform {
            wl_output::Transform::_90 | wl_output::Transform::Flipped90 => 90.,
            wl_output::Transform::_180 | wl_output::Transform::Flipped180 => 180.,
            wl_output::Transform::_270 | wl_output::Transform::Flipped270 => 270.,
            _ => 0.,
        };
        let frequency = info
            .modes
            .iter()
            .find(|m| m.current || m.preferred)
            .map(|m| m.refresh_rate as f32 / 1000.0)
            .unwrap_or(0.);
        let (x, y) = info.logical_position.unwrap_or(info.location);
        let (w, h) = info.logical_size.unwrap_or(info.physical_size);
        DisplayInfo {
            id: info.id,
            name: info.name.clone().unwrap_or_default(),
            raw_handle: unsafe { xcb::randr::Output::new(info.id) },
            x: ((x as f32) / scale_factor) as i32,
            y: ((y as f32) / scale_factor) as i32,
            width: ((w as f32) / scale_factor) as u32,
            height: ((h as f32) / scale_factor) as u32,
            rotation,
            scale_factor,
            frequency,
            is_primary: false,
        }
    }
}

/// Application data.
struct ListOutputs {
    registry_state: RegistryState,
    output_state: OutputState,
}

impl OutputHandler for ListOutputs {
    fn output_state(&mut self) -> &mut OutputState {
        &mut self.output_state
    }

    fn new_output(
        &mut self,
        _conn: &Connection,
        _qh: &QueueHandle<Self>,
        _output: wl_output::WlOutput,
    ) {
    }

    fn update_output(
        &mut self,
        _conn: &Connection,
        _qh: &QueueHandle<Self>,
        _output: wl_output::WlOutput,
    ) {
    }

    fn output_destroyed(
        &mut self,
        _conn: &Connection,
        _qh: &QueueHandle<Self>,
        _output: wl_output::WlOutput,
    ) {
    }
}

delegate_output!(ListOutputs);
delegate_registry!(ListOutputs);

impl ProvidesRegistryState for ListOutputs {
    fn registry(&mut self) -> &mut RegistryState {
        &mut self.registry_state
    }

    registry_handlers! {
        OutputState,
    }
}

pub fn get_all() -> Result<Vec<DisplayInfo>> {
    let conn = Connection::connect_to_env()?;

    let (globals, mut event_queue) = registry_queue_init(&conn).unwrap();
    let qh = event_queue.handle();

    let registry_state = RegistryState::new(&globals);

    let output_delegate = OutputState::new(&globals, &qh);

    let mut list_outputs = ListOutputs {
        registry_state,
        output_state: output_delegate,
    };

    event_queue.roundtrip(&mut list_outputs)?;

    list_outputs
        .output_state
        .outputs()
        .map(|output| {
            list_outputs
                .output_state
                .info(&output)
                .map(|o| DisplayInfo::from(&o))
                .ok_or(anyhow!("Cannot get info from Output in Wayland"))
        })
        .collect::<Result<Vec<DisplayInfo>>>()
}

pub fn get_from_point(x: i32, y: i32) -> Result<DisplayInfo> {
    let display_infos = get_all()?;

    display_infos
        .iter()
        .find(|&d| {
            x >= d.x
                && x - (d.width as i32) < d.x + d.width as i32
                && y >= d.y
                && y - (d.height as i32) < d.y + d.height as i32
        })
        .cloned()
        .ok_or_else(|| anyhow!("Get display info failed"))
}