azul_winit/platform_impl/linux/x11/
monitor.rs1use std::os::raw::*;
2
3use parking_lot::Mutex;
4
5use super::{
6 ffi::{
7 RRCrtc, RRCrtcChangeNotifyMask, RRMode, RROutputPropertyNotifyMask,
8 RRScreenChangeNotifyMask, True, Window, XRRCrtcInfo, XRRScreenResources,
9 },
10 util, XConnection, XError,
11};
12use crate::{
13 dpi::{PhysicalPosition, PhysicalSize},
14 monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode},
15 platform_impl::{MonitorHandle as PlatformMonitorHandle, VideoMode as PlatformVideoMode},
16};
17
18const DISABLE_MONITOR_LIST_CACHING: bool = false;
20
21lazy_static! {
22 static ref MONITORS: Mutex<Option<Vec<MonitorHandle>>> = Mutex::default();
23}
24
25pub fn invalidate_cached_monitor_list() -> Option<Vec<MonitorHandle>> {
26 (*MONITORS.lock()).take()
28}
29
30#[derive(Debug, Clone, PartialEq, Eq, Hash)]
31pub struct VideoMode {
32 pub(crate) size: (u32, u32),
33 pub(crate) bit_depth: u16,
34 pub(crate) refresh_rate: u16,
35 pub(crate) native_mode: RRMode,
36 pub(crate) monitor: Option<MonitorHandle>,
37}
38
39impl VideoMode {
40 #[inline]
41 pub fn size(&self) -> PhysicalSize<u32> {
42 self.size.into()
43 }
44
45 #[inline]
46 pub fn bit_depth(&self) -> u16 {
47 self.bit_depth
48 }
49
50 #[inline]
51 pub fn refresh_rate(&self) -> u16 {
52 self.refresh_rate
53 }
54
55 #[inline]
56 pub fn monitor(&self) -> RootMonitorHandle {
57 RootMonitorHandle {
58 inner: PlatformMonitorHandle::X(self.monitor.clone().unwrap()),
59 }
60 }
61}
62
63#[derive(Debug, Clone)]
64pub struct MonitorHandle {
65 pub(crate) id: RRCrtc,
67 pub(crate) name: String,
69 dimensions: (u32, u32),
71 position: (i32, i32),
73 primary: bool,
75 pub(crate) scale_factor: f64,
77 pub(crate) rect: util::AaRect,
79 video_modes: Vec<VideoMode>,
81}
82
83impl PartialEq for MonitorHandle {
84 fn eq(&self, other: &Self) -> bool {
85 self.id == other.id
86 }
87}
88
89impl Eq for MonitorHandle {}
90
91impl PartialOrd for MonitorHandle {
92 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
93 Some(self.cmp(&other))
94 }
95}
96
97impl Ord for MonitorHandle {
98 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
99 self.id.cmp(&other.id)
100 }
101}
102
103impl std::hash::Hash for MonitorHandle {
104 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
105 self.id.hash(state);
106 }
107}
108
109impl MonitorHandle {
110 fn new(
111 xconn: &XConnection,
112 resources: *mut XRRScreenResources,
113 id: RRCrtc,
114 crtc: *mut XRRCrtcInfo,
115 primary: bool,
116 ) -> Option<Self> {
117 let (name, scale_factor, video_modes) = unsafe { xconn.get_output_info(resources, crtc)? };
118 let dimensions = unsafe { ((*crtc).width as u32, (*crtc).height as u32) };
119 let position = unsafe { ((*crtc).x as i32, (*crtc).y as i32) };
120 let rect = util::AaRect::new(position, dimensions);
121 Some(MonitorHandle {
122 id,
123 name,
124 scale_factor,
125 dimensions,
126 position,
127 primary,
128 rect,
129 video_modes,
130 })
131 }
132
133 pub fn dummy() -> Self {
134 MonitorHandle {
135 id: 0,
136 name: "<dummy monitor>".into(),
137 scale_factor: 1.0,
138 dimensions: (1, 1),
139 position: (0, 0),
140 primary: true,
141 rect: util::AaRect::new((0, 0), (1, 1)),
142 video_modes: Vec::new(),
143 }
144 }
145
146 pub(crate) fn is_dummy(&self) -> bool {
147 self.id == 0
149 }
150
151 pub fn name(&self) -> Option<String> {
152 Some(self.name.clone())
153 }
154
155 #[inline]
156 pub fn native_identifier(&self) -> u32 {
157 self.id as u32
158 }
159
160 pub fn size(&self) -> PhysicalSize<u32> {
161 self.dimensions.into()
162 }
163
164 pub fn position(&self) -> PhysicalPosition<i32> {
165 self.position.into()
166 }
167
168 #[inline]
169 pub fn scale_factor(&self) -> f64 {
170 self.scale_factor
171 }
172
173 #[inline]
174 pub fn video_modes(&self) -> impl Iterator<Item = RootVideoMode> {
175 let monitor = self.clone();
176 self.video_modes.clone().into_iter().map(move |mut x| {
177 x.monitor = Some(monitor.clone());
178 RootVideoMode {
179 video_mode: PlatformVideoMode::X(x),
180 }
181 })
182 }
183}
184
185impl XConnection {
186 pub fn get_monitor_for_window(&self, window_rect: Option<util::AaRect>) -> MonitorHandle {
187 let monitors = self.available_monitors();
188
189 if monitors.is_empty() {
190 return MonitorHandle::dummy();
192 }
193
194 let default = monitors.get(0).unwrap();
195
196 let window_rect = match window_rect {
197 Some(rect) => rect,
198 None => return default.to_owned(),
199 };
200
201 let mut largest_overlap = 0;
202 let mut matched_monitor = default;
203 for monitor in &monitors {
204 let overlapping_area = window_rect.get_overlapping_area(&monitor.rect);
205 if overlapping_area > largest_overlap {
206 largest_overlap = overlapping_area;
207 matched_monitor = &monitor;
208 }
209 }
210
211 matched_monitor.to_owned()
212 }
213
214 fn query_monitor_list(&self) -> Vec<MonitorHandle> {
215 unsafe {
216 let mut major = 0;
217 let mut minor = 0;
218 (self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor);
219
220 let root = (self.xlib.XDefaultRootWindow)(self.display);
221 let resources = if (major == 1 && minor >= 3) || major > 1 {
222 (self.xrandr.XRRGetScreenResourcesCurrent)(self.display, root)
223 } else {
224 (self.xrandr.XRRGetScreenResources)(self.display, root)
227 };
228
229 if resources.is_null() {
230 panic!("[winit] `XRRGetScreenResources` returned NULL. That should only happen if the root window doesn't exist.");
231 }
232
233 let mut available;
234 let mut has_primary = false;
235
236 let primary = (self.xrandr.XRRGetOutputPrimary)(self.display, root);
237 available = Vec::with_capacity((*resources).ncrtc as usize);
238 for crtc_index in 0..(*resources).ncrtc {
239 let crtc_id = *((*resources).crtcs.offset(crtc_index as isize));
240 let crtc = (self.xrandr.XRRGetCrtcInfo)(self.display, resources, crtc_id);
241 let is_active = (*crtc).width > 0 && (*crtc).height > 0 && (*crtc).noutput > 0;
242 if is_active {
243 let is_primary = *(*crtc).outputs.offset(0) == primary;
244 has_primary |= is_primary;
245 MonitorHandle::new(self, resources, crtc_id, crtc, is_primary)
246 .map(|monitor_id| available.push(monitor_id));
247 }
248 (self.xrandr.XRRFreeCrtcInfo)(crtc);
249 }
250
251 if !has_primary {
253 if let Some(ref mut fallback) = available.first_mut() {
254 fallback.primary = true;
256 }
257 }
258
259 (self.xrandr.XRRFreeScreenResources)(resources);
260 available
261 }
262 }
263
264 pub fn available_monitors(&self) -> Vec<MonitorHandle> {
265 let mut monitors_lock = MONITORS.lock();
266 (*monitors_lock)
267 .as_ref()
268 .cloned()
269 .or_else(|| {
270 let monitors = Some(self.query_monitor_list());
271 if !DISABLE_MONITOR_LIST_CACHING {
272 (*monitors_lock) = monitors.clone();
273 }
274 monitors
275 })
276 .unwrap()
277 }
278
279 #[inline]
280 pub fn primary_monitor(&self) -> MonitorHandle {
281 self.available_monitors()
282 .into_iter()
283 .find(|monitor| monitor.primary)
284 .unwrap_or_else(MonitorHandle::dummy)
285 }
286
287 pub fn select_xrandr_input(&self, root: Window) -> Result<c_int, XError> {
288 let has_xrandr = unsafe {
289 let mut major = 0;
290 let mut minor = 0;
291 (self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor)
292 };
293 assert!(
294 has_xrandr == True,
295 "[winit] XRandR extension not available."
296 );
297
298 let mut event_offset = 0;
299 let mut error_offset = 0;
300 let status = unsafe {
301 (self.xrandr.XRRQueryExtension)(self.display, &mut event_offset, &mut error_offset)
302 };
303
304 if status != True {
305 self.check_errors()?;
306 unreachable!("[winit] `XRRQueryExtension` failed but no error was received.");
307 }
308
309 let mask = RRCrtcChangeNotifyMask | RROutputPropertyNotifyMask | RRScreenChangeNotifyMask;
310 unsafe { (self.xrandr.XRRSelectInput)(self.display, root, mask) };
311
312 Ok(event_offset)
313 }
314}