Skip to main content

gpui_macos/
gpui_macos.rs

1#![cfg(target_os = "macos")]
2//! macOS platform implementation for GPUI.
3//!
4//! macOS screens have a y axis that goes up from the bottom of the screen and
5//! an origin at the bottom left of the main display.
6
7mod display;
8mod display_link;
9mod events;
10mod keyboard;
11mod pasteboard;
12mod system_notifications;
13
14#[cfg(feature = "screen-capture")]
15mod screen_capture;
16
17use gpui_apple::metal_renderer as renderer;
18
19pub mod metal_renderer {
20    pub use gpui_apple::metal_renderer::{PathRasterizationVertex, PathSprite, SurfaceBounds};
21
22    #[cfg(any(test, feature = "bench-support", feature = "test-support"))]
23    pub use gpui_apple::metal_renderer::MetalHeadlessRenderer;
24}
25
26/// The producer's half of the imported-texture path on macOS. Re-exported here because this is the crate a macOS
27/// application already reaches the platform through, and the facade re-exports it from here.
28pub use gpui_apple::imported_texture::MetalTextureExt;
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 cocoa::{
41    base::{id, nil},
42    foundation::{NSAutoreleasePool, NSNotFound, NSString, NSUInteger},
43};
44
45use objc::runtime::{BOOL, NO, YES};
46use std::{
47    ffi::{CStr, c_char},
48    ops::Range,
49};
50
51pub(crate) use display::*;
52pub(crate) use display_link::*;
53pub(crate) use gpui_apple::{AppleActivity, AppleDispatcher};
54pub(crate) use keyboard::*;
55pub(crate) use platform::*;
56pub(crate) use window::*;
57
58#[cfg(feature = "font-kit")]
59pub(crate) use text_system::*;
60
61pub use platform::MacPlatform;
62
63trait BoolExt {
64    fn to_objc(self) -> BOOL;
65}
66
67impl BoolExt for bool {
68    fn to_objc(self) -> BOOL {
69        if self { YES } else { NO }
70    }
71}
72
73trait NSStringExt {
74    unsafe fn to_str(&self) -> &str;
75}
76
77impl NSStringExt for id {
78    unsafe fn to_str(&self) -> &str {
79        unsafe {
80            let cstr = self.UTF8String();
81            if cstr.is_null() {
82                ""
83            } else {
84                CStr::from_ptr(cstr as *mut c_char).to_str().unwrap()
85            }
86        }
87    }
88}
89
90#[repr(C)]
91#[derive(Copy, Clone, Debug)]
92struct NSRange {
93    pub location: NSUInteger,
94    pub length: NSUInteger,
95}
96
97impl NSRange {
98    fn invalid() -> Self {
99        Self {
100            location: NSNotFound as NSUInteger,
101            length: 0,
102        }
103    }
104
105    fn is_valid(&self) -> bool {
106        self.location != NSNotFound as NSUInteger
107    }
108
109    fn to_range(self) -> Option<Range<usize>> {
110        if self.is_valid() {
111            let start = self.location as usize;
112            let end = start + self.length as usize;
113            Some(start..end)
114        } else {
115            None
116        }
117    }
118}
119
120impl From<Range<usize>> for NSRange {
121    fn from(range: Range<usize>) -> Self {
122        NSRange {
123            location: range.start as NSUInteger,
124            length: range.len() as NSUInteger,
125        }
126    }
127}
128
129unsafe impl objc::Encode for NSRange {
130    fn encode() -> objc::Encoding {
131        let encoding = format!(
132            "{{NSRange={}{}}}",
133            NSUInteger::encode().as_str(),
134            NSUInteger::encode().as_str()
135        );
136        unsafe { objc::Encoding::from_str(&encoding) }
137    }
138}
139
140/// Allow NSString::alloc use here because it sets autorelease
141#[allow(clippy::disallowed_methods)]
142unsafe fn ns_string(string: &str) -> id {
143    unsafe { NSString::alloc(nil).init_str(string).autorelease() }
144}