core_graphics2/
display.rs

1use std::ptr::null;
2
3use core_foundation::{array::CFArray, base::TCFType, string::CFString};
4use libc::c_double;
5
6#[cfg(feature = "window")]
7use crate::window::CGWindowID;
8use crate::{
9    color_space::CGColorSpace,
10    context::CGContext,
11    error::CGError,
12    geometry::{CGPoint, CGRect, CGSize},
13    image::CGImage,
14};
15pub use crate::{direct_display::*, display_configuration::*, display_fade::*, remote_operation::*};
16
17// IOKit/graphics/IOGraphicsTypes.h
18pub const IO1BitIndexedPixels: &str = "P";
19pub const IO2BitIndexedPixels: &str = "PP";
20pub const IO4BitIndexedPixels: &str = "PPPP";
21pub const IO8BitIndexedPixels: &str = "PPPPPPPP";
22pub const IO16BitDirectPixels: &str = "-RRRRRGGGGGBBBBB";
23pub const IO32BitDirectPixels: &str = "--------RRRRRRRRGGGGGGGGBBBBBBBB";
24pub const kIO30BitDirectPixels: &str = "--RRRRRRRRRRGGGGGGGGGGBBBBBBBBBB";
25pub const kIO64BitDirectPixels: &str = "-16R16G16B16";
26pub const kIO16BitFloatPixels: &str = "-16FR16FG16FB16";
27pub const kIO32BitFloatPixels: &str = "-32FR32FG32FB32";
28pub const IOYUV422Pixels: &str = "Y4U2V2";
29pub const IO8BitOverlayPixels: &str = "O8";
30
31pub struct CGDisplayMode(CGDisplayModeRef);
32
33impl Drop for CGDisplayMode {
34    fn drop(&mut self) {
35        unsafe { CGDisplayModeRelease(self.0) }
36    }
37}
38
39impl_TCFType!(CGDisplayMode, CGDisplayModeRef, CGDisplayModeGetTypeID);
40impl_CFTypeDescription!(CGDisplayMode);
41
42impl CGDisplayMode {
43    pub fn width(&self) -> usize {
44        unsafe { CGDisplayModeGetWidth(self.as_concrete_TypeRef()) as usize }
45    }
46
47    pub fn height(&self) -> usize {
48        unsafe { CGDisplayModeGetHeight(self.as_concrete_TypeRef()) as usize }
49    }
50
51    pub fn pixel_width(&self) -> usize {
52        unsafe { CGDisplayModeGetPixelWidth(self.as_concrete_TypeRef()) as usize }
53    }
54
55    pub fn pixel_height(&self) -> usize {
56        unsafe { CGDisplayModeGetPixelHeight(self.as_concrete_TypeRef()) as usize }
57    }
58
59    pub fn copy_pixel_encoding(&self) -> Option<CFString> {
60        unsafe {
61            let encoding = CGDisplayModeCopyPixelEncoding(self.as_concrete_TypeRef());
62            if encoding.is_null() {
63                None
64            } else {
65                Some(TCFType::wrap_under_create_rule(encoding))
66            }
67        }
68    }
69
70    pub fn bit_depth(&self) -> usize {
71        match self.copy_pixel_encoding() {
72            None => 0,
73            Some(encoding) => match encoding.to_string().as_str() {
74                kIO32BitFloatPixels => 96,
75                kIO64BitDirectPixels => 64,
76                kIO16BitFloatPixels => 48,
77                kIO30BitDirectPixels => 30,
78                IO32BitDirectPixels => 32,
79                IO16BitDirectPixels => 16,
80                IOYUV422Pixels => 16,
81                IO8BitOverlayPixels => 8,
82                IO8BitIndexedPixels => 8,
83                IO4BitIndexedPixels => 4,
84                IO2BitIndexedPixels => 2,
85                IO1BitIndexedPixels => 1,
86                _ => 0,
87            },
88        }
89    }
90
91    pub fn refresh_rate(&self) -> f64 {
92        unsafe { CGDisplayModeGetRefreshRate(self.as_concrete_TypeRef()) }
93    }
94
95    pub fn io_flags(&self) -> u32 {
96        unsafe { CGDisplayModeGetIOFlags(self.as_concrete_TypeRef()) }
97    }
98
99    pub fn io_display_mode_id(&self) -> i32 {
100        unsafe { CGDisplayModeGetIODisplayModeID(self.as_concrete_TypeRef()) }
101    }
102
103    pub fn is_usable_for_desktop_gui(&self) -> bool {
104        unsafe { CGDisplayModeIsUsableForDesktopGUI(self.as_concrete_TypeRef()) }
105    }
106}
107
108#[derive(Clone, Copy, Debug, Eq, PartialEq)]
109pub struct CGDisplay {
110    pub id: CGDirectDisplayID,
111}
112
113impl CGDisplay {
114    pub fn new(id: CGDirectDisplayID) -> Self {
115        CGDisplay {
116            id,
117        }
118    }
119
120    pub fn main() -> Self {
121        unsafe { CGDisplay::new(CGMainDisplayID()) }
122    }
123
124    pub fn copy_display_modes(&self) -> Option<CFArray<CGDisplayMode>> {
125        unsafe {
126            let modes = CGDisplayCopyAllDisplayModes(self.id, null());
127            if modes.is_null() {
128                None
129            } else {
130                Some(TCFType::wrap_under_create_rule(modes))
131            }
132        }
133    }
134
135    pub fn copy_display_mode(&self) -> Option<CGDisplayMode> {
136        unsafe {
137            let mode = CGDisplayCopyDisplayMode(self.id);
138            if mode.is_null() {
139                None
140            } else {
141                Some(TCFType::wrap_under_create_rule(mode))
142            }
143        }
144    }
145
146    pub fn bounds(&self) -> CGRect {
147        unsafe { CGDisplayBounds(self.id) }
148    }
149
150    pub fn pixels_wide(&self) -> usize {
151        unsafe { CGDisplayPixelsWide(self.id) as usize }
152    }
153
154    pub fn pixels_high(&self) -> usize {
155        unsafe { CGDisplayPixelsHigh(self.id) as usize }
156    }
157
158    pub fn set_display_mode(&self, mode: &CGDisplayMode) -> Result<(), CGError> {
159        let result = unsafe { CGDisplaySetDisplayMode(self.id, mode.as_concrete_TypeRef(), null()) };
160        if result == CGError::Success {
161            Ok(())
162        } else {
163            Err(result)
164        }
165    }
166
167    pub fn capture(&self) -> Result<(), CGError> {
168        let result = unsafe { CGDisplayCapture(self.id) };
169        if result == CGError::Success {
170            Ok(())
171        } else {
172            Err(result)
173        }
174    }
175
176    pub fn capture_with_options(&self, options: CGCaptureOptions) -> Result<(), CGError> {
177        let result = unsafe { CGDisplayCaptureWithOptions(self.id, options) };
178        if result == CGError::Success {
179            Ok(())
180        } else {
181            Err(result)
182        }
183    }
184
185    #[cfg(feature = "window")]
186    pub fn shielding_window_id(&self) -> CGWindowID {
187        unsafe { CGShieldingWindowID(self.id) }
188    }
189
190    pub fn new_image(&self) -> Option<CGImage> {
191        unsafe {
192            let image = CGDisplayCreateImage(self.id);
193            if image.is_null() {
194                None
195            } else {
196                Some(TCFType::wrap_under_create_rule(image))
197            }
198        }
199    }
200
201    pub fn new_image_for_rect(&self, rect: CGRect) -> Option<CGImage> {
202        unsafe {
203            let image = CGDisplayCreateImageForRect(self.id, rect);
204            if image.is_null() {
205                None
206            } else {
207                Some(TCFType::wrap_under_create_rule(image))
208            }
209        }
210    }
211
212    pub fn hide_cursor(&self) -> Result<(), CGError> {
213        let result = unsafe { CGDisplayHideCursor(self.id) };
214        if result == CGError::Success {
215            Ok(())
216        } else {
217            Err(result)
218        }
219    }
220
221    pub fn show_cursor(&self) -> Result<(), CGError> {
222        let result = unsafe { CGDisplayShowCursor(self.id) };
223        if result == CGError::Success {
224            Ok(())
225        } else {
226            Err(result)
227        }
228    }
229
230    pub fn move_cursor_to_point(&self, point: CGPoint) -> Result<(), CGError> {
231        let result = unsafe { CGDisplayMoveCursorToPoint(self.id, point) };
232        if result == CGError::Success {
233            Ok(())
234        } else {
235            Err(result)
236        }
237    }
238
239    pub fn warp_mouse_cursor_position(&self, point: CGPoint) -> Result<(), CGError> {
240        let result = unsafe { CGWarpMouseCursorPosition(point) };
241        if result == CGError::Success {
242            Ok(())
243        } else {
244            Err(result)
245        }
246    }
247
248    pub fn get_drawing_context(&self) -> Option<CGContext> {
249        unsafe {
250            let context = CGDisplayGetDrawingContext(self.id);
251            if context.is_null() {
252                None
253            } else {
254                Some(TCFType::wrap_under_create_rule(context))
255            }
256        }
257    }
258
259    // display configuration
260
261    pub fn set_stereo_operation(&self, stereo: bool, force_blue_line: bool, option: CGConfigureOption) -> Result<(), CGError> {
262        let result = unsafe { CGDisplaySetStereoOperation(self.id, stereo as _, force_blue_line as _, option) };
263        if result == CGError::Success {
264            Ok(())
265        } else {
266            Err(result)
267        }
268    }
269
270    pub fn is_active(&self) -> bool {
271        unsafe { CGDisplayIsActive(self.id) != 0 }
272    }
273
274    pub fn is_asleep(&self) -> bool {
275        unsafe { CGDisplayIsAsleep(self.id) != 0 }
276    }
277
278    pub fn is_online(&self) -> bool {
279        unsafe { CGDisplayIsOnline(self.id) != 0 }
280    }
281
282    pub fn is_main(&self) -> bool {
283        unsafe { CGDisplayIsMain(self.id) != 0 }
284    }
285
286    pub fn is_built_in(&self) -> bool {
287        unsafe { CGDisplayIsBuiltin(self.id) != 0 }
288    }
289
290    pub fn is_in_mirror_set(&self) -> bool {
291        unsafe { CGDisplayIsInMirrorSet(self.id) != 0 }
292    }
293
294    pub fn is_always_in_mirror_set(&self) -> bool {
295        unsafe { CGDisplayIsInHWMirrorSet(self.id) != 0 }
296    }
297
298    pub fn is_in_hw_mirror_set(&self) -> bool {
299        unsafe { CGDisplayIsInHWMirrorSet(self.id) != 0 }
300    }
301
302    pub fn is_stereo(&self) -> bool {
303        unsafe { CGDisplayIsStereo(self.id) != 0 }
304    }
305
306    pub fn uses_opengl_acceleration(&self) -> bool {
307        unsafe { CGDisplayUsesOpenGLAcceleration(self.id) != 0 }
308    }
309
310    pub fn mirrors_display(&self) -> Self {
311        unsafe { CGDisplay::new(CGDisplayMirrorsDisplay(self.id)) }
312    }
313
314    pub fn primary_display(&self) -> Self {
315        unsafe { CGDisplay::new(CGDisplayPrimaryDisplay(self.id)) }
316    }
317
318    pub fn unit_number(&self) -> u32 {
319        unsafe { CGDisplayUnitNumber(self.id) }
320    }
321
322    pub fn vendor_number(&self) -> u32 {
323        unsafe { CGDisplayVendorNumber(self.id) }
324    }
325
326    pub fn model_number(&self) -> u32 {
327        unsafe { CGDisplayModelNumber(self.id) }
328    }
329
330    pub fn serial_number(&self) -> u32 {
331        unsafe { CGDisplaySerialNumber(self.id) }
332    }
333
334    pub fn screen_size(&self) -> CGSize {
335        unsafe { CGDisplayScreenSize(self.id) }
336    }
337
338    pub fn rotation(&self) -> c_double {
339        unsafe { CGDisplayRotation(self.id) }
340    }
341
342    pub fn copy_color_space(&self) -> Option<CGColorSpace> {
343        unsafe {
344            let space = CGDisplayCopyColorSpace(self.id);
345            if space.is_null() {
346                None
347            } else {
348                Some(TCFType::wrap_under_create_rule(space))
349            }
350        }
351    }
352}
353
354fn get_displays_from_ids(ids: &mut Vec<CGDirectDisplayID>, count: usize) -> Option<Vec<CGDisplay>> {
355    ids.truncate(count as usize);
356    Some(ids.into_iter().map(|id| CGDisplay::new(*id)).collect())
357}
358
359pub fn get_displays_with_point(point: CGPoint, max_displays: usize) -> Option<Vec<CGDisplay>> {
360    unsafe {
361        let mut count = max_displays as u32;
362        let mut ids: Vec<CGDirectDisplayID> = vec![0; max_displays];
363        let result = CGGetDisplaysWithPoint(point, count, ids.as_mut_ptr(), &mut count);
364        if result == CGError::Success {
365            get_displays_from_ids(&mut ids, count as usize)
366        } else {
367            None
368        }
369    }
370}
371
372pub fn get_displays_with_rect(rect: CGRect, max_displays: usize) -> Option<Vec<CGDisplay>> {
373    unsafe {
374        let mut count = max_displays as u32;
375        let mut ids = vec![0; max_displays];
376        let result = CGGetDisplaysWithRect(rect, count, ids.as_mut_ptr(), &mut count);
377        if result == CGError::Success {
378            get_displays_from_ids(&mut ids, count as usize)
379        } else {
380            None
381        }
382    }
383}
384
385pub fn get_displays_with_opengl_display_mask(mask: CGOpenGLDisplayMask, max_displays: usize) -> Option<Vec<CGDisplay>> {
386    unsafe {
387        let mut count = max_displays as u32;
388        let mut ids = vec![0; max_displays];
389        let result = CGGetDisplaysWithOpenGLDisplayMask(mask, count, ids.as_mut_ptr(), &mut count);
390        if result == CGError::Success {
391            get_displays_from_ids(&mut ids, count as usize)
392        } else {
393            None
394        }
395    }
396}
397
398pub fn get_active_display_list(max_displays: usize) -> Option<Vec<CGDisplay>> {
399    unsafe {
400        let mut count = max_displays as u32;
401        let mut ids = vec![0; max_displays];
402        let result = CGGetActiveDisplayList(count, ids.as_mut_ptr(), &mut count);
403        if result == CGError::Success {
404            get_displays_from_ids(&mut ids, count as usize)
405        } else {
406            None
407        }
408    }
409}
410
411pub fn get_online_display_list(max_displays: usize) -> Option<Vec<CGDisplay>> {
412    unsafe {
413        let mut count = max_displays as u32;
414        let mut ids = vec![0; max_displays];
415        let result = CGGetOnlineDisplayList(count, ids.as_mut_ptr(), &mut count);
416        if result == CGError::Success {
417            get_displays_from_ids(&mut ids, count as usize)
418        } else {
419            None
420        }
421    }
422}
423
424pub fn display_id_to_opengl_display_mask(display_id: CGDirectDisplayID) -> CGOpenGLDisplayMask {
425    unsafe { CGDisplayIDToOpenGLDisplayMask(display_id) }
426}
427
428pub fn opengl_display_mask_to_display_id(mask: CGOpenGLDisplayMask) -> CGDirectDisplayID {
429    unsafe { CGOpenGLDisplayMaskToDisplayID(mask) }
430}
431
432pub fn capture_all_displays() -> Result<(), CGError> {
433    let result = unsafe { CGCaptureAllDisplays() };
434    if result == CGError::Success {
435        Ok(())
436    } else {
437        Err(result)
438    }
439}
440
441pub fn capture_all_displays_with_options(options: CGCaptureOptions) -> Result<(), CGError> {
442    let result = unsafe { CGCaptureAllDisplaysWithOptions(options) };
443    if result == CGError::Success {
444        Ok(())
445    } else {
446        Err(result)
447    }
448}
449
450pub fn release_all_displays() -> Result<(), CGError> {
451    let result = unsafe { CGReleaseAllDisplays() };
452    if result == CGError::Success {
453        Ok(())
454    } else {
455        Err(result)
456    }
457}