Refactor macOS to use new objc2 features (#2465)

* Remove UnownedWindow::inner_rect

* Refactor custom view to use much less `unsafe`

The compiler fence is safe to get rid of now since `interpretKeyEvents` takes `&mut self`

* Refactor Window to use much less unsafe

* Refactor NSApplication usage to have much less unsafe

* Remove cocoa dependency

* Enable `deny(unsafe_op_in_unsafe_fn)` on macOS

Also re-enable clippy `let_unit_value` lint

* Remove #[macro_use] on macOS

* Refactor window delegate to use much less unsafe
This commit is contained in:
Mads Marquart 2022-09-08 16:45:29 +02:00 committed by GitHub
parent 05dd31b8ea
commit 340f951d10
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 2756 additions and 2335 deletions

View file

@ -1,7 +1,10 @@
use objc2::foundation::NSObject;
use objc2::{extern_class, ClassType};
use objc2::foundation::{MainThreadMarker, NSArray, NSInteger, NSObject, NSUInteger};
use objc2::rc::{Id, Shared};
use objc2::runtime::Object;
use objc2::{extern_class, extern_methods, msg_send_id, ClassType};
use objc2::{Encode, Encoding};
use super::NSResponder;
use super::{NSEvent, NSMenu, NSResponder, NSWindow};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
@ -12,3 +15,121 @@ extern_class!(
type Super = NSResponder;
}
);
pub(crate) fn NSApp() -> Id<NSApplication, Shared> {
// TODO: Only allow access from main thread
NSApplication::shared(unsafe { MainThreadMarker::new_unchecked() })
}
extern_methods!(
unsafe impl NSApplication {
/// This can only be called on the main thread since it may initialize
/// the application and since it's parameters may be changed by the main
/// thread at any time (hence it is only safe to access on the main thread).
pub fn shared(_mtm: MainThreadMarker) -> Id<Self, Shared> {
let app: Option<_> = unsafe { msg_send_id![Self::class(), sharedApplication] };
// SAFETY: `sharedApplication` always initializes the app if it isn't already
unsafe { app.unwrap_unchecked() }
}
pub fn currentEvent(&self) -> Option<Id<NSEvent, Shared>> {
unsafe { msg_send_id![self, currentEvent] }
}
#[sel(postEvent:atStart:)]
pub fn postEvent_atStart(&self, event: &NSEvent, front_of_queue: bool);
#[sel(presentationOptions)]
pub fn presentationOptions(&self) -> NSApplicationPresentationOptions;
pub fn windows(&self) -> Id<NSArray<NSWindow, Shared>, Shared> {
unsafe { msg_send_id![self, windows] }
}
pub fn keyWindow(&self) -> Option<Id<NSWindow, Shared>> {
unsafe { msg_send_id![self, keyWindow] }
}
// TODO: NSApplicationDelegate
#[sel(setDelegate:)]
pub fn setDelegate(&self, delegate: &Object);
#[sel(setPresentationOptions:)]
pub fn setPresentationOptions(&self, options: NSApplicationPresentationOptions);
#[sel(hide:)]
pub fn hide(&self, sender: Option<&Object>);
#[sel(orderFrontCharacterPalette:)]
#[allow(dead_code)]
pub fn orderFrontCharacterPalette(&self, sender: Option<&Object>);
#[sel(hideOtherApplications:)]
pub fn hideOtherApplications(&self, sender: Option<&Object>);
#[sel(stop:)]
pub fn stop(&self, sender: Option<&Object>);
#[sel(activateIgnoringOtherApps:)]
pub fn activateIgnoringOtherApps(&self, ignore: bool);
#[sel(requestUserAttention:)]
pub fn requestUserAttention(&self, type_: NSRequestUserAttentionType) -> NSInteger;
#[sel(setActivationPolicy:)]
pub fn setActivationPolicy(&self, policy: NSApplicationActivationPolicy) -> bool;
#[sel(setMainMenu:)]
pub fn setMainMenu(&self, menu: &NSMenu);
#[sel(run)]
pub unsafe fn run(&self);
}
);
#[allow(dead_code)]
#[repr(isize)] // NSInteger
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum NSApplicationActivationPolicy {
NSApplicationActivationPolicyRegular = 0,
NSApplicationActivationPolicyAccessory = 1,
NSApplicationActivationPolicyProhibited = 2,
NSApplicationActivationPolicyERROR = -1,
}
unsafe impl Encode for NSApplicationActivationPolicy {
const ENCODING: Encoding = NSInteger::ENCODING;
}
bitflags! {
pub struct NSApplicationPresentationOptions: NSUInteger {
const NSApplicationPresentationDefault = 0;
const NSApplicationPresentationAutoHideDock = 1 << 0;
const NSApplicationPresentationHideDock = 1 << 1;
const NSApplicationPresentationAutoHideMenuBar = 1 << 2;
const NSApplicationPresentationHideMenuBar = 1 << 3;
const NSApplicationPresentationDisableAppleMenu = 1 << 4;
const NSApplicationPresentationDisableProcessSwitching = 1 << 5;
const NSApplicationPresentationDisableForceQuit = 1 << 6;
const NSApplicationPresentationDisableSessionTermination = 1 << 7;
const NSApplicationPresentationDisableHideApplication = 1 << 8;
const NSApplicationPresentationDisableMenuBarTransparency = 1 << 9;
const NSApplicationPresentationFullScreen = 1 << 10;
const NSApplicationPresentationAutoHideToolbar = 1 << 11;
}
}
unsafe impl Encode for NSApplicationPresentationOptions {
const ENCODING: Encoding = NSUInteger::ENCODING;
}
#[repr(usize)] // NSUInteger
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum NSRequestUserAttentionType {
NSCriticalRequest = 0,
NSInformationalRequest = 10,
}
unsafe impl Encode for NSRequestUserAttentionType {
const ENCODING: Encoding = NSUInteger::ENCODING;
}

View file

@ -0,0 +1,14 @@
use objc2::foundation::NSObject;
use objc2::{extern_class, ClassType};
use super::{NSControl, NSResponder, NSView};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct NSButton;
unsafe impl ClassType for NSButton {
#[inherits(NSView, NSResponder, NSObject)]
type Super = NSControl;
}
);

View file

@ -0,0 +1,28 @@
use objc2::foundation::NSObject;
use objc2::rc::{Id, Shared};
use objc2::{extern_class, extern_methods, msg_send_id, ClassType};
extern_class!(
/// An object that stores color data and sometimes opacity (alpha value).
///
/// <https://developer.apple.com/documentation/appkit/nscolor?language=objc>
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct NSColor;
unsafe impl ClassType for NSColor {
type Super = NSObject;
}
);
// SAFETY: Documentation clearly states:
// > Color objects are immutable and thread-safe
unsafe impl Send for NSColor {}
unsafe impl Sync for NSColor {}
extern_methods!(
unsafe impl NSColor {
pub fn clear() -> Id<Self, Shared> {
unsafe { msg_send_id![Self::class(), clearColor] }
}
}
);

View file

@ -0,0 +1,14 @@
use objc2::foundation::NSObject;
use objc2::{extern_class, ClassType};
use super::{NSResponder, NSView};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct NSControl;
unsafe impl ClassType for NSControl {
#[inherits(NSResponder, NSObject)]
type Super = NSView;
}
);

View file

@ -3,7 +3,7 @@ use once_cell::sync::Lazy;
use objc2::foundation::{NSData, NSDictionary, NSNumber, NSObject, NSPoint, NSString};
use objc2::rc::{DefaultId, Id, Shared};
use objc2::runtime::Sel;
use objc2::{extern_class, extern_methods, msg_send_id, ns_string, ClassType};
use objc2::{extern_class, extern_methods, msg_send_id, ns_string, sel, ClassType};
use super::NSImage;
use crate::window::CursorIcon;

View file

@ -0,0 +1,234 @@
use std::os::raw::c_ushort;
use objc2::encode::{Encode, Encoding};
use objc2::foundation::{
CGFloat, NSCopying, NSInteger, NSObject, NSPoint, NSString, NSTimeInterval, NSUInteger,
};
use objc2::rc::{Id, Shared};
use objc2::{extern_class, extern_methods, msg_send_id, ClassType};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct NSEvent;
unsafe impl ClassType for NSEvent {
type Super = NSObject;
}
);
// > Safely handled only on the same thread, whether that be the main thread
// > or a secondary thread; otherwise you run the risk of having events get
// > out of sequence.
// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CocoaFundamentals/AddingBehaviortoaCocoaProgram/AddingBehaviorCocoa.html#//apple_ref/doc/uid/TP40002974-CH5-SW47>
// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html#//apple_ref/doc/uid/10000057i-CH12-123383>
extern_methods!(
unsafe impl NSEvent {
unsafe fn otherEventWithType(
type_: NSEventType,
location: NSPoint,
flags: NSEventModifierFlags,
time: NSTimeInterval,
window_num: NSInteger,
context: Option<&NSObject>, // NSGraphicsContext
subtype: NSEventSubtype,
data1: NSInteger,
data2: NSInteger,
) -> Id<Self, Shared> {
unsafe {
msg_send_id![
Self::class(),
otherEventWithType: type_,
location: location,
modifierFlags: flags,
timestamp: time,
windowNumber: window_num,
context: context,
subtype: subtype,
data1: data1,
data2: data2,
]
}
}
pub fn dummy() -> Id<Self, Shared> {
unsafe {
Self::otherEventWithType(
NSEventType::NSApplicationDefined,
NSPoint::new(0.0, 0.0),
NSEventModifierFlags::empty(),
0.0,
0,
None,
NSEventSubtype::NSWindowExposedEventType,
0,
0,
)
}
}
#[sel(locationInWindow)]
pub fn locationInWindow(&self) -> NSPoint;
// TODO: MainThreadMarker
#[sel(pressedMouseButtons)]
pub fn pressedMouseButtons() -> NSUInteger;
#[sel(modifierFlags)]
pub fn modifierFlags(&self) -> NSEventModifierFlags;
#[sel(type)]
pub fn type_(&self) -> NSEventType;
// In AppKit, `keyCode` refers to the position (scancode) of a key rather than its character,
// and there is no easy way to navtively retrieve the layout-dependent character.
// In winit, we use keycode to refer to the key's character, and so this function aligns
// AppKit's terminology with ours.
#[sel(keyCode)]
pub fn scancode(&self) -> c_ushort;
#[sel(magnification)]
pub fn magnification(&self) -> CGFloat;
#[sel(phase)]
pub fn phase(&self) -> NSEventPhase;
#[sel(momentumPhase)]
pub fn momentumPhase(&self) -> NSEventPhase;
#[sel(deltaX)]
pub fn deltaX(&self) -> CGFloat;
#[sel(deltaY)]
pub fn deltaY(&self) -> CGFloat;
#[sel(buttonNumber)]
pub fn buttonNumber(&self) -> NSInteger;
#[sel(scrollingDeltaX)]
pub fn scrollingDeltaX(&self) -> CGFloat;
#[sel(scrollingDeltaY)]
pub fn scrollingDeltaY(&self) -> CGFloat;
#[sel(hasPreciseScrollingDeltas)]
pub fn hasPreciseScrollingDeltas(&self) -> bool;
#[sel(rotation)]
pub fn rotation(&self) -> f32;
#[sel(pressure)]
pub fn pressure(&self) -> f32;
#[sel(stage)]
pub fn stage(&self) -> NSInteger;
pub fn characters(&self) -> Option<Id<NSString, Shared>> {
unsafe { msg_send_id![self, characters] }
}
pub fn charactersIgnoringModifiers(&self) -> Option<Id<NSString, Shared>> {
unsafe { msg_send_id![self, charactersIgnoringModifiers] }
}
}
);
unsafe impl NSCopying for NSEvent {
type Ownership = Shared;
type Output = NSEvent;
}
bitflags! {
pub struct NSEventModifierFlags: NSUInteger {
const NSAlphaShiftKeyMask = 1 << 16;
const NSShiftKeyMask = 1 << 17;
const NSControlKeyMask = 1 << 18;
const NSAlternateKeyMask = 1 << 19;
const NSCommandKeyMask = 1 << 20;
const NSNumericPadKeyMask = 1 << 21;
const NSHelpKeyMask = 1 << 22;
const NSFunctionKeyMask = 1 << 23;
const NSDeviceIndependentModifierFlagsMask = 0xffff0000;
}
}
unsafe impl Encode for NSEventModifierFlags {
const ENCODING: Encoding = NSUInteger::ENCODING;
}
bitflags! {
pub struct NSEventPhase: NSUInteger {
const NSEventPhaseNone = 0;
const NSEventPhaseBegan = 0x1 << 0;
const NSEventPhaseStationary = 0x1 << 1;
const NSEventPhaseChanged = 0x1 << 2;
const NSEventPhaseEnded = 0x1 << 3;
const NSEventPhaseCancelled = 0x1 << 4;
const NSEventPhaseMayBegin = 0x1 << 5;
}
}
unsafe impl Encode for NSEventPhase {
const ENCODING: Encoding = NSUInteger::ENCODING;
}
#[allow(dead_code)]
#[repr(i16)] // short
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum NSEventSubtype {
// TODO: Not sure what these values are
// NSMouseEventSubtype = NX_SUBTYPE_DEFAULT,
// NSTabletPointEventSubtype = NX_SUBTYPE_TABLET_POINT,
// NSTabletProximityEventSubtype = NX_SUBTYPE_TABLET_PROXIMITY
// NSTouchEventSubtype = NX_SUBTYPE_MOUSE_TOUCH,
NSWindowExposedEventType = 0,
NSApplicationActivatedEventType = 1,
NSApplicationDeactivatedEventType = 2,
NSWindowMovedEventType = 4,
NSScreenChangedEventType = 8,
NSAWTEventType = 16,
}
unsafe impl Encode for NSEventSubtype {
const ENCODING: Encoding = i16::ENCODING;
}
#[allow(dead_code)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(usize)] // NSUInteger
pub enum NSEventType {
NSLeftMouseDown = 1,
NSLeftMouseUp = 2,
NSRightMouseDown = 3,
NSRightMouseUp = 4,
NSMouseMoved = 5,
NSLeftMouseDragged = 6,
NSRightMouseDragged = 7,
NSMouseEntered = 8,
NSMouseExited = 9,
NSKeyDown = 10,
NSKeyUp = 11,
NSFlagsChanged = 12,
NSAppKitDefined = 13,
NSSystemDefined = 14,
NSApplicationDefined = 15,
NSPeriodic = 16,
NSCursorUpdate = 17,
NSScrollWheel = 22,
NSTabletPoint = 23,
NSTabletProximity = 24,
NSOtherMouseDown = 25,
NSOtherMouseUp = 26,
NSOtherMouseDragged = 27,
NSEventTypeGesture = 29,
NSEventTypeMagnify = 30,
NSEventTypeSwipe = 31,
NSEventTypeRotate = 18,
NSEventTypeBeginGesture = 19,
NSEventTypeEndGesture = 20,
NSEventTypePressure = 34,
}
unsafe impl Encode for NSEventType {
const ENCODING: Encoding = NSUInteger::ENCODING;
}

View file

@ -0,0 +1,25 @@
use objc2::foundation::NSObject;
use objc2::rc::{Id, Shared};
use objc2::{extern_class, extern_methods, msg_send_id, ClassType};
use super::NSMenuItem;
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct NSMenu;
unsafe impl ClassType for NSMenu {
type Super = NSObject;
}
);
extern_methods!(
unsafe impl NSMenu {
pub fn new() -> Id<Self, Shared> {
unsafe { msg_send_id![Self::class(), new] }
}
#[sel(addItem:)]
pub fn addItem(&self, item: &NSMenuItem);
}
);

View file

@ -0,0 +1,48 @@
use objc2::foundation::{NSObject, NSString};
use objc2::rc::{Id, Shared};
use objc2::runtime::Sel;
use objc2::{extern_class, extern_methods, msg_send_id, ClassType};
use super::{NSEventModifierFlags, NSMenu};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct NSMenuItem;
unsafe impl ClassType for NSMenuItem {
type Super = NSObject;
}
);
extern_methods!(
unsafe impl NSMenuItem {
pub fn new() -> Id<Self, Shared> {
unsafe { msg_send_id![Self::class(), new] }
}
pub fn newWithTitle(
title: &NSString,
action: Sel,
key_equivalent: &NSString,
) -> Id<Self, Shared> {
unsafe {
msg_send_id![
msg_send_id![Self::class(), alloc],
initWithTitle: title,
action: action,
keyEquivalent: key_equivalent,
]
}
}
pub fn separatorItem() -> Id<Self, Shared> {
unsafe { msg_send_id![Self::class(), separatorItem] }
}
#[sel(setKeyEquivalentModifierMask:)]
pub fn setKeyEquivalentModifierMask(&self, mask: NSEventModifierFlags);
#[sel(setSubmenu:)]
pub fn setSubmenu(&self, submenu: &NSMenu);
}
);

View file

@ -1,18 +1,59 @@
#![deny(unsafe_op_in_unsafe_fn)]
//! Safe bindings for the AppKit framework.
//!
//! These are split out from the rest of `winit` to make safety easier to review.
//! In the future, these should probably live in another crate like `cacao`.
//!
//! TODO: Main thread safety.
// Objective-C methods have different conventions, and it's much easier to
// understand if we just use the same names
#![allow(non_snake_case)]
#![allow(clippy::too_many_arguments)]
#![allow(clippy::enum_variant_names)]
#![allow(non_upper_case_globals)]
mod application;
mod button;
mod color;
mod control;
mod cursor;
mod event;
mod image;
mod menu;
mod menu_item;
mod pasteboard;
mod responder;
mod screen;
mod text_input_context;
mod version;
mod view;
mod window;
pub(crate) use self::application::NSApplication;
pub(crate) use self::application::{
NSApp, NSApplication, NSApplicationActivationPolicy, NSApplicationPresentationOptions,
NSRequestUserAttentionType,
};
pub(crate) use self::button::NSButton;
pub(crate) use self::color::NSColor;
pub(crate) use self::control::NSControl;
pub(crate) use self::cursor::NSCursor;
#[allow(unused_imports)]
pub(crate) use self::event::{
NSEvent, NSEventModifierFlags, NSEventPhase, NSEventSubtype, NSEventType,
};
pub(crate) use self::image::NSImage;
pub(crate) use self::menu::NSMenu;
pub(crate) use self::menu_item::NSMenuItem;
pub(crate) use self::pasteboard::{NSFilenamesPboardType, NSPasteboard, NSPasteboardType};
pub(crate) use self::responder::NSResponder;
pub(crate) use self::view::NSView;
pub(crate) use self::window::NSWindow;
#[allow(unused_imports)]
pub(crate) use self::screen::{NSDeviceDescriptionKey, NSScreen};
pub(crate) use self::text_input_context::NSTextInputContext;
pub(crate) use self::version::NSAppKitVersion;
pub(crate) use self::view::{NSTrackingRectTag, NSView};
pub(crate) use self::window::{
NSBackingStoreType, NSWindow, NSWindowButton, NSWindowLevel, NSWindowOcclusionState,
NSWindowStyleMask, NSWindowTitleVisibility,
};
#[link(name = "AppKit", kind = "framework")]
extern "C" {}

View file

@ -0,0 +1,26 @@
use objc2::foundation::{NSObject, NSString};
use objc2::rc::{Id, Shared};
use objc2::{extern_class, extern_methods, msg_send_id, ClassType};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct NSPasteboard;
unsafe impl ClassType for NSPasteboard {
type Super = NSObject;
}
);
extern_methods!(
unsafe impl NSPasteboard {
pub fn propertyListForType(&self, type_: &NSPasteboardType) -> Id<NSObject, Shared> {
unsafe { msg_send_id![self, propertyListForType: type_] }
}
}
);
pub type NSPasteboardType = NSString;
extern "C" {
pub static NSFilenamesPboardType: &'static NSPasteboardType;
}

View file

@ -1,5 +1,8 @@
use objc2::foundation::NSObject;
use objc2::{extern_class, ClassType};
use objc2::foundation::{NSArray, NSObject};
use objc2::rc::Shared;
use objc2::{extern_class, extern_methods, ClassType};
use super::NSEvent;
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
@ -9,3 +12,13 @@ extern_class!(
type Super = NSObject;
}
);
// Documented as "Thread-Unsafe".
extern_methods!(
unsafe impl NSResponder {
// TODO: Allow "immutably" on main thread
#[sel(interpretKeyEvents:)]
pub unsafe fn interpretKeyEvents(&mut self, events: &NSArray<NSEvent, Shared>);
}
);

View file

@ -0,0 +1,64 @@
use objc2::foundation::{CGFloat, NSArray, NSDictionary, NSNumber, NSObject, NSRect, NSString};
use objc2::rc::{Id, Shared};
use objc2::runtime::Object;
use objc2::{extern_class, extern_methods, msg_send_id, ns_string, ClassType};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct NSScreen;
unsafe impl ClassType for NSScreen {
type Super = NSObject;
}
);
// TODO: Main thread marker!
extern_methods!(
unsafe impl NSScreen {
/// The application object must have been created.
pub fn main() -> Option<Id<Self, Shared>> {
unsafe { msg_send_id![Self::class(), mainScreen] }
}
/// The application object must have been created.
pub fn screens() -> Id<NSArray<Self, Shared>, Shared> {
unsafe { msg_send_id![Self::class(), screens] }
}
#[sel(frame)]
pub fn frame(&self) -> NSRect;
#[sel(visibleFrame)]
pub fn visibleFrame(&self) -> NSRect;
pub fn deviceDescription(
&self,
) -> Id<NSDictionary<NSDeviceDescriptionKey, Object>, Shared> {
unsafe { msg_send_id![self, deviceDescription] }
}
pub fn display_id(&self) -> u32 {
let device_description = self.deviceDescription();
// Retrieve the CGDirectDisplayID associated with this screen
//
// SAFETY: The value from @"NSScreenNumber" in deviceDescription is guaranteed
// to be an NSNumber. See documentation for `deviceDescription` for details:
// <https://developer.apple.com/documentation/appkit/nsscreen/1388360-devicedescription?language=objc>
let obj = device_description
.get(ns_string!("NSScreenNumber"))
.expect("failed getting screen display id from device description");
let obj: *const Object = obj;
let obj: *const NSNumber = obj.cast();
let obj: &NSNumber = unsafe { &*obj };
obj.as_u32()
}
#[sel(backingScaleFactor)]
pub fn backingScaleFactor(&self) -> CGFloat;
}
);
pub type NSDeviceDescriptionKey = NSString;

View file

@ -0,0 +1,31 @@
use objc2::foundation::{NSObject, NSString};
use objc2::rc::{Id, Shared};
use objc2::{extern_class, extern_methods, msg_send_id, ClassType};
type NSTextInputSourceIdentifier = NSString;
extern_class!(
/// Main-Thread-Only!
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct NSTextInputContext;
unsafe impl ClassType for NSTextInputContext {
type Super = NSObject;
}
);
extern_methods!(
unsafe impl NSTextInputContext {
#[sel(invalidateCharacterCoordinates)]
pub fn invalidateCharacterCoordinates(&self);
#[sel(discardMarkedText)]
pub fn discardMarkedText(&self);
pub fn selectedKeyboardInputSource(
&self,
) -> Option<Id<NSTextInputSourceIdentifier, Shared>> {
unsafe { msg_send_id![self, selectedKeyboardInputSource] }
}
}
);

View file

@ -0,0 +1,62 @@
#[repr(transparent)]
#[derive(PartialEq, PartialOrd, Debug, Clone, Copy)]
pub struct NSAppKitVersion(f64);
#[allow(dead_code)]
#[allow(non_upper_case_globals)]
impl NSAppKitVersion {
pub fn current() -> Self {
extern "C" {
static NSAppKitVersionNumber: NSAppKitVersion;
}
unsafe { NSAppKitVersionNumber }
}
pub fn floor(self) -> Self {
Self(self.0.floor())
}
pub const NSAppKitVersionNumber10_0: Self = Self(577.0);
pub const NSAppKitVersionNumber10_1: Self = Self(620.0);
pub const NSAppKitVersionNumber10_2: Self = Self(663.0);
pub const NSAppKitVersionNumber10_2_3: Self = Self(663.6);
pub const NSAppKitVersionNumber10_3: Self = Self(743.0);
pub const NSAppKitVersionNumber10_3_2: Self = Self(743.14);
pub const NSAppKitVersionNumber10_3_3: Self = Self(743.2);
pub const NSAppKitVersionNumber10_3_5: Self = Self(743.24);
pub const NSAppKitVersionNumber10_3_7: Self = Self(743.33);
pub const NSAppKitVersionNumber10_3_9: Self = Self(743.36);
pub const NSAppKitVersionNumber10_4: Self = Self(824.0);
pub const NSAppKitVersionNumber10_4_1: Self = Self(824.1);
pub const NSAppKitVersionNumber10_4_3: Self = Self(824.23);
pub const NSAppKitVersionNumber10_4_4: Self = Self(824.33);
pub const NSAppKitVersionNumber10_4_7: Self = Self(824.41);
pub const NSAppKitVersionNumber10_5: Self = Self(949.0);
pub const NSAppKitVersionNumber10_5_2: Self = Self(949.27);
pub const NSAppKitVersionNumber10_5_3: Self = Self(949.33);
pub const NSAppKitVersionNumber10_6: Self = Self(1038.0);
pub const NSAppKitVersionNumber10_7: Self = Self(1138.0);
pub const NSAppKitVersionNumber10_7_2: Self = Self(1138.23);
pub const NSAppKitVersionNumber10_7_3: Self = Self(1138.32);
pub const NSAppKitVersionNumber10_7_4: Self = Self(1138.47);
pub const NSAppKitVersionNumber10_8: Self = Self(1187.0);
pub const NSAppKitVersionNumber10_9: Self = Self(1265.0);
pub const NSAppKitVersionNumber10_10: Self = Self(1343.0);
pub const NSAppKitVersionNumber10_10_2: Self = Self(1344.0);
pub const NSAppKitVersionNumber10_10_3: Self = Self(1347.0);
pub const NSAppKitVersionNumber10_10_4: Self = Self(1348.0);
pub const NSAppKitVersionNumber10_10_5: Self = Self(1348.0);
pub const NSAppKitVersionNumber10_10_Max: Self = Self(1349.0);
pub const NSAppKitVersionNumber10_11: Self = Self(1404.0);
pub const NSAppKitVersionNumber10_11_1: Self = Self(1404.13);
pub const NSAppKitVersionNumber10_11_2: Self = Self(1404.34);
pub const NSAppKitVersionNumber10_11_3: Self = Self(1404.34);
pub const NSAppKitVersionNumber10_12: Self = Self(1504.0);
pub const NSAppKitVersionNumber10_12_1: Self = Self(1504.60);
pub const NSAppKitVersionNumber10_12_2: Self = Self(1504.76);
pub const NSAppKitVersionNumber10_13: Self = Self(1561.0);
pub const NSAppKitVersionNumber10_13_1: Self = Self(1561.1);
pub const NSAppKitVersionNumber10_13_2: Self = Self(1561.2);
pub const NSAppKitVersionNumber10_13_4: Self = Self(1561.4);
}

View file

@ -1,7 +1,13 @@
use objc2::foundation::{NSObject, NSRect};
use objc2::{extern_class, extern_methods, ClassType};
use std::ffi::c_void;
use std::num::NonZeroIsize;
use std::ptr;
use super::{NSCursor, NSResponder};
use objc2::foundation::{NSObject, NSPoint, NSRect};
use objc2::rc::{Id, Shared};
use objc2::runtime::Object;
use objc2::{extern_class, extern_methods, msg_send_id, ClassType};
use super::{NSCursor, NSResponder, NSTextInputContext};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
@ -13,16 +19,77 @@ extern_class!(
}
);
// Documented as "Main Thread Only".
// > generally thread safe, although operations on views such as creating,
// > resizing, and moving should happen on the main thread.
// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CocoaFundamentals/AddingBehaviortoaCocoaProgram/AddingBehaviorCocoa.html#//apple_ref/doc/uid/TP40002974-CH5-SW47>
//
// > If you want to use a thread to draw to a view, bracket all drawing code
// > between the lockFocusIfCanDraw and unlockFocus methods of NSView.
// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html#//apple_ref/doc/uid/10000057i-CH12-123351-BBCFIIEB>
extern_methods!(
/// Getter methods
unsafe impl NSView {
#[sel(frame)]
pub fn frame(&self) -> NSRect;
#[sel(bounds)]
pub fn bounds(&self) -> NSRect;
pub fn inputContext(
&self,
// _mtm: MainThreadMarker,
) -> Option<Id<NSTextInputContext, Shared>> {
unsafe { msg_send_id![self, inputContext] }
}
#[sel(visibleRect)]
pub fn visibleRect(&self) -> NSRect;
#[sel(hasMarkedText)]
pub fn hasMarkedText(&self) -> bool;
#[sel(convertPoint:fromView:)]
pub fn convertPoint_fromView(&self, point: NSPoint, view: Option<&NSView>) -> NSPoint;
}
unsafe impl NSView {
#[sel(setWantsBestResolutionOpenGLSurface:)]
pub fn setWantsBestResolutionOpenGLSurface(&self, value: bool);
#[sel(setWantsLayer:)]
pub fn setWantsLayer(&self, wants_layer: bool);
#[sel(setPostsFrameChangedNotifications:)]
pub fn setPostsFrameChangedNotifications(&mut self, value: bool);
#[sel(removeTrackingRect:)]
pub fn removeTrackingRect(&self, tag: NSTrackingRectTag);
#[sel(addTrackingRect:owner:userData:assumeInside:)]
unsafe fn inner_addTrackingRect(
&self,
rect: NSRect,
owner: &Object,
user_data: *mut c_void,
assume_inside: bool,
) -> Option<NSTrackingRectTag>;
pub fn add_tracking_rect(&self, rect: NSRect, assume_inside: bool) -> NSTrackingRectTag {
// SAFETY: The user data is NULL, so it is valid
unsafe { self.inner_addTrackingRect(rect, self, ptr::null_mut(), assume_inside) }
.expect("failed creating tracking rect")
}
#[sel(addCursorRect:cursor:)]
// NSCursor safe to take by shared reference since it is already immutable
pub fn addCursorRect(&self, rect: NSRect, cursor: &NSCursor);
#[sel(setHidden:)]
pub fn setHidden(&self, hidden: bool);
}
);
/// <https://developer.apple.com/documentation/appkit/nstrackingrecttag?language=objc>
pub type NSTrackingRectTag = NonZeroIsize; // NSInteger, but non-zero!

View file

@ -1,9 +1,15 @@
use objc2::foundation::NSObject;
use objc2::{extern_class, ClassType};
use objc2::encode::{Encode, Encoding};
use objc2::foundation::{
CGFloat, NSArray, NSInteger, NSObject, NSPoint, NSRect, NSSize, NSString, NSUInteger,
};
use objc2::rc::{Id, Shared};
use objc2::runtime::Object;
use objc2::{extern_class, extern_methods, msg_send_id, ClassType};
use super::NSResponder;
use super::{NSButton, NSColor, NSEvent, NSPasteboardType, NSResponder, NSScreen, NSView};
extern_class!(
/// Main-Thread-Only!
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct NSWindow;
@ -12,3 +18,314 @@ extern_class!(
type Super = NSResponder;
}
);
// Documented as "Main Thread Only", but:
// > Thread safe in that you can create and manage them on a secondary thread.
// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CocoaFundamentals/AddingBehaviortoaCocoaProgram/AddingBehaviorCocoa.html#//apple_ref/doc/uid/TP40002974-CH5-SW47>
// <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html#//apple_ref/doc/uid/10000057i-CH12-123364>
//
// So could in theory be `Send`, and perhaps also `Sync` - but we would like
// interior mutability on windows, since that's just much easier, and in that
// case, they can't be!
extern_methods!(
unsafe impl NSWindow {
#[sel(frame)]
pub fn frame(&self) -> NSRect;
#[sel(backingScaleFactor)]
pub fn backingScaleFactor(&self) -> CGFloat;
pub fn contentView(&self) -> Id<NSView, Shared> {
unsafe { msg_send_id![self, contentView] }
}
#[sel(setContentView:)]
pub fn setContentView(&self, view: &NSView);
#[sel(setInitialFirstResponder:)]
pub fn setInitialFirstResponder(&self, view: &NSView);
#[sel(makeFirstResponder:)]
#[must_use]
pub fn makeFirstResponder(&self, responder: Option<&NSResponder>) -> bool;
#[sel(contentRectForFrameRect:)]
pub fn contentRectForFrameRect(&self, windowFrame: NSRect) -> NSRect;
pub fn screen(&self) -> Option<Id<NSScreen, Shared>> {
unsafe { msg_send_id![self, screen] }
}
#[sel(setContentSize:)]
pub fn setContentSize(&self, contentSize: NSSize);
#[sel(setFrameTopLeftPoint:)]
pub fn setFrameTopLeftPoint(&self, point: NSPoint);
#[sel(setMinSize:)]
pub fn setMinSize(&self, minSize: NSSize);
#[sel(setMaxSize:)]
pub fn setMaxSize(&self, maxSize: NSSize);
#[sel(setResizeIncrements:)]
pub fn setResizeIncrements(&self, increments: NSSize);
#[sel(contentResizeIncrements)]
pub fn contentResizeIncrements(&self) -> NSSize;
#[sel(setContentResizeIncrements:)]
pub fn setContentResizeIncrements(&self, increments: NSSize);
#[sel(setFrame:display:)]
pub fn setFrame_display(&self, frameRect: NSRect, flag: bool);
#[sel(setMovable:)]
pub fn setMovable(&self, movable: bool);
#[sel(setOpaque:)]
pub fn setOpaque(&self, opaque: bool);
#[sel(hasShadow)]
pub fn hasShadow(&self) -> bool;
#[sel(setHasShadow:)]
pub fn setHasShadow(&self, has_shadow: bool);
#[sel(setIgnoresMouseEvents:)]
pub fn setIgnoresMouseEvents(&self, ignores: bool);
#[sel(setBackgroundColor:)]
pub fn setBackgroundColor(&self, color: &NSColor);
#[sel(styleMask)]
pub fn styleMask(&self) -> NSWindowStyleMask;
#[sel(setStyleMask:)]
pub fn setStyleMask(&self, mask: NSWindowStyleMask);
#[sel(registerForDraggedTypes:)]
pub fn registerForDraggedTypes(&self, types: &NSArray<NSPasteboardType>);
#[sel(makeKeyAndOrderFront:)]
pub fn makeKeyAndOrderFront(&self, sender: Option<&Object>);
#[sel(miniaturize:)]
pub fn miniaturize(&self, sender: Option<&Object>);
#[sel(sender:)]
pub fn deminiaturize(&self, sender: Option<&Object>);
#[sel(toggleFullScreen:)]
pub fn toggleFullScreen(&self, sender: Option<&Object>);
#[sel(orderOut:)]
pub fn orderOut(&self, sender: Option<&Object>);
#[sel(zoom:)]
pub fn zoom(&self, sender: Option<&Object>);
#[sel(selectNextKeyView:)]
pub fn selectNextKeyView(&self, sender: Option<&Object>);
#[sel(selectPreviousKeyView:)]
pub fn selectPreviousKeyView(&self, sender: Option<&Object>);
pub fn firstResponder(&self) -> Option<Id<NSResponder, Shared>> {
unsafe { msg_send_id![self, firstResponder] }
}
pub fn standardWindowButton(&self, kind: NSWindowButton) -> Option<Id<NSButton, Shared>> {
unsafe { msg_send_id![self, standardWindowButton: kind] }
}
#[sel(setTitle:)]
pub fn setTitle(&self, title: &NSString);
#[sel(setReleasedWhenClosed:)]
pub fn setReleasedWhenClosed(&self, val: bool);
#[sel(setAcceptsMouseMovedEvents:)]
pub fn setAcceptsMouseMovedEvents(&self, val: bool);
#[sel(setTitlebarAppearsTransparent:)]
pub fn setTitlebarAppearsTransparent(&self, val: bool);
#[sel(setTitleVisibility:)]
pub fn setTitleVisibility(&self, visibility: NSWindowTitleVisibility);
#[sel(setMovableByWindowBackground:)]
pub fn setMovableByWindowBackground(&self, val: bool);
#[sel(setLevel:)]
pub fn setLevel(&self, level: NSWindowLevel);
#[sel(occlusionState)]
pub fn occlusionState(&self) -> NSWindowOcclusionState;
#[sel(center)]
pub fn center(&self);
#[sel(isResizable)]
pub fn isResizable(&self) -> bool;
#[sel(isMiniaturized)]
pub fn isMiniaturized(&self) -> bool;
#[sel(isVisible)]
pub fn isVisible(&self) -> bool;
#[sel(isZoomed)]
pub fn isZoomed(&self) -> bool;
#[sel(close)]
pub fn close(&self);
#[sel(performWindowDragWithEvent:)]
// TODO: Can this actually accept NULL?
pub fn performWindowDragWithEvent(&self, event: Option<&NSEvent>);
#[sel(invalidateCursorRectsForView:)]
pub fn invalidateCursorRectsForView(&self, view: &NSView);
#[sel(setDelegate:)]
pub fn setDelegate(&self, delegate: Option<&NSObject>);
#[sel(sendEvent:)]
pub unsafe fn sendEvent(&self, event: &NSEvent);
}
);
#[allow(dead_code)]
#[repr(isize)] // NSInteger
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum NSWindowTitleVisibility {
#[doc(alias = "NSWindowTitleVisible")]
Visible = 0,
#[doc(alias = "NSWindowTitleHidden")]
Hidden = 1,
}
unsafe impl Encode for NSWindowTitleVisibility {
const ENCODING: Encoding = NSInteger::ENCODING;
}
#[allow(dead_code)]
#[repr(usize)] // NSUInteger
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum NSWindowButton {
#[doc(alias = "NSWindowCloseButton")]
Close = 0,
#[doc(alias = "NSWindowMiniaturizeButton")]
Miniaturize = 1,
#[doc(alias = "NSWindowZoomButton")]
Zoom = 2,
#[doc(alias = "NSWindowToolbarButton")]
Toolbar = 3,
#[doc(alias = "NSWindowDocumentIconButton")]
DocumentIcon = 4,
#[doc(alias = "NSWindowDocumentVersionsButton")]
DocumentVersions = 6,
#[doc(alias = "NSWindowFullScreenButton")]
#[deprecated = "Deprecated since macOS 10.12"]
FullScreen = 7,
}
unsafe impl Encode for NSWindowButton {
const ENCODING: Encoding = NSUInteger::ENCODING;
}
#[allow(dead_code)]
mod window_level_key {
use objc2::foundation::NSInteger;
pub const kCGBaseWindowLevelKey: NSInteger = 0;
pub const kCGMinimumWindowLevelKey: NSInteger = 1;
pub const kCGDesktopWindowLevelKey: NSInteger = 2;
pub const kCGBackstopMenuLevelKey: NSInteger = 3;
pub const kCGNormalWindowLevelKey: NSInteger = 4;
pub const kCGFloatingWindowLevelKey: NSInteger = 5;
pub const kCGTornOffMenuWindowLevelKey: NSInteger = 6;
pub const kCGDockWindowLevelKey: NSInteger = 7;
pub const kCGMainMenuWindowLevelKey: NSInteger = 8;
pub const kCGStatusWindowLevelKey: NSInteger = 9;
pub const kCGModalPanelWindowLevelKey: NSInteger = 10;
pub const kCGPopUpMenuWindowLevelKey: NSInteger = 11;
pub const kCGDraggingWindowLevelKey: NSInteger = 12;
pub const kCGScreenSaverWindowLevelKey: NSInteger = 13;
pub const kCGMaximumWindowLevelKey: NSInteger = 14;
pub const kCGOverlayWindowLevelKey: NSInteger = 15;
pub const kCGHelpWindowLevelKey: NSInteger = 16;
pub const kCGUtilityWindowLevelKey: NSInteger = 17;
pub const kCGDesktopIconWindowLevelKey: NSInteger = 18;
pub const kCGCursorWindowLevelKey: NSInteger = 19;
pub const kCGNumberOfWindowLevelKeys: NSInteger = 20;
}
use window_level_key::*;
#[allow(dead_code)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(isize)]
pub enum NSWindowLevel {
#[doc(alias = "NSNormalWindowLevel")]
Normal = kCGBaseWindowLevelKey,
#[doc(alias = "NSFloatingWindowLevel")]
Floating = kCGFloatingWindowLevelKey,
#[doc(alias = "NSTornOffMenuWindowLevel")]
TornOffMenu = kCGTornOffMenuWindowLevelKey,
#[doc(alias = "NSModalPanelWindowLevel")]
ModalPanel = kCGModalPanelWindowLevelKey,
#[doc(alias = "NSMainMenuWindowLevel")]
MainMenu = kCGMainMenuWindowLevelKey,
#[doc(alias = "NSStatusWindowLevel")]
Status = kCGStatusWindowLevelKey,
#[doc(alias = "NSPopUpMenuWindowLevel")]
PopUpMenu = kCGPopUpMenuWindowLevelKey,
#[doc(alias = "NSScreenSaverWindowLevel")]
ScreenSaver = kCGScreenSaverWindowLevelKey,
}
unsafe impl Encode for NSWindowLevel {
const ENCODING: Encoding = NSInteger::ENCODING;
}
bitflags! {
pub struct NSWindowOcclusionState: NSUInteger {
const NSWindowOcclusionStateVisible = 1 << 1;
}
}
unsafe impl Encode for NSWindowOcclusionState {
const ENCODING: Encoding = NSUInteger::ENCODING;
}
bitflags! {
pub struct NSWindowStyleMask: NSUInteger {
const NSBorderlessWindowMask = 0;
const NSTitledWindowMask = 1 << 0;
const NSClosableWindowMask = 1 << 1;
const NSMiniaturizableWindowMask = 1 << 2;
const NSResizableWindowMask = 1 << 3;
const NSTexturedBackgroundWindowMask = 1 << 8;
const NSUnifiedTitleAndToolbarWindowMask = 1 << 12;
const NSFullScreenWindowMask = 1 << 14;
const NSFullSizeContentViewWindowMask = 1 << 15;
}
}
unsafe impl Encode for NSWindowStyleMask {
const ENCODING: Encoding = NSUInteger::ENCODING;
}
#[allow(dead_code)]
#[repr(usize)] // NSUInteger
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum NSBackingStoreType {
NSBackingStoreRetained = 0,
NSBackingStoreNonretained = 1,
NSBackingStoreBuffered = 2,
}
unsafe impl Encode for NSBackingStoreType {
const ENCODING: Encoding = NSUInteger::ENCODING;
}