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