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
30mod platform;
31mod window;
32mod window_appearance;
33
34use cocoa::{
35    base::{id, nil},
36    foundation::{NSAutoreleasePool, NSNotFound, NSString, NSUInteger},
37};
38
39use objc::runtime::{BOOL, NO, YES};
40use std::{
41    ffi::{CStr, c_char},
42    ops::Range,
43};
44
45pub(crate) use display::*;
46pub(crate) use display_link::*;
47pub(crate) use gpui_apple::{AppleActivity, AppleDispatcher};
48pub(crate) use keyboard::*;
49pub(crate) use platform::*;
50pub(crate) use window::*;
51
52pub use platform::MacPlatform;
53
54trait BoolExt {
55    fn to_objc(self) -> BOOL;
56}
57
58impl BoolExt for bool {
59    fn to_objc(self) -> BOOL {
60        if self { YES } else { NO }
61    }
62}
63
64trait NSStringExt {
65    unsafe fn to_str(&self) -> &str;
66}
67
68impl NSStringExt for id {
69    unsafe fn to_str(&self) -> &str {
70        unsafe {
71            let cstr = self.UTF8String();
72            if cstr.is_null() {
73                ""
74            } else {
75                CStr::from_ptr(cstr as *mut c_char).to_str().unwrap()
76            }
77        }
78    }
79}
80
81#[repr(C)]
82#[derive(Copy, Clone, Debug)]
83struct NSRange {
84    pub location: NSUInteger,
85    pub length: NSUInteger,
86}
87
88impl NSRange {
89    fn invalid() -> Self {
90        Self {
91            location: NSNotFound as NSUInteger,
92            length: 0,
93        }
94    }
95
96    fn is_valid(&self) -> bool {
97        self.location != NSNotFound as NSUInteger
98    }
99
100    fn to_range(self) -> Option<Range<usize>> {
101        if self.is_valid() {
102            let start = self.location as usize;
103            let end = start + self.length as usize;
104            Some(start..end)
105        } else {
106            None
107        }
108    }
109}
110
111impl From<Range<usize>> for NSRange {
112    fn from(range: Range<usize>) -> Self {
113        NSRange {
114            location: range.start as NSUInteger,
115            length: range.len() as NSUInteger,
116        }
117    }
118}
119
120unsafe impl objc::Encode for NSRange {
121    fn encode() -> objc::Encoding {
122        let encoding = format!(
123            "{{NSRange={}{}}}",
124            NSUInteger::encode().as_str(),
125            NSUInteger::encode().as_str()
126        );
127        unsafe { objc::Encoding::from_str(&encoding) }
128    }
129}
130
131/// Allow NSString::alloc use here because it sets autorelease
132#[allow(clippy::disallowed_methods)]
133unsafe fn ns_string(string: &str) -> id {
134    unsafe { NSString::alloc(nil).init_str(string).autorelease() }
135}