azalea_core/
monitor.rs

1use std::collections::HashMap;
2
3use gtk::{
4    gdk::{
5        self,
6        prelude::{DisplayExt, MonitorExt},
7    },
8    gio::prelude::ListModelExt,
9    glib::object::CastNone,
10};
11
12/// Monitor connector, used to identify a monitor
13pub type Id = String;
14
15/// Used to find a specific monitor
16#[derive(Default, clap::Parser, serde::Serialize, serde::Deserialize, Debug, Clone)]
17pub struct MonitorMatch {
18    connector: Option<Id>,
19    manufacturer: Option<String>,
20    model: Option<String>,
21}
22
23impl From<gdk::Monitor> for MonitorMatch {
24    fn from(value: gdk::Monitor) -> Self {
25        Self {
26            connector: value.connector().map(|v| v.to_string()),
27            manufacturer: value.manufacturer().map(|v| v.to_string()),
28            model: value.model().map(|v| v.to_string()),
29        }
30    }
31}
32
33#[derive(Default, serde::Serialize, serde::Deserialize, Debug, Clone)]
34pub enum Monitor {
35    /// Monitor is determined on-the-fly (good for popups)
36    Dynamic,
37
38    /// Stay attached to a single monitor
39    Single(MonitorMatch),
40
41    /// Create multiple instances, each attached to a monitor
42    Multi(Vec<MonitorMatch>),
43
44    /// Create an instance for each monitor (good for taskbars)
45    #[default]
46    All,
47}
48
49pub fn all() -> Vec<gdk::Monitor> {
50    let monitors = gdk::Display::default().unwrap().monitors();
51
52    let mut output_monitors = vec![];
53
54    for i in 0..monitors.n_items() {
55        let Some(monitor): Option<gdk::Monitor> = monitors.item(i).and_downcast() else {
56            continue;
57        };
58        output_monitors.push(monitor);
59    }
60
61    output_monitors
62}
63
64impl MonitorMatch {
65    pub fn find_matches(&self) -> Vec<gdk::Monitor> {
66        let monitors = gdk::Display::default().unwrap().monitors();
67
68        let mut matched_monitors = vec![];
69
70        for i in 0..monitors.n_items() {
71            let Some(monitor): Option<gdk::Monitor> = monitors.item(i).and_downcast() else {
72                continue;
73            };
74
75            if let Some(connector) = &self.connector {
76                if monitor
77                    .connector()
78                    .map(|v| v.to_string())
79                    .unwrap_or(format!(""))
80                    != *connector
81                {
82                    continue;
83                }
84            }
85
86            if let Some(manufacturer) = &self.manufacturer {
87                if monitor
88                    .manufacturer()
89                    .map(|v| v.to_string())
90                    .unwrap_or(format!(""))
91                    != *manufacturer
92                {
93                    continue;
94                }
95            }
96
97            if let Some(model) = &self.model {
98                if monitor
99                    .model()
100                    .map(|v| v.to_string())
101                    .unwrap_or(format!(""))
102                    != *model
103                {
104                    continue;
105                }
106            }
107
108            matched_monitors.push(monitor);
109        }
110
111        return matched_monitors;
112    }
113}
114
115pub fn monitors() -> Vec<gdk::Monitor> {
116    let monitors = gdk::Display::default().unwrap().monitors();
117    let mut output = vec![];
118
119    for i in 0..monitors.n_items() {
120        let Some(monitor): Option<gdk::Monitor> = monitors.item(i).and_downcast() else {
121            continue;
122        };
123        output.push(monitor);
124    }
125
126    output
127}
128
129pub fn monitors_to_string() -> String {
130    let output: Vec<HashMap<&str, String>> = monitors()
131        .iter()
132        .map(|monitor| {
133            HashMap::from([
134                (
135                    "connector",
136                    monitor
137                        .connector()
138                        .map(|v| v.to_string())
139                        .unwrap_or_default(),
140                ),
141                (
142                    "description",
143                    monitor
144                        .description()
145                        .map(|v| v.to_string())
146                        .unwrap_or_default(),
147                ),
148                (
149                    "manufacturer",
150                    monitor
151                        .manufacturer()
152                        .map(|v| v.to_string())
153                        .unwrap_or_default(),
154                ),
155                (
156                    "model",
157                    monitor.model().map(|v| v.to_string()).unwrap_or_default(),
158                ),
159            ])
160        })
161        .collect();
162
163    serde_json::to_string_pretty(&output).unwrap()
164}
165
166pub fn get_monitor(index: u32) -> Option<gdk::Monitor> {
167    gdk::Display::default().and_then(|display| display.monitors().item(index).and_downcast())
168}