core_graphics/
display.rs

1// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution.
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10#![allow(non_upper_case_globals)]
11
12use bitflags::bitflags;
13use core::ffi::{c_double, c_void};
14use std::ops::Deref;
15use std::ptr;
16
17pub use crate::base::{boolean_t, CGError};
18pub use crate::geometry::{CGPoint, CGRect, CGSize};
19
20use crate::image::CGImage;
21use crate::window::{
22    kCGNullWindowID, CGWindowID, CGWindowImageOption, CGWindowLevel, CGWindowListOption,
23};
24use core_foundation::base::{CFRetain, TCFType};
25use core_foundation::string::{CFString, CFStringRef};
26use core_graphics_types::base::kCGErrorSuccess;
27use foreign_types::{foreign_type, ForeignType};
28
29pub type CGDirectDisplayID = u32;
30
31pub const kCGNullDirectDisplayID: CGDirectDisplayID = 0 as CGDirectDisplayID;
32
33pub const kDisplayModeValidFlag: u32 = 0x00000001;
34pub const kDisplayModeSafeFlag: u32 = 0x00000002;
35pub const kDisplayModeDefaultFlag: u32 = 0x00000004;
36pub const kDisplayModeAlwaysShowFlag: u32 = 0x00000008;
37pub const kDisplayModeNeverShowFlag: u32 = 0x00000080;
38pub const kDisplayModeNotResizeFlag: u32 = 0x00000010;
39pub const kDisplayModeRequiresPanFlag: u32 = 0x00000020;
40pub const kDisplayModeInterlacedFlag: u32 = 0x00000040;
41pub const kDisplayModeSimulscanFlag: u32 = 0x00000100;
42pub const kDisplayModeBuiltInFlag: u32 = 0x00000400;
43pub const kDisplayModeNotPresetFlag: u32 = 0x00000200;
44pub const kDisplayModeStretchedFlag: u32 = 0x00000800;
45pub const kDisplayModeNotGraphicsQualityFlag: u32 = 0x00001000;
46pub const kDisplayModeValidateAgainstDisplay: u32 = 0x00002000;
47pub const kDisplayModeTelevisionFlag: u32 = 0x00100000;
48pub const kDisplayModeValidForMirroringFlag: u32 = 0x00200000;
49pub const kDisplayModeAcceleratorBackedFlag: u32 = 0x00400000;
50pub const kDisplayModeValidForHiResFlag: u32 = 0x00800000;
51pub const kDisplayModeValidForAirPlayFlag: u32 = 0x01000000;
52pub const kDisplayModeNativeFlag: u32 = 0x02000000;
53
54pub const kDisplayModeSafetyFlags: u32 = 0x00000007;
55
56pub type CGDisplayBlendFraction = f32;
57pub const kCGDisplayBlendNormal: CGDisplayBlendFraction = 0.0;
58pub const kCGDisplayBlendSolidColor: CGDisplayBlendFraction = 1.0;
59
60pub type CGDisplayFadeReservationToken = u32;
61pub const kCGDisplayFadeReservationInvalidToken: CGDisplayFadeReservationToken = 0;
62
63pub type CGDisplayFadeInterval = f32;
64pub type CGDisplayReservationInterval = f32;
65pub const kCGMaxDisplayReservationInterval: CGDisplayReservationInterval = 15.0;
66
67pub const IO1BitIndexedPixels: &str = "P";
68pub const IO2BitIndexedPixels: &str = "PP";
69pub const IO4BitIndexedPixels: &str = "PPPP";
70pub const IO8BitIndexedPixels: &str = "PPPPPPPP";
71pub const IO16BitDirectPixels: &str = "-RRRRRGGGGGBBBBB";
72pub const IO32BitDirectPixels: &str = "--------RRRRRRRRGGGGGGGGBBBBBBBB";
73pub const kIO30BitDirectPixels: &str = "--RRRRRRRRRRGGGGGGGGGGBBBBBBBBBB";
74pub const kIO64BitDirectPixels: &str = "-16R16G16B16";
75pub const kIO16BitFloatPixels: &str = "-16FR16FG16FB16";
76pub const kIO32BitFloatPixels: &str = "-32FR32FG32FB32";
77pub const IOYUV422Pixels: &str = "Y4U2V2";
78pub const IO8BitOverlayPixels: &str = "O8";
79
80pub use core_foundation::array::{CFArray, CFArrayRef};
81pub use core_foundation::array::{CFArrayGetCount, CFArrayGetValueAtIndex};
82pub use core_foundation::base::{CFIndex, CFRelease, CFTypeRef};
83pub use core_foundation::dictionary::{
84    CFDictionary, CFDictionaryGetValueIfPresent, CFDictionaryRef,
85};
86
87pub type CGDisplayConfigRef = *mut c_void;
88
89#[repr(u32)]
90#[derive(Clone, Copy)]
91pub enum CGConfigureOption {
92    ConfigureForAppOnly = 0,
93    ConfigureForSession = 1,
94    ConfigurePermanently = 2,
95}
96
97/// A client-supplied callback function that’s invoked whenever the configuration of a local display is changed.
98pub type CGDisplayReconfigurationCallBack =
99    unsafe extern "C" fn(display: CGDirectDisplayID, flags: u32, user_info: *const c_void);
100
101bitflags! {
102    /// The configuration parameters that are passed to a display reconfiguration callback function.
103    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
104    pub struct CGDisplayChangeSummaryFlags: u32 {
105        /// The display configuration is about to change.
106        const kCGDisplayBeginConfigurationFlag = 1;
107        /// The location of the upper-left corner of the display in the global display coordinate space has changed.
108        const kCGDisplayMovedFlag = 1 << 1;
109        /// The display is now the main display.
110        const kCGDisplaySetMainFlag = 1 << 2;
111        /// The display mode has changed.
112        const kCGDisplaySetModeFlag = 1 << 3;
113        /// The display has been added to the active display list.
114        const kCGDisplayAddFlag = 1 << 4;
115        /// The display has been removed from the active display list.
116        const kCGDisplayRemoveFlag = 1 << 5;
117        /// The display has been enabled.
118        const kCGDisplayEnabledFlag = 1 << 8;
119        /// The display has been disabled.
120        const kCGDisplayDisabledFlag = 1 << 9;
121        /// The display is now mirroring another display.
122        const kCGDisplayMirrorFlag = 1 << 10;
123        /// The display is no longer mirroring another display.
124        const kCGDisplayUnMirrorFlag = 1 << 11;
125        /// The shape of the desktop (the union of display areas) has changed.
126        const kCGDisplayDesktopShapeChangedFlag = 1 << 12;
127
128        const _ = !0;
129    }
130}
131
132#[derive(Copy, Clone, Debug)]
133pub struct CGDisplay {
134    pub id: CGDirectDisplayID,
135}
136
137foreign_type! {
138    #[doc(hidden)]
139    pub unsafe type CGDisplayMode {
140        type CType = crate::sys::CGDisplayMode;
141        fn drop = CGDisplayModeRelease;
142        fn clone = |p| CFRetain(p as *const _) as *mut _;
143    }
144}
145
146impl CGDisplay {
147    #[inline]
148    pub fn new(id: CGDirectDisplayID) -> CGDisplay {
149        CGDisplay { id }
150    }
151
152    /// Returns the the main display.
153    #[inline]
154    pub fn main() -> CGDisplay {
155        CGDisplay::new(unsafe { CGMainDisplayID() })
156    }
157
158    /// A value that will never correspond to actual hardware.
159    pub fn null_display() -> CGDisplay {
160        CGDisplay::new(kCGNullDirectDisplayID)
161    }
162
163    /// Return the number of online displays with bounds that include the
164    /// specified point.
165    #[inline]
166    pub fn display_count_with_point(point: CGPoint) -> Result<u32, CGError> {
167        let mut matching_display_count: u32 = 0;
168        let result = unsafe {
169            CGGetDisplaysWithPoint(point, 0, ptr::null_mut(), &mut matching_display_count)
170        };
171        if result == kCGErrorSuccess {
172            Ok(matching_display_count)
173        } else {
174            Err(result)
175        }
176    }
177
178    /// Return a list of online displays with bounds that include the specified
179    /// point.
180    #[inline]
181    pub fn displays_with_point(
182        point: CGPoint,
183        max_displays: u32,
184    ) -> Result<(Vec<CGDirectDisplayID>, u32), CGError> {
185        let count = CGDisplay::display_count_with_point(point)?;
186        let count = u32::max(u32::min(count, max_displays), 1);
187
188        let mut matching_display_count: u32 = 0;
189        let mut displays: Vec<CGDirectDisplayID> = vec![0; count as usize];
190        let result = unsafe {
191            CGGetDisplaysWithPoint(
192                point,
193                max_displays,
194                displays.as_mut_ptr(),
195                &mut matching_display_count,
196            )
197        };
198
199        if result == kCGErrorSuccess {
200            Ok((displays, matching_display_count))
201        } else {
202            Err(result)
203        }
204    }
205
206    /// Return the number of online displays with bounds that intersect the
207    /// specified rectangle.
208    #[inline]
209    pub fn display_count_with_rect(rect: CGRect) -> Result<u32, CGError> {
210        let mut matching_display_count: u32 = 0;
211        let result =
212            unsafe { CGGetDisplaysWithRect(rect, 0, ptr::null_mut(), &mut matching_display_count) };
213        if result == kCGErrorSuccess {
214            Ok(matching_display_count)
215        } else {
216            Err(result)
217        }
218    }
219
220    /// Return a list of online displays with bounds that intersect the specified rectangle.
221    #[inline]
222    pub fn displays_with_rect(
223        rect: CGRect,
224        max_displays: u32,
225    ) -> Result<(Vec<CGDirectDisplayID>, u32), CGError> {
226        let count = CGDisplay::display_count_with_rect(rect)?;
227        let count = u32::max(u32::min(count, max_displays), 1);
228
229        let mut matching_display_count: u32 = 0;
230        let mut displays: Vec<CGDirectDisplayID> = vec![0; count as usize];
231        let result = unsafe {
232            CGGetDisplaysWithRect(
233                rect,
234                max_displays,
235                displays.as_mut_ptr(),
236                &mut matching_display_count,
237            )
238        };
239
240        if result == kCGErrorSuccess {
241            Ok((displays, matching_display_count))
242        } else {
243            Err(result)
244        }
245    }
246
247    /// Returns the bounds of a display in the global display coordinate space.
248    #[inline]
249    pub fn bounds(&self) -> CGRect {
250        unsafe { CGDisplayBounds(self.id) }
251    }
252
253    /// Returns information about a display's current configuration.
254    #[inline]
255    pub fn display_mode(&self) -> Option<CGDisplayMode> {
256        unsafe {
257            let mode_ref = CGDisplayCopyDisplayMode(self.id);
258            if !mode_ref.is_null() {
259                Some(CGDisplayMode::from_ptr(mode_ref))
260            } else {
261                None
262            }
263        }
264    }
265
266    /// Begins a new set of display configuration changes.
267    pub fn begin_configuration(&self) -> Result<CGDisplayConfigRef, CGError> {
268        unsafe {
269            let mut config_ref: CGDisplayConfigRef = ptr::null_mut();
270            let result = CGBeginDisplayConfiguration(&mut config_ref);
271            if result == 0 {
272                Ok(config_ref)
273            } else {
274                Err(result)
275            }
276        }
277    }
278
279    /// Cancels a set of display configuration changes.
280    pub fn cancel_configuration(&self, config_ref: &CGDisplayConfigRef) -> Result<(), CGError> {
281        let result = unsafe { CGCancelDisplayConfiguration(*config_ref) };
282        if result == 0 {
283            Ok(())
284        } else {
285            Err(result)
286        }
287    }
288
289    /// Completes a set of display configuration changes.
290    pub fn complete_configuration(
291        &self,
292        config_ref: &CGDisplayConfigRef,
293        option: CGConfigureOption,
294    ) -> Result<(), CGError> {
295        let result = unsafe { CGCompleteDisplayConfiguration(*config_ref, option) };
296        if result == 0 {
297            Ok(())
298        } else {
299            Err(result)
300        }
301    }
302
303    /// Configures the display mode of a display.
304    pub fn configure_display_with_display_mode(
305        &self,
306        config_ref: &CGDisplayConfigRef,
307        display_mode: &CGDisplayMode,
308    ) -> Result<(), CGError> {
309        let result = unsafe {
310            CGConfigureDisplayWithDisplayMode(
311                *config_ref,
312                self.id,
313                display_mode.as_ptr(),
314                ptr::null(),
315            )
316        };
317        if result == 0 {
318            Ok(())
319        } else {
320            Err(result)
321        }
322    }
323
324    /// Configures the origin of a display in the global display coordinate space.
325    pub fn configure_display_origin(
326        &self,
327        config_ref: &CGDisplayConfigRef,
328        x: i32,
329        y: i32,
330    ) -> Result<(), CGError> {
331        let result = unsafe { CGConfigureDisplayOrigin(*config_ref, self.id, x, y) };
332
333        if result == 0 {
334            Ok(())
335        } else {
336            Err(result)
337        }
338    }
339
340    /// Changes the configuration of a mirroring set.
341    pub fn configure_display_mirror_of_display(
342        &self,
343        config_ref: &CGDisplayConfigRef,
344        master: &CGDisplay,
345    ) -> Result<(), CGError> {
346        let result = unsafe { CGConfigureDisplayMirrorOfDisplay(*config_ref, self.id, master.id) };
347
348        if result == 0 {
349            Ok(())
350        } else {
351            Err(result)
352        }
353    }
354
355    /// Returns an image containing the contents of the specified display.
356    #[inline]
357    pub fn image(&self) -> Option<CGImage> {
358        unsafe {
359            let image_ref = CGDisplayCreateImage(self.id);
360            if !image_ref.is_null() {
361                Some(CGImage::from_ptr(image_ref))
362            } else {
363                None
364            }
365        }
366    }
367
368    /// Returns an image containing the contents of a portion of the specified display.
369    #[inline]
370    pub fn image_for_rect(&self, bounds: CGRect) -> Option<CGImage> {
371        unsafe {
372            let image_ref = CGDisplayCreateImageForRect(self.id, bounds);
373            if !image_ref.is_null() {
374                Some(CGImage::from_ptr(image_ref))
375            } else {
376                None
377            }
378        }
379    }
380
381    /// Returns a composite image based on a dynamically generated list of
382    /// windows.
383    #[inline]
384    pub fn screenshot(
385        bounds: CGRect,
386        list_option: CGWindowListOption,
387        window_id: CGWindowID,
388        image_option: CGWindowImageOption,
389    ) -> Option<CGImage> {
390        unsafe {
391            let image_ref = CGWindowListCreateImage(bounds, list_option, window_id, image_option);
392            if !image_ref.is_null() {
393                Some(CGImage::from_ptr(image_ref))
394            } else {
395                None
396            }
397        }
398    }
399
400    /// Returns a composite image of the specified windows.
401    #[inline]
402    pub fn screenshot_from_windows(
403        bounds: CGRect,
404        windows: CFArray,
405        image_option: CGWindowImageOption,
406    ) -> Option<CGImage> {
407        unsafe {
408            let image_ref = CGWindowListCreateImageFromArray(
409                bounds,
410                windows.as_concrete_TypeRef(),
411                image_option,
412            );
413            if !image_ref.is_null() {
414                Some(CGImage::from_ptr(image_ref))
415            } else {
416                None
417            }
418        }
419    }
420
421    /// Generates and returns information about the selected windows in the
422    /// current user session.
423    pub fn window_list_info(
424        option: CGWindowListOption,
425        relative_to_window: Option<CGWindowID>,
426    ) -> Option<CFArray> {
427        let relative_to_window = relative_to_window.unwrap_or(kCGNullWindowID);
428        let array_ref = unsafe { CGWindowListCopyWindowInfo(option, relative_to_window) };
429        if !array_ref.is_null() {
430            Some(unsafe { TCFType::wrap_under_create_rule(array_ref) })
431        } else {
432            None
433        }
434    }
435
436    /// Returns a Boolean value indicating whether a display is active.
437    #[inline]
438    pub fn is_active(&self) -> bool {
439        unsafe { CGDisplayIsActive(self.id) != 0 }
440    }
441
442    /// Returns a boolean indicating whether a display is always in a
443    /// mirroring set.
444    #[inline]
445    pub fn is_always_in_mirror_set(&self) -> bool {
446        unsafe { CGDisplayIsAlwaysInMirrorSet(self.id) != 0 }
447    }
448
449    /// Returns a boolean indicating whether a display is sleeping (and is
450    /// therefore not drawable.)
451    #[inline]
452    pub fn is_asleep(&self) -> bool {
453        unsafe { CGDisplayIsAsleep(self.id) != 0 }
454    }
455
456    /// Returns a boolean indicating whether a display is built-in, such as
457    /// the internal display in portable systems.
458    #[inline]
459    pub fn is_builtin(&self) -> bool {
460        unsafe { CGDisplayIsBuiltin(self.id) != 0 }
461    }
462
463    /// Returns a boolean indicating whether a display is in a hardware
464    /// mirroring set.
465    #[inline]
466    pub fn is_in_hw_mirror_set(&self) -> bool {
467        unsafe { CGDisplayIsInHWMirrorSet(self.id) != 0 }
468    }
469
470    /// Returns a boolean indicating whether a display is in a mirroring set.
471    #[inline]
472    pub fn is_in_mirror_set(&self) -> bool {
473        unsafe { CGDisplayIsInMirrorSet(self.id) != 0 }
474    }
475
476    /// Returns a boolean indicating whether a display is the main display.
477    #[inline]
478    pub fn is_main(&self) -> bool {
479        unsafe { CGDisplayIsMain(self.id) != 0 }
480    }
481
482    /// Returns a boolean indicating whether a display is connected or online.
483    #[inline]
484    pub fn is_online(&self) -> bool {
485        unsafe { CGDisplayIsOnline(self.id) != 0 }
486    }
487
488    /// Returns a boolean indicating whether Quartz is using OpenGL-based
489    /// window acceleration (Quartz Extreme) to render in a display.
490    #[inline]
491    pub fn uses_open_gl_acceleration(&self) -> bool {
492        unsafe { CGDisplayUsesOpenGLAcceleration(self.id) != 0 }
493    }
494
495    /// Returns a boolean indicating whether a display is running in a stereo
496    /// graphics mode.
497    #[inline]
498    pub fn is_stereo(&self) -> bool {
499        unsafe { CGDisplayIsStereo(self.id) != 0 }
500    }
501
502    /// For a secondary display in a mirroring set, returns the primary
503    /// display.
504    #[inline]
505    pub fn mirrors_display(&self) -> CGDirectDisplayID {
506        unsafe { CGDisplayMirrorsDisplay(self.id) }
507    }
508
509    /// Returns the primary display in a hardware mirroring set.
510    #[inline]
511    pub fn primary_display(&self) -> CGDirectDisplayID {
512        unsafe { CGDisplayPrimaryDisplay(self.id) }
513    }
514
515    /// Returns the rotation angle of a display in degrees.
516    #[inline]
517    pub fn rotation(&self) -> f64 {
518        unsafe { CGDisplayRotation(self.id) }
519    }
520
521    /// Returns the width and height of a display in millimeters.
522    #[inline]
523    pub fn screen_size(&self) -> CGSize {
524        unsafe { CGDisplayScreenSize(self.id) }
525    }
526
527    /// Returns the serial number of a display monitor.
528    #[inline]
529    pub fn serial_number(&self) -> u32 {
530        unsafe { CGDisplaySerialNumber(self.id) }
531    }
532
533    /// Returns the logical unit number of a display.
534    #[inline]
535    pub fn unit_number(&self) -> u32 {
536        unsafe { CGDisplayUnitNumber(self.id) }
537    }
538
539    /// Returns the vendor number of the specified display's monitor.
540    #[inline]
541    pub fn vendor_number(&self) -> u32 {
542        unsafe { CGDisplayVendorNumber(self.id) }
543    }
544
545    /// Returns the model number of a display monitor.
546    #[inline]
547    pub fn model_number(&self) -> u32 {
548        unsafe { CGDisplayModelNumber(self.id) }
549    }
550
551    /// Returns the display height in pixel units.
552    #[inline]
553    pub fn pixels_high(&self) -> u64 {
554        unsafe { CGDisplayPixelsHigh(self.id) as u64 }
555    }
556
557    /// Returns the display width in pixel units.
558    #[inline]
559    pub fn pixels_wide(&self) -> u64 {
560        unsafe { CGDisplayPixelsWide(self.id) as u64 }
561    }
562
563    /// Provides a list of displays that are active (or drawable).
564    #[inline]
565    pub fn active_displays() -> Result<Vec<CGDirectDisplayID>, CGError> {
566        let expected_count = CGDisplay::active_display_count()?;
567        let mut buf: Vec<CGDirectDisplayID> = vec![0; expected_count as usize];
568
569        let mut actual_count: u32 = 0;
570
571        let result =
572            unsafe { CGGetActiveDisplayList(expected_count, buf.as_mut_ptr(), &mut actual_count) };
573        if result == 0 {
574            buf.truncate(actual_count as usize);
575            Ok(buf)
576        } else {
577            Err(result)
578        }
579    }
580
581    /// Provides count of displays that are active (or drawable).
582    #[inline]
583    pub fn active_display_count() -> Result<u32, CGError> {
584        let mut count: u32 = 0;
585        let result = unsafe { CGGetActiveDisplayList(0, ptr::null_mut(), &mut count) };
586        if result == 0 {
587            Ok(count)
588        } else {
589            Err(result)
590        }
591    }
592
593    /// Hides the mouse cursor, and increments the hide cursor count.
594    #[inline]
595    pub fn hide_cursor(&self) -> Result<(), CGError> {
596        let result = unsafe { CGDisplayHideCursor(self.id) };
597        if result == 0 {
598            Ok(())
599        } else {
600            Err(result)
601        }
602    }
603
604    /// Decrements the hide cursor count, and shows the mouse cursor if the
605    /// count is 0.
606    #[inline]
607    pub fn show_cursor(&self) -> Result<(), CGError> {
608        let result = unsafe { CGDisplayShowCursor(self.id) };
609        if result == 0 {
610            Ok(())
611        } else {
612            Err(result)
613        }
614    }
615
616    /// Moves the mouse cursor to a specified point relative to the display
617    /// origin (the upper-left corner of the display).
618    #[inline]
619    pub fn move_cursor_to_point(&self, point: CGPoint) -> Result<(), CGError> {
620        let result = unsafe { CGDisplayMoveCursorToPoint(self.id, point) };
621        if result == 0 {
622            Ok(())
623        } else {
624            Err(result)
625        }
626    }
627
628    /// Moves the mouse cursor without generating events.
629    #[inline]
630    pub fn warp_mouse_cursor_position(point: CGPoint) -> Result<(), CGError> {
631        let result = unsafe { CGWarpMouseCursorPosition(point) };
632        if result == 0 {
633            Ok(())
634        } else {
635            Err(result)
636        }
637    }
638
639    /// Connects or disconnects the mouse and cursor while an application is
640    /// in the foreground.
641    #[inline]
642    pub fn associate_mouse_and_mouse_cursor_position(connected: bool) -> Result<(), CGError> {
643        let result = unsafe { CGAssociateMouseAndMouseCursorPosition(connected as boolean_t) };
644        if result == 0 {
645            Ok(())
646        } else {
647            Err(result)
648        }
649    }
650}
651
652impl CGDisplayMode {
653    /// Returns all display modes for the specified display id.
654    pub fn all_display_modes(
655        display_id: CGDirectDisplayID,
656        options: CFDictionaryRef,
657    ) -> Option<Vec<CGDisplayMode>> {
658        let array_opt: Option<CFArray> = unsafe {
659            let array_ref = CGDisplayCopyAllDisplayModes(display_id, options);
660            if !array_ref.is_null() {
661                Some(CFArray::wrap_under_create_rule(array_ref))
662            } else {
663                None
664            }
665        };
666        match array_opt {
667            Some(modes) => {
668                let vec: Vec<CGDisplayMode> = modes
669                    .into_iter()
670                    .map(|value0| {
671                        let x = *value0.deref() as *mut crate::sys::CGDisplayMode;
672                        unsafe { CGDisplayMode::from_ptr(x) }
673                    })
674                    .collect();
675                Some(vec)
676            }
677            None => None,
678        }
679    }
680
681    /// Returns the height of the specified display mode.
682    #[inline]
683    pub fn height(&self) -> u64 {
684        unsafe { CGDisplayModeGetHeight(self.as_ptr()) as u64 }
685    }
686
687    /// Returns the width of the specified display mode.
688    #[inline]
689    pub fn width(&self) -> u64 {
690        unsafe { CGDisplayModeGetWidth(self.as_ptr()) as u64 }
691    }
692
693    /// Returns the pixel height of the specified display mode.
694    #[inline]
695    pub fn pixel_height(&self) -> u64 {
696        unsafe { CGDisplayModeGetPixelHeight(self.as_ptr()) as u64 }
697    }
698
699    /// Returns the pixel width of the specified display mode.
700    #[inline]
701    pub fn pixel_width(&self) -> u64 {
702        unsafe { CGDisplayModeGetPixelWidth(self.as_ptr()) as u64 }
703    }
704
705    #[inline]
706    pub fn refresh_rate(&self) -> f64 {
707        unsafe { CGDisplayModeGetRefreshRate(self.as_ptr()) }
708    }
709
710    /// Returns the I/O Kit flags of the specified display mode.
711    #[inline]
712    pub fn io_flags(&self) -> u32 {
713        unsafe { CGDisplayModeGetIOFlags(self.as_ptr()) }
714    }
715
716    /// Returns the pixel encoding of the specified display mode.
717    #[inline]
718    pub fn pixel_encoding(&self) -> CFString {
719        unsafe { CFString::wrap_under_create_rule(CGDisplayModeCopyPixelEncoding(self.as_ptr())) }
720    }
721
722    /// Returns the number of bits per pixel of the specified display mode.
723    pub fn bit_depth(&self) -> usize {
724        let pixel_encoding = self.pixel_encoding().to_string();
725        // my numerical representation for kIO16BitFloatPixels and kIO32bitFloatPixels
726        // are made up and possibly non-sensical
727        if pixel_encoding.eq_ignore_ascii_case(kIO32BitFloatPixels) {
728            96
729        } else if pixel_encoding.eq_ignore_ascii_case(kIO64BitDirectPixels) {
730            64
731        } else if pixel_encoding.eq_ignore_ascii_case(kIO16BitFloatPixels) {
732            48
733        } else if pixel_encoding.eq_ignore_ascii_case(IO32BitDirectPixels) {
734            32
735        } else if pixel_encoding.eq_ignore_ascii_case(kIO30BitDirectPixels) {
736            30
737        } else if pixel_encoding.eq_ignore_ascii_case(IO16BitDirectPixels) {
738            16
739        } else if pixel_encoding.eq_ignore_ascii_case(IO8BitIndexedPixels) {
740            8
741        } else {
742            0
743        }
744    }
745
746    pub fn mode_id(&self) -> i32 {
747        unsafe { CGDisplayModeGetIODisplayModeID(self.as_ptr()) }
748    }
749}
750
751#[cfg_attr(feature = "link", link(name = "CoreGraphics", kind = "framework"))]
752extern "C" {
753    pub static CGRectNull: CGRect;
754    pub static CGRectInfinite: CGRect;
755
756    pub static kCGDisplayShowDuplicateLowResolutionModes: CFStringRef;
757
758    pub fn CGDisplayModeRetain(mode: crate::sys::CGDisplayModeRef);
759    pub fn CGDisplayModeRelease(mode: crate::sys::CGDisplayModeRef);
760
761    pub fn CGMainDisplayID() -> CGDirectDisplayID;
762    pub fn CGDisplayIsActive(display: CGDirectDisplayID) -> boolean_t;
763    pub fn CGDisplayIsAlwaysInMirrorSet(display: CGDirectDisplayID) -> boolean_t;
764    pub fn CGDisplayIsAsleep(display: CGDirectDisplayID) -> boolean_t;
765    pub fn CGDisplayIsBuiltin(display: CGDirectDisplayID) -> boolean_t;
766    pub fn CGDisplayIsInHWMirrorSet(display: CGDirectDisplayID) -> boolean_t;
767    pub fn CGDisplayIsInMirrorSet(display: CGDirectDisplayID) -> boolean_t;
768    pub fn CGDisplayIsMain(display: CGDirectDisplayID) -> boolean_t;
769    pub fn CGDisplayIsOnline(display: CGDirectDisplayID) -> boolean_t;
770    pub fn CGDisplayIsStereo(display: CGDirectDisplayID) -> boolean_t;
771    pub fn CGDisplayMirrorsDisplay(display: CGDirectDisplayID) -> CGDirectDisplayID;
772    pub fn CGDisplayPrimaryDisplay(display: CGDirectDisplayID) -> CGDirectDisplayID;
773    pub fn CGDisplayRotation(display: CGDirectDisplayID) -> c_double;
774    pub fn CGDisplayScreenSize(display: CGDirectDisplayID) -> CGSize;
775    pub fn CGDisplaySerialNumber(display: CGDirectDisplayID) -> u32;
776    pub fn CGDisplayUnitNumber(display: CGDirectDisplayID) -> u32;
777    pub fn CGDisplayUsesOpenGLAcceleration(display: CGDirectDisplayID) -> boolean_t;
778    pub fn CGDisplayVendorNumber(display: CGDirectDisplayID) -> u32;
779    pub fn CGGetActiveDisplayList(
780        max_displays: u32,
781        active_displays: *mut CGDirectDisplayID,
782        display_count: *mut u32,
783    ) -> CGError;
784    pub fn CGGetDisplaysWithPoint(
785        point: CGPoint,
786        max_displays: u32,
787        displays: *mut CGDirectDisplayID,
788        matching_display_count: *mut u32,
789    ) -> CGError;
790    pub fn CGGetDisplaysWithRect(
791        rect: CGRect,
792        max_displays: u32,
793        displays: *mut CGDirectDisplayID,
794        matching_display_count: *mut u32,
795    ) -> CGError;
796    pub fn CGDisplayModelNumber(display: CGDirectDisplayID) -> u32;
797    pub fn CGDisplayPixelsHigh(display: CGDirectDisplayID) -> usize;
798    pub fn CGDisplayPixelsWide(display: CGDirectDisplayID) -> usize;
799    pub fn CGDisplayBounds(display: CGDirectDisplayID) -> CGRect;
800    pub fn CGDisplayCreateImage(display: CGDirectDisplayID) -> crate::sys::CGImageRef;
801    pub fn CGDisplayCreateImageForRect(
802        display: CGDirectDisplayID,
803        rect: CGRect,
804    ) -> crate::sys::CGImageRef;
805
806    // Capturing and Releasing Displays
807    pub fn CGDisplayCapture(display: CGDirectDisplayID) -> CGError;
808    pub fn CGDisplayRelease(display: CGDirectDisplayID) -> CGError;
809    pub fn CGShieldingWindowLevel() -> CGWindowLevel;
810
811    // Configuring Displays
812    pub fn CGBeginDisplayConfiguration(config: *mut CGDisplayConfigRef) -> CGError;
813    pub fn CGCancelDisplayConfiguration(config: CGDisplayConfigRef) -> CGError;
814    pub fn CGCompleteDisplayConfiguration(
815        config: CGDisplayConfigRef,
816        option: CGConfigureOption,
817    ) -> CGError;
818    pub fn CGConfigureDisplayWithDisplayMode(
819        config: CGDisplayConfigRef,
820        display: CGDirectDisplayID,
821        mode: crate::sys::CGDisplayModeRef,
822        options: CFDictionaryRef,
823    ) -> CGError;
824    pub fn CGConfigureDisplayMirrorOfDisplay(
825        config: CGDisplayConfigRef,
826        display: CGDirectDisplayID,
827        master: CGDirectDisplayID,
828    ) -> CGError;
829    pub fn CGConfigureDisplayOrigin(
830        config: CGDisplayConfigRef,
831        display: CGDirectDisplayID,
832        x: i32,
833        y: i32,
834    ) -> CGError;
835    pub fn CGRestorePermanentDisplayConfiguration();
836    pub fn CGDisplayRegisterReconfigurationCallback(
837        callback: CGDisplayReconfigurationCallBack,
838        user_info: *const c_void,
839    ) -> CGError;
840    pub fn CGDisplayRemoveReconfigurationCallback(
841        callback: CGDisplayReconfigurationCallBack,
842        user_info: *const c_void,
843    ) -> CGError;
844
845    pub fn CGDisplayCopyDisplayMode(display: CGDirectDisplayID) -> crate::sys::CGDisplayModeRef;
846    pub fn CGDisplayModeGetHeight(mode: crate::sys::CGDisplayModeRef) -> usize;
847    pub fn CGDisplayModeGetWidth(mode: crate::sys::CGDisplayModeRef) -> usize;
848    pub fn CGDisplayModeGetPixelHeight(mode: crate::sys::CGDisplayModeRef) -> usize;
849    pub fn CGDisplayModeGetPixelWidth(mode: crate::sys::CGDisplayModeRef) -> usize;
850    pub fn CGDisplayModeGetRefreshRate(mode: crate::sys::CGDisplayModeRef) -> c_double;
851    pub fn CGDisplayModeGetIOFlags(mode: crate::sys::CGDisplayModeRef) -> u32;
852    pub fn CGDisplayModeCopyPixelEncoding(mode: crate::sys::CGDisplayModeRef) -> CFStringRef;
853    pub fn CGDisplayModeGetIODisplayModeID(mode: crate::sys::CGDisplayModeRef) -> i32;
854
855    pub fn CGDisplayCopyAllDisplayModes(
856        display: CGDirectDisplayID,
857        options: CFDictionaryRef,
858    ) -> CFArrayRef;
859    pub fn CGDisplaySetDisplayMode(
860        display: CGDirectDisplayID,
861        mode: crate::sys::CGDisplayModeRef,
862        options: CFDictionaryRef,
863    ) -> CGError;
864
865    // mouse stuff
866    pub fn CGDisplayHideCursor(display: CGDirectDisplayID) -> CGError;
867    pub fn CGDisplayShowCursor(display: CGDirectDisplayID) -> CGError;
868    pub fn CGDisplayMoveCursorToPoint(display: CGDirectDisplayID, point: CGPoint) -> CGError;
869    pub fn CGWarpMouseCursorPosition(point: CGPoint) -> CGError;
870    pub fn CGAssociateMouseAndMouseCursorPosition(connected: boolean_t) -> CGError;
871
872    // Display Fade Effects
873    pub fn CGConfigureDisplayFadeEffect(
874        config: CGDisplayConfigRef,
875        fadeOutSeconds: CGDisplayFadeInterval,
876        fadeInSeconds: CGDisplayFadeInterval,
877        fadeRed: f32,
878        fadeGreen: f32,
879        fadeBlue: f32,
880    ) -> CGError;
881    pub fn CGAcquireDisplayFadeReservation(
882        seconds: CGDisplayReservationInterval,
883        token: *mut CGDisplayFadeReservationToken,
884    ) -> CGError;
885    pub fn CGDisplayFade(
886        token: CGDisplayFadeReservationToken,
887        duration: CGDisplayFadeInterval,
888        startBlend: CGDisplayBlendFraction,
889        endBlend: CGDisplayBlendFraction,
890        redBlend: f32,
891        greenBlend: f32,
892        blueBlend: f32,
893        synchronous: boolean_t,
894    ) -> CGError;
895    // CGDisplayFadeOperationInProgress
896    pub fn CGReleaseDisplayFadeReservation(token: CGDisplayFadeReservationToken) -> CGError;
897
898    // Window Services Reference
899    pub fn CGWindowListCopyWindowInfo(
900        option: CGWindowListOption,
901        relativeToWindow: CGWindowID,
902    ) -> CFArrayRef;
903    pub fn CGWindowListCreateImage(
904        screenBounds: CGRect,
905        listOptions: CGWindowListOption,
906        windowId: CGWindowID,
907        imageOptions: CGWindowImageOption,
908    ) -> crate::sys::CGImageRef;
909    pub fn CGWindowListCreateImageFromArray(
910        screenBounds: CGRect,
911        windowArray: CFArrayRef,
912        imageOptions: CGWindowImageOption,
913    ) -> crate::sys::CGImageRef;
914}
915
916#[cfg(test)]
917mod test {
918    use super::*;
919
920    #[test]
921    fn test_display_count_with_point() {
922        let result = CGDisplay::display_count_with_point(CGPoint::new(0., 0.));
923        assert!(result.is_ok());
924    }
925
926    #[test]
927    fn test_displays_with_point_0() {
928        let result = CGDisplay::displays_with_point(CGPoint::new(0., 0.), 0);
929        assert!(result.is_ok());
930        let (displays, count) = result.unwrap();
931        assert_eq!(displays.len(), count as usize);
932    }
933
934    #[test]
935    fn test_displays_with_point_5() {
936        let result = CGDisplay::displays_with_point(CGPoint::new(0., 0.), 5);
937        assert!(result.is_ok());
938        let (displays, count) = result.unwrap();
939        assert_eq!(displays.len(), count as usize);
940    }
941
942    // NOTE: CGMainDisplayID must be called before CGGetDisplaysWithRect to avoid:
943    //   Assertion failed: (did_initialize), function CGS_REQUIRE_INIT, file CGInitialization.c, line 44.
944    // See https://github.com/JXA-Cookbook/JXA-Cookbook/issues/27#issuecomment-277517668
945
946    #[test]
947    fn test_display_count_with_rect() {
948        let _ = CGDisplay::main();
949
950        let result = CGDisplay::display_count_with_rect(CGRect::new(
951            &CGPoint::new(10., 10.),
952            &CGSize::new(100., 100.),
953        ));
954        assert!(result.is_ok());
955    }
956
957    #[test]
958    fn test_displays_with_rect_0() {
959        let _ = CGDisplay::main();
960
961        let result = CGDisplay::displays_with_rect(
962            CGRect::new(&CGPoint::new(0., 0.), &CGSize::new(100., 100.)),
963            0,
964        );
965        assert!(result.is_ok());
966        let (displays, count) = result.unwrap();
967        assert_eq!(displays.len(), count as usize);
968    }
969
970    #[test]
971    fn test_displays_with_rect_5() {
972        let _ = CGDisplay::main();
973
974        let result = CGDisplay::displays_with_rect(
975            CGRect::new(&CGPoint::new(0., 0.), &CGSize::new(100., 100.)),
976            5,
977        );
978        assert!(result.is_ok());
979        let (displays, count) = result.unwrap();
980        assert_eq!(displays.len(), count as usize);
981    }
982}