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