mac.rs

  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 events;
  5mod platform;
  6mod screen;
  7mod window;
  8mod window_appearence;
  9
 10use std::{
 11    ffi::{c_char, CStr, OsStr},
 12    ops::Range,
 13    os::unix::prelude::OsStrExt,
 14    path::PathBuf,
 15};
 16
 17use crate::{px, size, Pixels, Size};
 18use anyhow::anyhow;
 19use cocoa::{
 20    base::{id, nil},
 21    foundation::{NSAutoreleasePool, NSNotFound, NSRect, NSSize, NSString, NSUInteger, NSURL},
 22};
 23use objc::{
 24    msg_send,
 25    runtime::{BOOL, NO, YES},
 26    sel, sel_impl,
 27};
 28pub use platform::*;
 29pub use screen::*;
 30pub use window::*;
 31use window_appearence::*;
 32
 33trait BoolExt {
 34    fn to_objc(self) -> BOOL;
 35}
 36
 37impl BoolExt for bool {
 38    fn to_objc(self) -> BOOL {
 39        if self {
 40            YES
 41        } else {
 42            NO
 43        }
 44    }
 45}
 46
 47#[repr(C)]
 48#[derive(Copy, Clone, Debug)]
 49struct NSRange {
 50    pub location: NSUInteger,
 51    pub length: NSUInteger,
 52}
 53
 54impl NSRange {
 55    fn invalid() -> Self {
 56        Self {
 57            location: NSNotFound as NSUInteger,
 58            length: 0,
 59        }
 60    }
 61
 62    fn is_valid(&self) -> bool {
 63        self.location != NSNotFound as NSUInteger
 64    }
 65
 66    fn to_range(self) -> Option<Range<usize>> {
 67        if self.is_valid() {
 68            let start = self.location as usize;
 69            let end = start + self.length as usize;
 70            Some(start..end)
 71        } else {
 72            None
 73        }
 74    }
 75}
 76
 77impl From<Range<usize>> for NSRange {
 78    fn from(range: Range<usize>) -> Self {
 79        NSRange {
 80            location: range.start as NSUInteger,
 81            length: range.len() as NSUInteger,
 82        }
 83    }
 84}
 85
 86unsafe impl objc::Encode for NSRange {
 87    fn encode() -> objc::Encoding {
 88        let encoding = format!(
 89            "{{NSRange={}{}}}",
 90            NSUInteger::encode().as_str(),
 91            NSUInteger::encode().as_str()
 92        );
 93        unsafe { objc::Encoding::from_str(&encoding) }
 94    }
 95}
 96
 97unsafe fn ns_string(string: &str) -> id {
 98    NSString::alloc(nil).init_str(string).autorelease()
 99}
100
101impl From<NSSize> for Size<Pixels> {
102    fn from(value: NSSize) -> Self {
103        Size {
104            width: px(value.width as f32),
105            height: px(value.height as f32),
106        }
107    }
108}
109
110pub trait NSRectExt {
111    fn size(&self) -> Size<Pixels>;
112    fn intersects(&self, other: Self) -> bool;
113}
114
115impl NSRectExt for NSRect {
116    fn size(&self) -> Size<Pixels> {
117        size(px(self.size.width as f32), px(self.size.height as f32))
118    }
119
120    fn intersects(&self, other: Self) -> bool {
121        self.size.width > 0.
122            && self.size.height > 0.
123            && other.size.width > 0.
124            && other.size.height > 0.
125            && self.origin.x <= other.origin.x + other.size.width
126            && self.origin.x + self.size.width >= other.origin.x
127            && self.origin.y <= other.origin.y + other.size.height
128            && self.origin.y + self.size.height >= other.origin.y
129    }
130}
131
132unsafe fn ns_url_to_path(url: id) -> crate::Result<PathBuf> {
133    let path: *mut c_char = msg_send![url, fileSystemRepresentation];
134    if path.is_null() {
135        Err(anyhow!(
136            "url is not a file path: {}",
137            CStr::from_ptr(url.absoluteString().UTF8String()).to_string_lossy()
138        ))
139    } else {
140        Ok(PathBuf::from(OsStr::from_bytes(
141            CStr::from_ptr(path).to_bytes(),
142        )))
143    }
144}