1//! Macos screen have a y axis that goings up from the bottom of the screen and
2//! an origin at the bottom left of the main display.
3mod dispatcher;
4mod display;
5mod display_link;
6mod events;
7mod keyboard;
8
9#[cfg(feature = "screen-capture")]
10mod screen_capture;
11#[cfg(all(feature = "screen-capture", any(test, feature = "test-support")))]
12pub use screen_capture::{
13 capture_window_screenshot, cv_pixel_buffer_to_rgba_image, screen_capture_frame_to_rgba_image,
14};
15
16#[cfg(not(feature = "macos-blade"))]
17mod metal_atlas;
18#[cfg(not(feature = "macos-blade"))]
19pub mod metal_renderer;
20
21use core_video::image_buffer::CVImageBuffer;
22#[cfg(not(feature = "macos-blade"))]
23use metal_renderer as renderer;
24
25#[cfg(feature = "macos-blade")]
26use crate::platform::blade as renderer;
27
28mod attributed_string;
29
30#[cfg(feature = "font-kit")]
31mod open_type;
32
33#[cfg(feature = "font-kit")]
34mod text_system;
35
36mod platform;
37mod window;
38mod window_appearance;
39
40use crate::{DevicePixels, Pixels, Size, px, size};
41use cocoa::{
42 base::{id, nil},
43 foundation::{NSAutoreleasePool, NSNotFound, NSRect, NSSize, NSString, NSUInteger},
44};
45
46use objc::runtime::{BOOL, NO, YES};
47use std::{
48 ffi::{CStr, c_char},
49 ops::Range,
50};
51
52pub(crate) use dispatcher::*;
53pub(crate) use display::*;
54pub(crate) use display_link::*;
55pub(crate) use keyboard::*;
56pub(crate) use platform::*;
57pub(crate) use window::*;
58
59#[cfg(feature = "font-kit")]
60pub(crate) use text_system::*;
61
62/// A frame of video captured from a screen.
63pub(crate) type PlatformScreenCaptureFrame = CVImageBuffer;
64
65trait BoolExt {
66 fn to_objc(self) -> BOOL;
67}
68
69impl BoolExt for bool {
70 fn to_objc(self) -> BOOL {
71 if self { YES } else { NO }
72 }
73}
74
75trait NSStringExt {
76 unsafe fn to_str(&self) -> &str;
77}
78
79impl NSStringExt for id {
80 unsafe fn to_str(&self) -> &str {
81 unsafe {
82 let cstr = self.UTF8String();
83 if cstr.is_null() {
84 ""
85 } else {
86 CStr::from_ptr(cstr as *mut c_char).to_str().unwrap()
87 }
88 }
89 }
90}
91
92#[repr(C)]
93#[derive(Copy, Clone, Debug)]
94struct NSRange {
95 pub location: NSUInteger,
96 pub length: NSUInteger,
97}
98
99impl NSRange {
100 fn invalid() -> Self {
101 Self {
102 location: NSNotFound as NSUInteger,
103 length: 0,
104 }
105 }
106
107 fn is_valid(&self) -> bool {
108 self.location != NSNotFound as NSUInteger
109 }
110
111 fn to_range(self) -> Option<Range<usize>> {
112 if self.is_valid() {
113 let start = self.location as usize;
114 let end = start + self.length as usize;
115 Some(start..end)
116 } else {
117 None
118 }
119 }
120}
121
122impl From<Range<usize>> for NSRange {
123 fn from(range: Range<usize>) -> Self {
124 NSRange {
125 location: range.start as NSUInteger,
126 length: range.len() as NSUInteger,
127 }
128 }
129}
130
131unsafe impl objc::Encode for NSRange {
132 fn encode() -> objc::Encoding {
133 let encoding = format!(
134 "{{NSRange={}{}}}",
135 NSUInteger::encode().as_str(),
136 NSUInteger::encode().as_str()
137 );
138 unsafe { objc::Encoding::from_str(&encoding) }
139 }
140}
141
142/// Allow NSString::alloc use here because it sets autorelease
143#[allow(clippy::disallowed_methods)]
144unsafe fn ns_string(string: &str) -> id {
145 unsafe { NSString::alloc(nil).init_str(string).autorelease() }
146}
147
148impl From<NSSize> for Size<Pixels> {
149 fn from(value: NSSize) -> Self {
150 Size {
151 width: px(value.width as f32),
152 height: px(value.height as f32),
153 }
154 }
155}
156
157impl From<NSRect> for Size<Pixels> {
158 fn from(rect: NSRect) -> Self {
159 let NSSize { width, height } = rect.size;
160 size(width.into(), height.into())
161 }
162}
163
164impl From<NSRect> for Size<DevicePixels> {
165 fn from(rect: NSRect) -> Self {
166 let NSSize { width, height } = rect.size;
167 size(DevicePixels(width as i32), DevicePixels(height as i32))
168 }
169}