refactor: Use objc2 (#1049)

This commit is contained in:
Mads Marquart
2025-02-21 17:29:48 +01:00
committed by GitHub
parent 5cc92980c4
commit 8c8f0e8b79
29 changed files with 1038 additions and 1101 deletions

5
.changes/objc2.md Normal file
View File

@@ -0,0 +1,5 @@
---
tao: patch
---
Use `objc2`.

View File

@@ -104,10 +104,41 @@ ndk-context = "0.1"
tao-macros = { version = "0.1.0", path = "./tao-macros" }
[target."cfg(any(target_os = \"ios\", target_os = \"macos\"))".dependencies]
objc = "0.2"
objc2 = "0.5"
[target."cfg(target_os = \"macos\")".dependencies]
cocoa = "0.26"
objc2-foundation = { version = "0.2", default-features = false, features = [
"std",
"NSArray",
"NSAttributedString",
"NSAutoreleasePool",
"NSDate",
"NSDictionary",
"NSEnumerator",
"NSGeometry",
"NSObjCRuntime",
"NSRange",
"NSString",
"NSThread",
"NSURL",
] }
objc2-app-kit = { version = "0.2", default-features = false, features = [
"std",
"NSApplication",
"NSButton",
"NSColor",
"NSControl",
"NSEvent",
"NSGraphics",
"NSImage",
"NSOpenGLView",
"NSPasteboard",
"NSResponder",
"NSRunningApplication",
"NSScreen",
"NSView",
"NSWindow",
] }
core-foundation = "0.10"
core-graphics = "0.24"
dispatch = "0.2"

View File

@@ -37,8 +37,7 @@
//! describes what happens in what order.
//!
//! [event_loop_run]: crate::event_loop::EventLoop::run
use std::path::PathBuf;
use std::time::Instant;
use std::{path::PathBuf, time::Instant};
use crate::{
dpi::{PhysicalPosition, PhysicalSize},

View File

@@ -167,8 +167,8 @@ extern crate serde;
#[macro_use]
extern crate bitflags;
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[macro_use]
extern crate objc;
#[macro_use(class, msg_send, sel)]
extern crate objc2;
pub use dpi;

View File

@@ -363,7 +363,10 @@ impl MonitorHandleExtMacOS for MonitorHandle {
}
fn ns_screen(&self) -> Option<*mut c_void> {
self.inner.ns_screen().map(|s| s as *mut c_void)
self
.inner
.ns_screen()
.map(|s| objc2::rc::Retained::into_raw(s) as *mut c_void)
}
}
@@ -388,36 +391,35 @@ pub trait EventLoopWindowTargetExtMacOS {
impl<T> EventLoopWindowTargetExtMacOS for EventLoopWindowTarget<T> {
fn hide_application(&self) {
let cls = objc::runtime::Class::get("NSApplication").unwrap();
let app: cocoa::base::id = unsafe { msg_send![cls, sharedApplication] };
unsafe { msg_send![app, hide: 0] }
// TODO: Safety.
let mtm = unsafe { objc2_foundation::MainThreadMarker::new_unchecked() };
objc2_app_kit::NSApplication::sharedApplication(mtm).hide(None)
}
fn show_application(&self) {
let cls = objc::runtime::Class::get("NSApplication").unwrap();
let app: cocoa::base::id = unsafe { msg_send![cls, sharedApplication] };
unsafe { msg_send![app, unhide: 0] }
// TODO: Safety.
let mtm = unsafe { objc2_foundation::MainThreadMarker::new_unchecked() };
unsafe { objc2_app_kit::NSApplication::sharedApplication(mtm).unhide(None) }
}
fn hide_other_applications(&self) {
let cls = objc::runtime::Class::get("NSApplication").unwrap();
let app: cocoa::base::id = unsafe { msg_send![cls, sharedApplication] };
unsafe { msg_send![app, hideOtherApplications: 0] }
// TODO: Safety.
let mtm = unsafe { objc2_foundation::MainThreadMarker::new_unchecked() };
objc2_app_kit::NSApplication::sharedApplication(mtm).hideOtherApplications(None)
}
fn set_activation_policy_at_runtime(&self, activation_policy: ActivationPolicy) {
use cocoa::appkit;
let cls = objc::runtime::Class::get("NSApplication").unwrap();
let app: cocoa::base::id = unsafe { msg_send![cls, sharedApplication] };
use objc2_app_kit::NSApplicationActivationPolicy;
let ns_activation_policy = match activation_policy {
ActivationPolicy::Regular => appkit::NSApplicationActivationPolicyRegular,
ActivationPolicy::Accessory => appkit::NSApplicationActivationPolicyAccessory,
ActivationPolicy::Prohibited => appkit::NSApplicationActivationPolicyProhibited,
ActivationPolicy::Regular => NSApplicationActivationPolicy::Regular,
ActivationPolicy::Accessory => NSApplicationActivationPolicy::Accessory,
ActivationPolicy::Prohibited => NSApplicationActivationPolicy::Prohibited,
};
unsafe { msg_send![app, setActivationPolicy: ns_activation_policy] }
// TODO: Safety.
let mtm = unsafe { objc2_foundation::MainThreadMarker::new_unchecked() };
objc2_app_kit::NSApplication::sharedApplication(mtm).setActivationPolicy(ns_activation_policy);
}
fn set_badge_label(&self, label: Option<String>) {

View File

@@ -13,7 +13,7 @@ use std::{
time::Instant,
};
use objc::runtime::{BOOL, YES};
use objc2::runtime::AnyObject;
use crate::{
dpi::LogicalSize,
@@ -475,10 +475,7 @@ impl AppState {
// retains window
pub unsafe fn set_key_window(window: id) {
bug_assert!(
{
let is_window: BOOL = msg_send![window, isKindOfClass: class!(UIWindow)];
is_window == YES
},
msg_send![window, isKindOfClass: class!(UIWindow)],
"set_key_window called with an incorrect type"
);
let mut this = AppState::get_mut();
@@ -505,10 +502,7 @@ pub unsafe fn set_key_window(window: id) {
// retains window
pub unsafe fn queue_gl_or_metal_redraw(window: id) {
bug_assert!(
{
let is_window: BOOL = msg_send![window, isKindOfClass: class!(UIWindow)];
is_window == YES
},
msg_send![window, isKindOfClass: class!(UIWindow)],
"set_key_window called with an incorrect type"
);
let mut this = AppState::get_mut();
@@ -582,7 +576,7 @@ pub unsafe fn did_finish_launching() {
let () = msg_send![window, setScreen: screen];
let () = msg_send![screen, release];
let controller: id = msg_send![window, rootViewController];
let () = msg_send![window, setRootViewController:ptr::null::<()>()];
let () = msg_send![window, setRootViewController: ptr::null::<AnyObject>()];
let () = msg_send![window, setRootViewController: controller];
let () = msg_send![window, makeKeyAndVisible];
}
@@ -1019,7 +1013,7 @@ pub fn os_capabilities() -> OSCapabilities {
static ref OS_CAPABILITIES: OSCapabilities = {
let version: NSOperatingSystemVersion = unsafe {
let process_info: id = msg_send![class!(NSProcessInfo), processInfo];
let atleast_ios_8: BOOL = msg_send![
let atleast_ios_8: bool = msg_send![
process_info,
respondsToSelector: sel!(operatingSystemVersion)
];
@@ -1031,7 +1025,7 @@ pub fn os_capabilities() -> OSCapabilities {
//
// The minimum required iOS version is likely to grow in the future.
assert!(
atleast_ios_8 == YES,
atleast_ios_8,
"`tao` requires iOS version 8 or greater"
);
msg_send![process_info, operatingSystemVersion]

View File

@@ -1,10 +1,12 @@
use objc::runtime::{Class, Object};
use objc::{msg_send, sel, sel_impl};
use objc2::{
msg_send,
runtime::{AnyClass, AnyObject},
};
pub fn set_badge_count(count: i32) {
unsafe {
let ui_application = Class::get("UIApplication").expect("Failed to get UIApplication class");
let app: *mut Object = msg_send![ui_application, sharedApplication];
let ui_application = AnyClass::get("UIApplication").expect("Failed to get UIApplication class");
let app: *mut AnyObject = msg_send![ui_application, sharedApplication];
let _: () = msg_send![app, setApplicationIconBadgeNumber:count];
}
}

View File

@@ -143,7 +143,7 @@ impl<T: 'static> EventLoop<T> {
F: 'static + FnMut(Event<'_, T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
{
unsafe {
let application: *mut c_void = msg_send![class!(UIApplication), sharedApplication];
let application: id = msg_send![class!(UIApplication), sharedApplication];
assert_eq!(
application,
ptr::null_mut(),

View File

@@ -6,16 +6,26 @@
use std::{convert::TryInto, ffi::CString, ops::BitOr, os::raw::*};
use objc::{runtime::Object, Encode, Encoding};
use objc2::{
encode::{Encode, Encoding},
runtime::{AnyObject, Bool},
};
use crate::{
dpi::LogicalSize,
platform::ios::{Idiom, ScreenEdge, ValidOrientations},
};
pub type id = *mut Object;
pub type id = *mut AnyObject;
pub const nil: id = 0 as id;
#[allow(non_camel_case_types)]
pub type BOOL = Bool;
#[allow(deprecated)]
pub const YES: Bool = Bool::YES;
#[allow(deprecated)]
pub const NO: Bool = Bool::NO;
#[cfg(target_pointer_width = "32")]
pub type CGFloat = f32;
#[cfg(target_pointer_width = "64")]
@@ -32,6 +42,17 @@ pub struct NSOperatingSystemVersion {
pub patch: NSInteger,
}
unsafe impl Encode for NSOperatingSystemVersion {
const ENCODING: Encoding = Encoding::Struct(
"?",
&[
NSInteger::ENCODING,
NSInteger::ENCODING,
NSInteger::ENCODING,
],
);
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct CGPoint {
@@ -39,6 +60,10 @@ pub struct CGPoint {
pub y: CGFloat,
}
unsafe impl Encode for CGPoint {
const ENCODING: Encoding = Encoding::Struct("CGPoint", &[CGFloat::ENCODING, CGFloat::ENCODING]);
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct CGSize {
@@ -55,6 +80,10 @@ impl CGSize {
}
}
unsafe impl Encode for CGSize {
const ENCODING: Encoding = Encoding::Struct("CGSize", &[CGFloat::ENCODING, CGFloat::ENCODING]);
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct CGRect {
@@ -69,18 +98,9 @@ impl CGRect {
}
unsafe impl Encode for CGRect {
fn encode() -> Encoding {
unsafe {
if cfg!(target_pointer_width = "32") {
Encoding::from_str("{CGRect={CGPoint=ff}{CGSize=ff}}")
} else if cfg!(target_pointer_width = "64") {
Encoding::from_str("{CGRect={CGPoint=dd}{CGSize=dd}}")
} else {
unimplemented!()
}
}
}
const ENCODING: Encoding = Encoding::Struct("CGRect", &[CGPoint::ENCODING, CGSize::ENCODING]);
}
#[derive(Debug)]
#[allow(dead_code)]
#[repr(isize)]
@@ -92,6 +112,10 @@ pub enum UITouchPhase {
Cancelled,
}
unsafe impl Encode for UITouchPhase {
const ENCODING: Encoding = isize::ENCODING;
}
#[derive(Debug, PartialEq)]
#[allow(dead_code)]
#[repr(isize)]
@@ -101,6 +125,10 @@ pub enum UIForceTouchCapability {
Available,
}
unsafe impl Encode for UIForceTouchCapability {
const ENCODING: Encoding = isize::ENCODING;
}
#[derive(Debug, PartialEq)]
#[allow(dead_code)]
#[repr(isize)]
@@ -110,6 +138,10 @@ pub enum UITouchType {
Pencil,
}
unsafe impl Encode for UITouchType {
const ENCODING: Encoding = isize::ENCODING;
}
#[repr(C)]
#[derive(Debug, Clone)]
pub struct UIEdgeInsets {
@@ -119,14 +151,24 @@ pub struct UIEdgeInsets {
pub right: CGFloat,
}
unsafe impl Encode for UIEdgeInsets {
const ENCODING: Encoding = Encoding::Struct(
"UIEdgeInsets",
&[
CGFloat::ENCODING,
CGFloat::ENCODING,
CGFloat::ENCODING,
CGFloat::ENCODING,
],
);
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct UIUserInterfaceIdiom(NSInteger);
unsafe impl Encode for UIUserInterfaceIdiom {
fn encode() -> Encoding {
NSInteger::encode()
}
const ENCODING: Encoding = NSInteger::ENCODING;
}
impl UIUserInterfaceIdiom {
@@ -167,9 +209,7 @@ impl Into<Idiom> for UIUserInterfaceIdiom {
pub struct UIInterfaceOrientationMask(NSUInteger);
unsafe impl Encode for UIInterfaceOrientationMask {
fn encode() -> Encoding {
NSUInteger::encode()
}
const ENCODING: Encoding = NSUInteger::ENCODING;
}
impl UIInterfaceOrientationMask {
@@ -217,9 +257,7 @@ impl UIInterfaceOrientationMask {
pub struct UIRectEdge(NSUInteger);
unsafe impl Encode for UIRectEdge {
fn encode() -> Encoding {
NSUInteger::encode()
}
const ENCODING: Encoding = NSUInteger::ENCODING;
}
impl From<ScreenEdge> for UIRectEdge {
@@ -245,9 +283,7 @@ impl Into<ScreenEdge> for UIRectEdge {
pub struct UIScreenOverscanCompensation(NSInteger);
unsafe impl Encode for UIScreenOverscanCompensation {
fn encode() -> Encoding {
NSInteger::encode()
}
const ENCODING: Encoding = NSInteger::ENCODING;
}
#[allow(dead_code)]
@@ -378,7 +414,7 @@ pub trait NSStringRust: Sized {
impl NSStringRust for id {
unsafe fn initWithUTF8String_(self, c_string: *const c_char) -> id {
msg_send![self, initWithUTF8String: c_string as id]
msg_send![self, initWithUTF8String: c_string]
}
unsafe fn stringByAppendingString_(self, other: id) -> id {

View File

@@ -66,8 +66,8 @@
// window size/position.
macro_rules! assert_main_thread {
($($t:tt)*) => {
let is_main_thread: ::objc::runtime::BOOL = msg_send!(class!(NSThread), isMainThread);
if is_main_thread == ::objc::runtime::NO {
let is_main_thread: bool = msg_send![class!(NSThread), isMainThread];
if !is_main_thread {
panic!($($t)*);
}
};

View File

@@ -192,7 +192,7 @@ impl MonitorHandle {
pub fn retained_new(uiscreen: id) -> MonitorHandle {
unsafe {
assert_main_thread!("`MonitorHandle` can only be cloned on the main thread on iOS");
let () = msg_send![uiscreen, retain];
let _: id = msg_send![uiscreen, retain];
}
MonitorHandle {
inner: Inner { uiscreen },

View File

@@ -4,10 +4,7 @@
use std::{collections::HashMap, ffi::c_char};
use objc::{
declare::ClassDecl,
runtime::{Class, Object, Sel, BOOL, NO, YES},
};
use objc2::runtime::{AnyClass as Class, AnyObject as Object, ClassBuilder as ClassDecl, Sel};
use crate::{
dpi::PhysicalPosition,
@@ -18,7 +15,7 @@ use crate::{
event_loop::{self, EventProxy, EventWrapper},
ffi::{
id, nil, CGFloat, CGPoint, CGRect, UIForceTouchCapability, UIInterfaceOrientationMask,
UIRectEdge, UITouchPhase, UITouchType,
UIRectEdge, UITouchPhase, UITouchType, BOOL, NO, YES,
},
window::PlatformSpecificWindowBuilderAttributes,
DeviceId,
@@ -52,8 +49,9 @@ macro_rules! add_property {
let setter = if $capability {
#[allow(non_snake_case)]
extern "C" fn $setter_name($object: &mut Object, _: Sel, value: $t) {
#[allow(deprecated)] // TODO: define_class!
unsafe {
$object.set_ivar::<$t>(VAR_NAME, value);
*$object.get_mut_ivar::<$t>(VAR_NAME) = value;
}
$after_set
}
@@ -61,8 +59,9 @@ macro_rules! add_property {
} else {
#[allow(non_snake_case)]
extern "C" fn $setter_name($object: &mut Object, _: Sel, value: $t) {
#[allow(deprecated)] // TODO: define_class!
unsafe {
$object.set_ivar::<$t>(VAR_NAME, value);
*$object.get_mut_ivar::<$t>(VAR_NAME) = value;
}
$err(&app_state::os_capabilities(), "ignoring")
}
@@ -70,15 +69,16 @@ macro_rules! add_property {
};
#[allow(non_snake_case)]
extern "C" fn $getter_name($object: &Object, _: Sel) -> $t {
#[allow(deprecated)] // TODO: define_class!
unsafe { *$object.get_ivar::<$t>(VAR_NAME) }
}
$decl.add_method(
sel!($setter_name:),
setter as extern "C" fn(&mut Object, Sel, $t),
setter as extern "C" fn(_, _, _),
);
$decl.add_method(
sel!($getter_name),
$getter_name as extern "C" fn(&Object, Sel) -> $t,
$getter_name as extern "C" fn(_, _) -> _,
);
}
};
@@ -97,11 +97,8 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
classes.entry(root_view_class).or_insert_with(move || {
let uiview_class = class!(UIView);
let is_uiview: BOOL = msg_send![root_view_class, isSubclassOfClass: uiview_class];
assert_eq!(
is_uiview, YES,
"`root_view_class` must inherit from `UIView`"
);
let is_uiview: bool = msg_send![root_view_class, isSubclassOfClass: uiview_class];
assert!(is_uiview, "`root_view_class` must inherit from `UIView`");
extern "C" fn draw_rect(object: &Object, _: Sel, rect: CGRect) {
unsafe {
@@ -155,7 +152,7 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
}
extern "C" fn set_content_scale_factor(
object: &mut Object,
object: &Object,
_: Sel,
untrusted_scale_factor: CGFloat,
) {
@@ -286,34 +283,28 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
let mut decl = ClassDecl::new(&format!("TaoUIView{}", ID), root_view_class)
.expect("Failed to declare class `TaoUIView`");
ID += 1;
decl.add_method(
sel!(drawRect:),
draw_rect as extern "C" fn(&Object, Sel, CGRect),
);
decl.add_method(
sel!(layoutSubviews),
layout_subviews as extern "C" fn(&Object, Sel),
);
decl.add_method(sel!(drawRect:), draw_rect as extern "C" fn(_, _, _));
decl.add_method(sel!(layoutSubviews), layout_subviews as extern "C" fn(_, _));
decl.add_method(
sel!(setContentScaleFactor:),
set_content_scale_factor as extern "C" fn(&mut Object, Sel, CGFloat),
set_content_scale_factor as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(touchesBegan:withEvent:),
handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id),
handle_touches as extern "C" fn(_, _, _, _),
);
decl.add_method(
sel!(touchesMoved:withEvent:),
handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id),
handle_touches as extern "C" fn(_, _, _, _),
);
decl.add_method(
sel!(touchesEnded:withEvent:),
handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id),
handle_touches as extern "C" fn(_, _, _, _),
);
decl.add_method(
sel!(touchesCancelled:withEvent:),
handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id),
handle_touches as extern "C" fn(_, _, _, _),
);
decl.register()
@@ -336,7 +327,7 @@ unsafe fn get_view_controller_class() -> &'static Class {
.expect("Failed to declare class `TaoUIViewController`");
decl.add_method(
sel!(shouldAutorotate),
should_autorotate as extern "C" fn(&Object, Sel) -> BOOL,
should_autorotate as extern "C" fn(_, _) -> _,
);
add_property! {
decl,
@@ -419,11 +410,11 @@ unsafe fn get_window_class() -> &'static Class {
ClassDecl::new("TaoUIWindow", uiwindow_class).expect("Failed to declare class `TaoUIWindow`");
decl.add_method(
sel!(becomeKeyWindow),
become_key_window as extern "C" fn(&Object, Sel),
become_key_window as extern "C" fn(_, _),
);
decl.add_method(
sel!(resignKeyWindow),
resign_key_window as extern "C" fn(&Object, Sel),
resign_key_window as extern "C" fn(_, _),
);
CLASS = Some(decl.register());
@@ -548,7 +539,7 @@ pub unsafe fn create_window(
}
pub fn create_delegate_class() {
extern "C" fn did_finish_launching(_: &mut Object, _: Sel, _: id, _: id) -> BOOL {
extern "C" fn did_finish_launching(_: &Object, _: Sel, _: id, _: id) -> BOOL {
unsafe {
app_state::did_finish_launching();
}
@@ -576,7 +567,7 @@ pub fn create_delegate_class() {
// custom URL schemes
// https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app
extern "C" fn application_open_url(
_self: &mut Object,
_self: &Object,
_cmd: Sel,
_app: id,
url: id,
@@ -590,7 +581,7 @@ pub fn create_delegate_class() {
// universal links
// https://developer.apple.com/documentation/xcode/supporting-universal-links-in-your-app
extern "C" fn application_continue(
_: &mut Object,
_: &Object,
_: Sel,
_application: id,
user_activity: id,
@@ -630,8 +621,8 @@ pub fn create_delegate_class() {
if window == nil {
break;
}
let is_tao_window: BOOL = msg_send![window, isKindOfClass: class!(TaoUIWindow)];
if is_tao_window == YES {
let is_tao_window: bool = msg_send![window, isKindOfClass: class!(TaoUIWindow)];
if is_tao_window {
events.push(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: RootWindowId(window.into()),
event: WindowEvent::Destroyed,
@@ -650,39 +641,39 @@ pub fn create_delegate_class() {
unsafe {
decl.add_method(
sel!(application:didFinishLaunchingWithOptions:),
did_finish_launching as extern "C" fn(&mut Object, Sel, id, id) -> BOOL,
did_finish_launching as extern "C" fn(_, _, _, _) -> _,
);
decl.add_method(
sel!(application:openURL:options:),
application_open_url as extern "C" fn(&mut Object, Sel, id, id, id) -> BOOL,
application_open_url as extern "C" fn(_, _, _, _, _) -> _,
);
decl.add_method(
sel!(application:continueUserActivity:restorationHandler:),
application_continue as extern "C" fn(&mut Object, Sel, id, id, id) -> BOOL,
application_continue as extern "C" fn(_, _, _, _, _) -> _,
);
decl.add_method(
sel!(applicationDidBecomeActive:),
did_become_active as extern "C" fn(&Object, Sel, id),
did_become_active as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(applicationWillResignActive:),
will_resign_active as extern "C" fn(&Object, Sel, id),
will_resign_active as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(applicationWillEnterForeground:),
will_enter_foreground as extern "C" fn(&Object, Sel, id),
will_enter_foreground as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(applicationDidEnterBackground:),
did_enter_background as extern "C" fn(&Object, Sel, id),
did_enter_background as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(applicationWillTerminate:),
will_terminate as extern "C" fn(&Object, Sel, id),
will_terminate as extern "C" fn(_, _, _),
);
decl.register();

View File

@@ -7,7 +7,7 @@ use std::{
ops::{Deref, DerefMut},
};
use objc::runtime::{Class, Object, BOOL, NO, YES};
use objc2::runtime::{AnyClass, AnyObject};
use crate::{
dpi::{self, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
@@ -21,7 +21,7 @@ use crate::{
event_loop::{self, EventProxy, EventWrapper},
ffi::{
id, CGFloat, CGPoint, CGRect, CGSize, UIEdgeInsets, UIInterfaceOrientationMask, UIRectEdge,
UIScreenOverscanCompensation,
UIScreenOverscanCompensation, NO, YES,
},
monitor, set_badge_count, view, EventLoopWindowTarget, MonitorHandle,
},
@@ -522,11 +522,11 @@ impl Window {
let view = view::create_view(&window_attributes, &platform_attributes, frame.clone());
let gl_or_metal_backed = {
let view_class: id = msg_send![view, class];
let layer_class: id = msg_send![view_class, layerClass];
let is_metal: BOOL = msg_send![layer_class, isSubclassOfClass: class!(CAMetalLayer)];
let is_gl: BOOL = msg_send![layer_class, isSubclassOfClass: class!(CAEAGLLayer)];
is_metal == YES || is_gl == YES
let view_class: *const AnyClass = msg_send![view, class];
let layer_class: *const AnyClass = msg_send![view_class, layerClass];
let is_metal: bool = msg_send![layer_class, isSubclassOfClass: class!(CAMetalLayer)];
let is_gl: bool = msg_send![layer_class, isSubclassOfClass: class!(CAEAGLLayer)];
is_metal || is_gl
};
let view_controller =
@@ -740,16 +740,16 @@ impl WindowId {
unsafe impl Send for WindowId {}
unsafe impl Sync for WindowId {}
impl From<&Object> for WindowId {
fn from(window: &Object) -> WindowId {
impl From<&AnyObject> for WindowId {
fn from(window: &AnyObject) -> WindowId {
WindowId {
window: window as *const _ as _,
}
}
}
impl From<&mut Object> for WindowId {
fn from(window: &mut Object) -> WindowId {
impl From<&mut AnyObject> for WindowId {
fn from(window: &mut AnyObject) -> WindowId {
WindowId {
window: window as _,
}
@@ -764,7 +764,7 @@ impl From<id> for WindowId {
#[derive(Clone)]
pub struct PlatformSpecificWindowBuilderAttributes {
pub root_view_class: &'static Class,
pub root_view_class: &'static AnyClass,
pub scale_factor: Option<f64>,
pub valid_orientations: ValidOrientations,
pub prefers_home_indicator_hidden: bool,

View File

@@ -4,16 +4,10 @@
use std::collections::VecDeque;
use cocoa::{
appkit::{self, NSEvent},
base::id,
};
use objc::{
declare::ClassDecl,
runtime::{Class, Object, Sel},
};
use objc2::runtime::{AnyClass as Class, AnyObject as Object, ClassBuilder as ClassDecl, Sel};
use objc2_app_kit::{self as appkit, NSEvent, NSEventType};
use super::{app_state::AppState, event::EventWrapper, util, DEVICE_ID};
use super::{app_state::AppState, event::EventWrapper, ffi::id, util, DEVICE_ID};
use crate::event::{DeviceEvent, ElementState, Event};
pub struct AppClass(pub *const Class);
@@ -25,10 +19,7 @@ lazy_static! {
let superclass = class!(NSApplication);
let mut decl = ClassDecl::new("TaoApp", superclass).unwrap();
decl.add_method(
sel!(sendEvent:),
send_event as extern "C" fn(&Object, Sel, id),
);
decl.add_method(sel!(sendEvent:), send_event as extern "C" fn(_, _, _));
AppClass(decl.register())
};
@@ -37,17 +28,17 @@ lazy_static! {
// Normally, holding Cmd + any key never sends us a `keyUp` event for that key.
// Overriding `sendEvent:` like this fixes that. (https://stackoverflow.com/a/15294196)
// Fun fact: Firefox still has this bug! (https://bugzilla.mozilla.org/show_bug.cgi?id=1299553)
extern "C" fn send_event(this: &Object, _sel: Sel, event: id) {
extern "C" fn send_event(this: &Object, _sel: Sel, event: &NSEvent) {
unsafe {
// For posterity, there are some undocumented event types
// (https://github.com/servo/cocoa-rs/issues/155)
// but that doesn't really matter here.
let event_type = event.eventType();
let event_type = event.r#type();
let modifier_flags = event.modifierFlags();
if event_type == appkit::NSKeyUp
&& util::has_flag(
modifier_flags,
appkit::NSEventModifierFlags::NSCommandKeyMask,
appkit::NSEventModifierFlags::NSEventModifierFlagCommand,
)
{
let key_window: id = msg_send![this, keyWindow];
@@ -60,13 +51,13 @@ extern "C" fn send_event(this: &Object, _sel: Sel, event: id) {
}
}
unsafe fn maybe_dispatch_device_event(event: id) {
let event_type = event.eventType();
unsafe fn maybe_dispatch_device_event(event: &NSEvent) {
let event_type = event.r#type();
match event_type {
appkit::NSMouseMoved
| appkit::NSLeftMouseDragged
| appkit::NSOtherMouseDragged
| appkit::NSRightMouseDragged => {
NSEventType::MouseMoved
| NSEventType::LeftMouseDragged
| NSEventType::OtherMouseDragged
| NSEventType::RightMouseDragged => {
let mut events = VecDeque::with_capacity(3);
let delta_x = event.deltaX() as f64;
@@ -103,7 +94,7 @@ unsafe fn maybe_dispatch_device_event(event: id) {
AppState::queue_events(events);
}
appkit::NSLeftMouseDown | appkit::NSRightMouseDown | appkit::NSOtherMouseDown => {
NSEventType::LeftMouseDown | NSEventType::RightMouseDown | NSEventType::OtherMouseDown => {
let mut events = VecDeque::with_capacity(1);
events.push_back(EventWrapper::StaticEvent(Event::DeviceEvent {
@@ -116,7 +107,7 @@ unsafe fn maybe_dispatch_device_event(event: id) {
AppState::queue_events(events);
}
appkit::NSLeftMouseUp | appkit::NSRightMouseUp | appkit::NSOtherMouseUp => {
NSEventType::LeftMouseUp | NSEventType::RightMouseUp | NSEventType::OtherMouseUp => {
let mut events = VecDeque::with_capacity(1);
events.push_back(EventWrapper::StaticEvent(Event::DeviceEvent {

View File

@@ -2,24 +2,21 @@
// Copyright 2021-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
use crate::{platform::macos::ActivationPolicy, platform_impl::platform::app_state::AppState};
use crate::{
platform::macos::ActivationPolicy,
platform_impl::platform::{
app_state::AppState,
ffi::{id, BOOL, YES},
},
};
use cocoa::{
base::{id, NO},
foundation::NSString,
};
use objc::{
declare::ClassDecl,
runtime::{Class, Object, Sel, BOOL},
};
use objc2::runtime::{AnyClass as Class, AnyObject as Object, ClassBuilder as ClassDecl, Sel};
use objc2_foundation::{NSArray, NSURL};
use std::{
cell::{RefCell, RefMut},
os::raw::c_void,
};
use cocoa::foundation::{NSArray, NSURL};
use std::ffi::CStr;
static AUX_DELEGATE_STATE_NAME: &str = "auxState";
pub struct AuxDelegateState {
@@ -40,28 +37,28 @@ lazy_static! {
let superclass = class!(NSResponder);
let mut decl = ClassDecl::new("TaoAppDelegateParent", superclass).unwrap();
decl.add_class_method(sel!(new), new as extern "C" fn(&Class, Sel) -> id);
decl.add_method(sel!(dealloc), dealloc as extern "C" fn(&Object, Sel));
decl.add_class_method(sel!(new), new as extern "C" fn(_, _) -> _);
decl.add_method(sel!(dealloc), dealloc as extern "C" fn(_, _));
decl.add_method(
sel!(applicationDidFinishLaunching:),
did_finish_launching as extern "C" fn(&Object, Sel, id),
did_finish_launching as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(applicationWillTerminate:),
application_will_terminate as extern "C" fn(&Object, Sel, id),
application_will_terminate as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(application:openURLs:),
application_open_urls as extern "C" fn(&Object, Sel, id, id),
application_open_urls as extern "C" fn(_, _, _, _),
);
decl.add_method(
sel!(applicationShouldHandleReopen:hasVisibleWindows:),
application_should_handle_reopen as extern "C" fn(&Object, Sel, id, BOOL) -> BOOL,
application_should_handle_reopen as extern "C" fn(_, _, _, _) -> _,
);
decl.add_method(
sel!(applicationSupportsSecureRestorableState:),
application_supports_secure_restorable_state as extern "C" fn(&Object, Sel, id) -> BOOL,
application_supports_secure_restorable_state as extern "C" fn(_, _, _) -> _,
);
decl.add_ivar::<*mut c_void>(AUX_DELEGATE_STATE_NAME);
@@ -70,6 +67,7 @@ lazy_static! {
}
/// Safety: Assumes that Object is an instance of APP_DELEGATE_CLASS
#[allow(deprecated)] // TODO: Use define_class!
pub unsafe fn get_aux_state_mut(this: &Object) -> RefMut<'_, AuxDelegateState> {
let ptr: *mut c_void = *this.get_ivar(AUX_DELEGATE_STATE_NAME);
// Watch out that this needs to be the correct type
@@ -77,21 +75,21 @@ pub unsafe fn get_aux_state_mut(this: &Object) -> RefMut<'_, AuxDelegateState> {
}
extern "C" fn new(class: &Class, _: Sel) -> id {
#[allow(deprecated)] // TODO: Use define_class!
unsafe {
let this: id = msg_send![class, alloc];
let this: id = msg_send![this, init];
(*this).set_ivar(
AUX_DELEGATE_STATE_NAME,
*(*this).get_mut_ivar(AUX_DELEGATE_STATE_NAME) =
Box::into_raw(Box::new(RefCell::new(AuxDelegateState {
activation_policy: ActivationPolicy::Regular,
activate_ignoring_other_apps: true,
}))) as *mut c_void,
);
}))) as *mut c_void;
this
}
}
extern "C" fn dealloc(this: &Object, _: Sel) {
#[allow(deprecated)] // TODO: Use define_class!
unsafe {
let state_ptr: *mut c_void = *(this.get_ivar(AUX_DELEGATE_STATE_NAME));
// As soon as the box is constructed it is immediately dropped, releasing the underlying
@@ -112,16 +110,12 @@ extern "C" fn application_will_terminate(_: &Object, _: Sel, _: id) {
trace!("Completed `applicationWillTerminate`");
}
extern "C" fn application_open_urls(_: &Object, _: Sel, _: id, urls: id) -> () {
extern "C" fn application_open_urls(_: &Object, _: Sel, _: id, urls: &NSArray<NSURL>) -> () {
trace!("Trigger `application:openURLs:`");
let urls = unsafe {
(0..urls.count())
.map(|i| {
url::Url::parse(
&CStr::from_ptr(urls.objectAtIndex(i).absoluteString().UTF8String()).to_string_lossy(),
)
})
.map(|i| url::Url::parse(&urls.objectAtIndex(i).absoluteString().unwrap().to_string()))
.flatten()
.collect::<Vec<_>>()
};
@@ -137,7 +131,7 @@ extern "C" fn application_should_handle_reopen(
has_visible_windows: BOOL,
) -> BOOL {
trace!("Triggered `applicationShouldHandleReopen`");
AppState::reopen(has_visible_windows != NO);
AppState::reopen(has_visible_windows.as_bool());
trace!("Completed `applicationShouldHandleReopen`");
has_visible_windows
}
@@ -145,5 +139,5 @@ extern "C" fn application_should_handle_reopen(
extern "C" fn application_supports_secure_restorable_state(_: &Object, _: Sel, _: id) -> BOOL {
trace!("Triggered `applicationSupportsSecureRestorableState`");
trace!("Completed `applicationSupportsSecureRestorableState`");
objc::runtime::YES
YES
}

View File

@@ -16,12 +16,9 @@ use std::{
time::Instant,
};
use cocoa::{
appkit::{NSApp, NSApplication, NSWindow},
base::{id, nil},
foundation::{NSAutoreleasePool, NSSize},
};
use objc::runtime::{Object, NO, YES};
use objc2::{msg_send_id, rc::Retained, runtime::AnyObject as Object};
use objc2_app_kit::{NSApp, NSApplication, NSApplicationActivationPolicy, NSWindow};
use objc2_foundation::{MainThreadMarker, NSAutoreleasePool, NSSize};
use crate::{
dpi::LogicalSize,
@@ -33,8 +30,9 @@ use crate::{
platform::{
event::{EventProxy, EventWrapper},
event_loop::{post_dummy_event, PanicInfo},
ffi::{id, nil},
observer::{CFRunLoopGetMain, CFRunLoopWakeUp, EventLoopWaker},
util::{self, IdRef, Never},
util::{self, Never},
window::get_window_id,
},
},
@@ -221,14 +219,14 @@ impl Handler {
fn handle_scale_factor_changed_event(
&self,
callback: &mut Box<dyn EventHandler + 'static>,
ns_window: IdRef,
ns_window: &NSWindow,
suggested_size: LogicalSize<f64>,
scale_factor: f64,
) {
let mut size = suggested_size.to_physical(scale_factor);
let old_size = size.clone();
let event = Event::WindowEvent {
window_id: WindowId(get_window_id(*ns_window)),
window_id: WindowId(get_window_id(ns_window)),
event: WindowEvent::ScaleFactorChanged {
scale_factor,
new_inner_size: &mut size,
@@ -240,7 +238,7 @@ impl Handler {
if old_size != size {
let logical_size = size.to_logical(scale_factor);
let size = NSSize::new(logical_size.width, logical_size.height);
unsafe { NSWindow::setContentSize_(*ns_window, size) };
NSWindow::setContentSize(ns_window, size);
}
}
@@ -251,7 +249,7 @@ impl Handler {
suggested_size,
scale_factor,
} => {
self.handle_scale_factor_changed_event(callback, ns_window, suggested_size, scale_factor)
self.handle_scale_factor_changed_event(callback, &ns_window, suggested_size, scale_factor)
}
}
}
@@ -285,14 +283,12 @@ impl AppState {
pub fn launched(app_delegate: &Object) {
apply_activation_policy(app_delegate);
unsafe {
let ns_app = NSApp();
window_activation_hack(ns_app);
let ignore = if get_aux_state_mut(app_delegate).activate_ignoring_other_apps {
YES
} else {
NO
};
ns_app.activateIgnoringOtherApps_(ignore);
let mtm = MainThreadMarker::new().unwrap();
let ns_app = NSApp(mtm);
window_activation_hack(&ns_app);
let ignore = get_aux_state_mut(app_delegate).activate_ignoring_other_apps;
#[allow(deprecated)]
ns_app.activateIgnoringOtherApps(ignore);
};
HANDLER.set_ready();
HANDLER.waker().start();
@@ -399,12 +395,12 @@ impl AppState {
HANDLER.set_in_callback(false);
if HANDLER.should_exit() {
unsafe {
let app: id = NSApp();
let pool = NSAutoreleasePool::new(nil);
let () = msg_send![app, stop: nil];
let mtm = MainThreadMarker::new().unwrap();
let app = NSApp(mtm);
let _pool = NSAutoreleasePool::new();
let () = msg_send![&app, stop: nil];
// To stop event loop immediately, we need to post some event here.
post_dummy_event(app);
pool.drain();
post_dummy_event(&app);
};
}
HANDLER.update_start_time();
@@ -426,23 +422,24 @@ impl AppState {
///
/// If this becomes too bothersome to maintain, it can probably be removed
/// without too much damage.
unsafe fn window_activation_hack(ns_app: id) {
unsafe fn window_activation_hack(ns_app: &NSApplication) {
// Get the application's windows
// TODO: Proper ordering of the windows
let ns_windows: id = msg_send![ns_app, windows];
let ns_enumerator: id = msg_send![ns_windows, objectEnumerator];
loop {
// Enumerate over the windows
let ns_window: id = msg_send![ns_enumerator, nextObject];
if ns_window == nil {
let ns_window: Option<Retained<NSWindow>> = msg_send_id![ns_enumerator, nextObject];
if ns_window.is_none() {
break;
}
let ns_window = ns_window.unwrap();
// And call `makeKeyAndOrderFront` if it was called on the window in `UnownedWindow::new`
// This way we preserve the user's desired initial visiblity status
// TODO: Also filter on the type/"level" of the window, and maybe other things?
if ns_window.isVisible() == YES {
if ns_window.isVisible() == true {
trace!("Activating visible window");
ns_window.makeKeyAndOrderFront_(nil);
ns_window.makeKeyAndOrderFront(None);
} else {
trace!("Skipping activating invisible window");
}
@@ -450,16 +447,16 @@ unsafe fn window_activation_hack(ns_app: id) {
}
fn apply_activation_policy(app_delegate: &Object) {
unsafe {
use cocoa::appkit::NSApplicationActivationPolicy::*;
let ns_app = NSApp();
let mtm = MainThreadMarker::new().unwrap();
let ns_app = NSApp(mtm);
// We need to delay setting the activation policy and activating the app
// until `applicationDidFinishLaunching` has been called. Otherwise the
// menu bar won't be interactable.
let act_pol = get_aux_state_mut(app_delegate).activation_policy;
ns_app.setActivationPolicy_(match act_pol {
ActivationPolicy::Regular => NSApplicationActivationPolicyRegular,
ActivationPolicy::Accessory => NSApplicationActivationPolicyAccessory,
ActivationPolicy::Prohibited => NSApplicationActivationPolicyProhibited,
ns_app.setActivationPolicy(match act_pol {
ActivationPolicy::Regular => NSApplicationActivationPolicy::Regular,
ActivationPolicy::Accessory => NSApplicationActivationPolicy::Accessory,
ActivationPolicy::Prohibited => NSApplicationActivationPolicy::Prohibited,
});
}
}

View File

@@ -1,12 +1,16 @@
use cocoa::{appkit::NSApp, base::nil, foundation::NSString};
use super::ffi::id;
use objc2_app_kit::NSApp;
use objc2_foundation::{MainThreadMarker, NSString};
pub fn set_badge_label(label: Option<String>) {
// SAFETY: TODO
let mtm = unsafe { MainThreadMarker::new_unchecked() };
unsafe {
let label = match label {
None => nil,
Some(label) => NSString::alloc(nil).init_str(&label),
None => None,
Some(label) => Some(NSString::from_str(&label)),
};
let dock_tile: cocoa::base::id = msg_send![NSApp(), dockTile];
let _: cocoa::base::id = msg_send![dock_tile, setBadgeLabel: label];
let dock_tile: id = msg_send![&NSApp(mtm), dockTile];
let _: () = msg_send![dock_tile, setBadgeLabel: label.as_deref()];
}
}

View File

@@ -4,21 +4,17 @@
use std::{collections::HashSet, ffi::c_void, os::raw::c_ushort, sync::Mutex};
use cocoa::{
appkit::{NSEvent, NSEventModifierFlags},
base::id,
};
use objc2::{msg_send_id, rc::Retained};
use objc2_app_kit::{NSEvent, NSEventModifierFlags, NSWindow};
use core_foundation::{base::CFRelease, data::CFDataGetBytePtr};
use objc2_foundation::NSString;
use crate::{
dpi::LogicalSize,
event::{ElementState, Event, KeyEvent},
keyboard::{Key, KeyCode, KeyLocation, ModifiersState, NativeKeyCode},
platform_impl::platform::{
ffi,
util::{ns_string_to_rust, IdRef, Never},
},
platform_impl::platform::{ffi, util::Never},
};
lazy_static! {
@@ -47,7 +43,7 @@ pub enum EventWrapper {
pub enum EventProxy {
#[non_exhaustive]
DpiChangedProxy {
ns_window: IdRef,
ns_window: Retained<NSWindow>,
suggested_size: LogicalSize<f64>,
scale_factor: f64,
},
@@ -115,9 +111,10 @@ pub fn get_modifierless_char(scancode: u16) -> Key<'static> {
Key::Character(insert_or_get_key_str(chars))
}
fn get_logical_key_char(ns_event: id, modifierless_chars: &str) -> Key<'static> {
let characters: id = unsafe { msg_send![ns_event, charactersIgnoringModifiers] };
let string = unsafe { ns_string_to_rust(characters) };
fn get_logical_key_char(ns_event: &NSEvent, modifierless_chars: &str) -> Key<'static> {
let characters: Retained<NSString> =
unsafe { msg_send_id![ns_event, charactersIgnoringModifiers] };
let string = characters.to_string();
if string.is_empty() {
// Probably a dead key
let first_char = modifierless_chars.chars().next();
@@ -128,7 +125,7 @@ fn get_logical_key_char(ns_event: id, modifierless_chars: &str) -> Key<'static>
#[allow(clippy::unnecessary_unwrap)]
pub fn create_key_event(
ns_event: id,
ns_event: &NSEvent,
is_press: bool,
is_repeat: bool,
in_ime: bool,
@@ -144,8 +141,8 @@ pub fn create_key_event(
if key_override.is_some() {
None
} else {
let characters: id = unsafe { msg_send![ns_event, characters] };
let characters = unsafe { ns_string_to_rust(characters) };
let characters: Retained<NSString> = unsafe { msg_send_id![ns_event, characters] };
let characters = characters.to_string();
if characters.is_empty() {
None
} else {
@@ -168,8 +165,8 @@ pub fn create_key_event(
key_without_modifiers = get_modifierless_char(scancode);
let modifiers = unsafe { NSEvent::modifierFlags(ns_event) };
let has_alt = modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask);
let has_ctrl = modifiers.contains(NSEventModifierFlags::NSControlKeyMask);
let has_alt = modifiers.contains(NSEventModifierFlags::NSEventModifierFlagOption);
let has_ctrl = modifiers.contains(NSEventModifierFlags::NSEventModifierFlagControl);
if has_alt || has_ctrl || text_with_all_modifiers.is_none() || !is_press {
let modifierless_chars = match key_without_modifiers.clone() {
Key::Character(ch) => ch,
@@ -308,29 +305,29 @@ pub fn extra_function_key_to_code(scancode: u16, string: &str) -> KeyCode {
}
}
pub fn event_mods(event: id) -> ModifiersState {
pub fn event_mods(event: &NSEvent) -> ModifiersState {
let flags = unsafe { NSEvent::modifierFlags(event) };
let mut m = ModifiersState::empty();
m.set(
ModifiersState::SHIFT,
flags.contains(NSEventModifierFlags::NSShiftKeyMask),
flags.contains(NSEventModifierFlags::NSEventModifierFlagShift),
);
m.set(
ModifiersState::CONTROL,
flags.contains(NSEventModifierFlags::NSControlKeyMask),
flags.contains(NSEventModifierFlags::NSEventModifierFlagControl),
);
m.set(
ModifiersState::ALT,
flags.contains(NSEventModifierFlags::NSAlternateKeyMask),
flags.contains(NSEventModifierFlags::NSEventModifierFlagOption),
);
m.set(
ModifiersState::SUPER,
flags.contains(NSEventModifierFlags::NSCommandKeyMask),
flags.contains(NSEventModifierFlags::NSEventModifierFlagCommand),
);
m
}
pub fn get_scancode(event: cocoa::base::id) -> c_ushort {
pub fn get_scancode(event: &NSEvent) -> c_ushort {
// 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 tao, we use keycode to refer to the key's character, and so this function aligns

View File

@@ -14,12 +14,10 @@ use std::{
rc::{Rc, Weak},
};
use cocoa::{
appkit::{NSApp, NSEventModifierFlags, NSEventSubtype, NSEventType::NSApplicationDefined},
base::{id, nil, YES},
foundation::{NSAutoreleasePool, NSInteger, NSPoint, NSTimeInterval},
};
use crossbeam_channel::{self as channel, Receiver, Sender};
use objc2::{msg_send_id, rc::Retained};
use objc2_app_kit::{NSApp, NSApplication, NSEventModifierFlags, NSEventSubtype, NSEventType};
use objc2_foundation::{MainThreadMarker, NSAutoreleasePool, NSInteger, NSPoint, NSTimeInterval};
use scopeguard::defer;
use crate::{
@@ -33,6 +31,7 @@ use crate::{
app::APP_CLASS,
app_delegate::APP_DELEGATE_CLASS,
app_state::AppState,
ffi::{id, nil, YES},
monitor::{self, MonitorHandle},
observer::*,
util::{self, IdRef},
@@ -176,9 +175,8 @@ impl<T> EventLoop<T> {
let app: id = msg_send![APP_CLASS.0, sharedApplication];
let delegate = IdRef::new(msg_send![APP_DELEGATE_CLASS.0, new]);
let pool = NSAutoreleasePool::new(nil);
let _pool = NSAutoreleasePool::new();
let _: () = msg_send![app, setDelegate:*delegate];
let _: () = msg_send![pool, drain];
delegate
};
@@ -222,11 +220,11 @@ impl<T> EventLoop<T> {
self._callback = Some(Rc::clone(&callback));
let mtm = MainThreadMarker::new().unwrap();
let exit_code = unsafe {
let pool = NSAutoreleasePool::new(nil);
defer!(pool.drain());
let app = NSApp();
assert_ne!(app, nil);
let _pool = NSAutoreleasePool::new();
let app = NSApp(mtm);
// A bit of juggling with the callback references to make sure
// that `self.callback` is the only owner of the callback.
@@ -234,7 +232,7 @@ impl<T> EventLoop<T> {
mem::drop(callback);
AppState::set_callback(weak_cb, Rc::clone(&self.window_target));
let () = msg_send![app, run];
let () = msg_send![&app, run];
if let Some(panic) = self.panic_info.take() {
drop(self._callback.take());
@@ -253,17 +251,17 @@ impl<T> EventLoop<T> {
}
#[inline]
pub unsafe fn post_dummy_event(target: id) {
pub unsafe fn post_dummy_event(target: &NSApplication) {
let event_class = class!(NSEvent);
let dummy_event: id = msg_send![
event_class,
otherEventWithType: NSApplicationDefined
otherEventWithType: NSEventType::ApplicationDefined
location: NSPoint::new(0.0, 0.0)
modifierFlags: NSEventModifierFlags::empty()
timestamp: 0 as NSTimeInterval
windowNumber: 0 as NSInteger
context: nil
subtype: NSEventSubtype::NSWindowExposedEventType
subtype: NSEventSubtype::WindowExposed
data1: 0 as NSInteger
data2: 0 as NSInteger
];
@@ -290,12 +288,12 @@ pub fn stop_app_on_panic<F: FnOnce() -> R + UnwindSafe, R>(
}
unsafe {
let app_class = class!(NSApplication);
let app: id = msg_send![app_class, sharedApplication];
let () = msg_send![app, stop: nil];
let app: Retained<NSApplication> = msg_send_id![app_class, sharedApplication];
let () = msg_send![&app, stop: nil];
// Posting a dummy event to get `stop` to take effect immediately.
// See: https://stackoverflow.com/questions/48041279/stopping-the-nsapplication-main-event-loop/48064752#48064752
post_dummy_event(app);
post_dummy_event(&app);
}
None
}

View File

@@ -11,13 +11,8 @@
clippy::enum_variant_names
)]
use std::ffi::c_void;
use std::{ffi::c_void, ptr};
use cocoa::{
appkit::CGPoint,
base::id,
foundation::{NSInteger, NSUInteger},
};
use core_foundation::{
array::CFArrayRef, data::CFDataRef, dictionary::CFDictionaryRef, string::CFStringRef,
uuid::CFUUIDRef,
@@ -25,75 +20,27 @@ use core_foundation::{
use core_graphics::{
base::CGError,
display::{boolean_t, CGDirectDisplayID, CGDisplayConfigRef},
geometry::CGRect,
geometry::{CGPoint, CGRect},
};
use objc2::{
encode::{Encode, Encoding},
runtime::{AnyObject, Bool},
};
use objc2_foundation::NSInteger;
#[allow(non_camel_case_types)]
pub type id = *mut AnyObject;
pub const nil: id = ptr::null_mut();
#[allow(non_camel_case_types)]
pub type BOOL = Bool;
#[allow(deprecated)]
pub const YES: Bool = Bool::YES;
#[allow(deprecated)]
pub const NO: Bool = Bool::NO;
pub const NSNotFound: NSInteger = NSInteger::max_value();
#[repr(C)]
pub struct NSRange {
pub location: NSUInteger,
pub length: NSUInteger,
}
impl NSRange {
#[inline]
pub fn new(location: NSUInteger, length: NSUInteger) -> NSRange {
NSRange { location, length }
}
}
unsafe impl objc::Encode for NSRange {
fn encode() -> objc::Encoding {
let encoding = format!(
// TODO: Verify that this is correct
"{{NSRange={}{}}}",
NSUInteger::encode().as_str(),
NSUInteger::encode().as_str(),
);
unsafe { objc::Encoding::from_str(&encoding) }
}
}
pub trait NSMutableAttributedString: Sized {
unsafe fn alloc(_: Self) -> id {
msg_send![class!(NSMutableAttributedString), alloc]
}
unsafe fn init(self) -> id; // *mut NSMutableAttributedString
unsafe fn initWithString(self, string: id) -> id;
unsafe fn initWithAttributedString(self, string: id) -> id;
unsafe fn string(self) -> id; // *mut NSString
unsafe fn mutableString(self) -> id; // *mut NSMutableString
unsafe fn length(self) -> NSUInteger;
}
impl NSMutableAttributedString for id {
unsafe fn init(self) -> id {
msg_send![self, init]
}
unsafe fn initWithString(self, string: id) -> id {
msg_send![self, initWithString: string]
}
unsafe fn initWithAttributedString(self, string: id) -> id {
msg_send![self, initWithAttributedString: string]
}
unsafe fn string(self) -> id {
msg_send![self, string]
}
unsafe fn mutableString(self) -> id {
msg_send![self, mutableString]
}
unsafe fn length(self) -> NSUInteger {
msg_send![self, length]
}
}
pub const kCGBaseWindowLevelKey: NSInteger = 0;
pub const kCGMinimumWindowLevelKey: NSInteger = 1;
pub const kCGDesktopWindowLevelKey: NSInteger = 2;
@@ -130,6 +77,10 @@ pub enum NSWindowLevel {
NSScreenSaverWindowLevel = kCGScreenSaverWindowLevelKey as _,
}
unsafe impl Encode for NSWindowLevel {
const ENCODING: Encoding = isize::ENCODING;
}
pub type CGDisplayFadeInterval = f32;
pub type CGDisplayReservationInterval = f32;
pub type CGDisplayBlendFraction = f32;

View File

@@ -25,7 +25,7 @@ use std::{fmt, ops::Deref, sync::Arc};
pub(crate) use self::event_loop::PlatformSpecificEventLoopAttributes;
pub use self::{
app_delegate::{get_aux_state_mut, AuxDelegateState},
app_delegate::get_aux_state_mut,
event::KeyEventExtra,
event_loop::{EventLoop, EventLoopWindowTarget, Proxy as EventLoopProxy},
keycode::{keycode_from_scancode, keycode_to_scancode},

View File

@@ -4,25 +4,23 @@
use std::{collections::VecDeque, fmt};
use super::{
ffi::{self, CGRectContainsPoint},
util,
};
use super::ffi::{self, id, nil, CGRectContainsPoint};
use crate::{
dpi::{PhysicalPosition, PhysicalSize},
monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode},
};
use cocoa::{
appkit::{CGPoint, NSScreen},
base::{id, nil},
foundation::NSUInteger,
};
use core_foundation::{
array::{CFArrayGetCount, CFArrayGetValueAtIndex},
base::{CFRelease, TCFType},
string::CFString,
};
use core_graphics::display::{CGDirectDisplayID, CGDisplay, CGDisplayBounds};
use core_graphics::{
display::{CGDirectDisplayID, CGDisplay, CGDisplayBounds},
geometry::CGPoint,
};
use objc2::{msg_send_id, rc::Retained};
use objc2_app_kit::NSScreen;
use objc2_foundation::{MainThreadMarker, NSString, NSUInteger};
#[derive(Clone)]
pub struct VideoMode {
@@ -237,7 +235,7 @@ impl MonitorHandle {
Some(screen) => screen,
None => return 1.0, // default to 1.0 when we can't find the screen
};
unsafe { NSScreen::backingScaleFactor(screen) as f64 }
NSScreen::backingScaleFactor(&screen) as f64
}
pub fn video_modes(&self) -> impl Iterator<Item = RootVideoMode> {
@@ -313,16 +311,18 @@ impl MonitorHandle {
}
}
pub(crate) fn ns_screen(&self) -> Option<id> {
pub(crate) fn ns_screen(&self) -> Option<Retained<NSScreen>> {
// SAFETY: TODO.
let mtm = unsafe { MainThreadMarker::new_unchecked() };
unsafe {
let uuid = ffi::CGDisplayCreateUUIDFromDisplayID(self.0);
let screens = NSScreen::screens(nil);
let count: NSUInteger = msg_send![screens, count];
let key = util::ns_string_id_ref("NSScreenNumber");
let screens = NSScreen::screens(mtm);
let count: NSUInteger = msg_send![&screens, count];
let key = NSString::from_str("NSScreenNumber");
for i in 0..count {
let screen = msg_send![screens, objectAtIndex: i as NSUInteger];
let device_description = NSScreen::deviceDescription(screen);
let value: id = msg_send![device_description, objectForKey:*key];
let screen: Retained<NSScreen> = msg_send_id![&screens, objectAtIndex: i as NSUInteger];
let device_description = NSScreen::deviceDescription(&screen);
let value: id = msg_send![&device_description, objectForKey: &*key];
if value != nil {
let other_native_id: NSUInteger = msg_send![value, unsignedIntegerValue];
let other_uuid =

View File

@@ -1,14 +1,13 @@
use std::sync::Once;
use cocoa::{
base::{id, nil},
foundation::{NSArray, NSPoint, NSRect, NSSize},
};
use objc::{
declare::ClassDecl,
runtime::{Class, Object, Sel, NO},
use objc2::{
msg_send_id,
rc::Retained,
runtime::{AnyClass as Class, AnyObject as Object, ClassBuilder as ClassDecl, Sel},
};
use objc2_foundation::{NSArray, NSInsetRect, NSPoint, NSRect, NSSize};
use super::ffi::{id, nil, NO};
use crate::window::{ProgressBarState, ProgressState};
/// Set progress indicator in the Dock.
@@ -30,8 +29,9 @@ pub fn set_progress_indicator(progress_state: ProgressBarState) {
let _: () = msg_send![progress_indicator, setDoubleValue: progress];
let _: () = msg_send![progress_indicator, setHidden: NO];
}
#[allow(deprecated)] // TODO: Use define_class!
if let Some(state) = progress_state.state {
(*progress_indicator).set_ivar("state", state as u8);
*(*progress_indicator).get_mut_ivar("state") = state as u8;
let _: () = msg_send![
progress_indicator,
setHidden: matches!(state, ProgressState::None)
@@ -61,7 +61,7 @@ fn create_progress_indicator(ns_app: id, dock_tile: id) -> id {
let progress_class = create_progress_indicator_class();
let progress_indicator: id = msg_send![progress_class, alloc];
let progress_indicator: id = msg_send![progress_indicator, initWithFrame: frame];
let _: () = msg_send![progress_indicator, autorelease];
let _: id = msg_send![progress_indicator, autorelease];
// set progress indicator to the dock tile
let _: () = msg_send![image_view, addSubview: progress_indicator];
@@ -76,13 +76,14 @@ fn get_exist_progress_indicator(dock_tile: id) -> Option<id> {
if content_view == nil {
return None;
}
let subviews: id /* NSArray */ = msg_send![content_view, subviews];
if subviews == nil {
let subviews: Option<Retained<NSArray>> = msg_send_id![content_view, subviews];
if subviews.is_none() {
return None;
}
let subviews = subviews.unwrap();
for idx in 0..subviews.count() {
let subview: id = msg_send![subviews, objectAtIndex: idx];
let subview: id = msg_send![&subviews, objectAtIndex: idx];
let is_progress_indicator: bool =
msg_send![subview, isKindOfClass: class!(NSProgressIndicator)];
@@ -102,10 +103,7 @@ fn create_progress_indicator_class() -> *const Class {
let superclass = class!(NSProgressIndicator);
let mut decl = ClassDecl::new("TaoProgressIndicator", superclass).unwrap();
decl.add_method(
sel!(drawRect:),
draw_progress_bar as extern "C" fn(&Object, _, NSRect),
);
decl.add_method(sel!(drawRect:), draw_progress_bar as extern "C" fn(_, _, _));
// progress bar states, follows ProgressState
decl.add_ivar::<u8>("state");
@@ -117,6 +115,7 @@ fn create_progress_indicator_class() -> *const Class {
}
extern "C" fn draw_progress_bar(this: &Object, _: Sel, rect: NSRect) {
#[allow(deprecated)] // TODO: Use define_class!
unsafe {
let bar = NSRect::new(
NSPoint { x: 0.0, y: 4.0 },
@@ -125,8 +124,8 @@ extern "C" fn draw_progress_bar(this: &Object, _: Sel, rect: NSRect) {
height: 8.0,
},
);
let bar_inner = bar.inset(0.5, 0.5);
let mut bar_progress = bar.inset(1.0, 1.0);
let bar_inner = NSInsetRect(bar, 0.5, 0.5);
let mut bar_progress = NSInsetRect(bar, 1.0, 1.0);
// set progress width
let current_progress: f64 = msg_send![this, doubleValue];

View File

@@ -7,25 +7,22 @@ use std::{
sync::{Mutex, Weak},
};
use cocoa::{
appkit::{CGFloat, NSScreen, NSWindow, NSWindowStyleMask},
base::{id, nil},
foundation::{NSPoint, NSSize, NSString},
};
use core_graphics::base::CGFloat;
use dispatch::Queue;
use objc::{
rc::autoreleasepool,
runtime::{BOOL, NO, YES},
};
use objc2::{rc::autoreleasepool, ClassType};
use objc2_app_kit::{NSScreen, NSView, NSWindow, NSWindowStyleMask};
use objc2_foundation::{MainThreadMarker, NSPoint, NSSize, NSString};
use crate::{
dpi::LogicalSize,
platform_impl::platform::{ffi, util::IdRef, window::SharedState},
platform_impl::platform::{
ffi::{self, id, NO, YES},
window::SharedState,
},
};
pub fn is_main_thread() -> bool {
let is: BOOL = unsafe { msg_send!(class!(NSThread), isMainThread) };
is == YES
unsafe { msg_send!(class!(NSThread), isMainThread) }
}
// Unsafe wrapper type that allows us to dispatch things that aren't Send.
@@ -51,72 +48,76 @@ fn run_on_main<R: Send>(f: impl FnOnce() -> R + Send) -> R {
}
}
unsafe fn set_style_mask(ns_window: id, ns_view: id, mask: NSWindowStyleMask) {
ns_window.setStyleMask_(mask);
unsafe fn set_style_mask(ns_window: &NSWindow, ns_view: &NSView, mask: NSWindowStyleMask) {
ns_window.setStyleMask(mask);
// If we don't do this, key handling will break
// (at least until the window is clicked again/etc.)
ns_window.makeFirstResponder_(ns_view);
ns_window.makeFirstResponder(Some(ns_view));
}
// Always use this function instead of trying to modify `styleMask` directly!
// `setStyleMask:` isn't thread-safe, so we have to use Grand Central Dispatch.
// Otherwise, this would vomit out errors about not being on the main thread
// and fail to do anything.
pub unsafe fn set_style_mask_async(ns_window: id, ns_view: id, mask: NSWindowStyleMask) {
let ns_window = MainThreadSafe(ns_window);
let ns_view = MainThreadSafe(ns_view);
pub unsafe fn set_style_mask_async(
ns_window: &NSWindow,
ns_view: &NSView,
mask: NSWindowStyleMask,
) {
let ns_window = MainThreadSafe(ns_window.retain());
let ns_view = MainThreadSafe(ns_view.retain());
Queue::main().exec_async(move || {
set_style_mask(*ns_window, *ns_view, mask);
set_style_mask(&*ns_window, &*ns_view, mask);
});
}
pub unsafe fn set_style_mask_sync(ns_window: id, ns_view: id, mask: NSWindowStyleMask) {
pub unsafe fn set_style_mask_sync(ns_window: &NSWindow, ns_view: &NSView, mask: NSWindowStyleMask) {
if is_main_thread() {
set_style_mask(ns_window, ns_view, mask);
} else {
let ns_window = MainThreadSafe(ns_window);
let ns_view = MainThreadSafe(ns_view);
let ns_window = MainThreadSafe(ns_window.retain());
let ns_view = MainThreadSafe(ns_view.retain());
Queue::main().exec_sync(move || {
set_style_mask(*ns_window, *ns_view, mask);
set_style_mask(&*ns_window, &*ns_view, mask);
})
}
}
// `setContentSize:` isn't thread-safe either, though it doesn't log any errors
// and just fails silently. Anyway, GCD to the rescue!
pub unsafe fn set_content_size_async(ns_window: id, size: LogicalSize<f64>) {
let ns_window = MainThreadSafe(ns_window);
pub unsafe fn set_content_size_async(ns_window: &NSWindow, size: LogicalSize<f64>) {
let ns_window = MainThreadSafe(ns_window.retain());
Queue::main().exec_async(move || {
ns_window.setContentSize_(NSSize::new(size.width as CGFloat, size.height as CGFloat));
ns_window.setContentSize(NSSize::new(size.width as CGFloat, size.height as CGFloat));
});
}
// `setFrameTopLeftPoint:` isn't thread-safe, but fortunately has the courtesy
// to log errors.
pub unsafe fn set_frame_top_left_point_async(ns_window: id, point: NSPoint) {
let ns_window = MainThreadSafe(ns_window);
pub unsafe fn set_frame_top_left_point_async(ns_window: &NSWindow, point: NSPoint) {
let ns_window = MainThreadSafe(ns_window.retain());
Queue::main().exec_async(move || {
ns_window.setFrameTopLeftPoint_(point);
ns_window.setFrameTopLeftPoint(point);
});
}
// `setFrameTopLeftPoint:` isn't thread-safe, and fails silently.
pub unsafe fn set_level_async(ns_window: id, level: ffi::NSWindowLevel) {
let ns_window = MainThreadSafe(ns_window);
pub unsafe fn set_level_async(ns_window: &NSWindow, level: ffi::NSWindowLevel) {
let ns_window = MainThreadSafe(ns_window.retain());
Queue::main().exec_async(move || {
ns_window.setLevel_(level as _);
ns_window.setLevel(level as _);
});
}
// `toggleFullScreen` is thread-safe, but our additional logic to account for
// window styles isn't.
pub unsafe fn toggle_full_screen_async(
ns_window: id,
ns_view: id,
ns_window: &NSWindow,
ns_view: &NSView,
not_fullscreen: bool,
shared_state: Weak<Mutex<SharedState>>,
) {
let ns_window = MainThreadSafe(ns_window);
let ns_view = MainThreadSafe(ns_view);
let ns_window = MainThreadSafe(ns_window.retain());
let ns_view = MainThreadSafe(ns_view.retain());
let shared_state = MainThreadSafe(shared_state);
Queue::main().exec_async(move || {
// `toggleFullScreen` doesn't work if the `StyleMask` is none, so we
@@ -124,10 +125,9 @@ pub unsafe fn toggle_full_screen_async(
// restored in `WindowDelegate::window_did_exit_fullscreen`.
if not_fullscreen {
let curr_mask = ns_window.styleMask();
let required =
NSWindowStyleMask::NSTitledWindowMask | NSWindowStyleMask::NSResizableWindowMask;
let required = NSWindowStyleMask::Titled | NSWindowStyleMask::Resizable;
if !curr_mask.contains(required) {
set_style_mask(*ns_window, *ns_view, required);
set_style_mask(&ns_window, &ns_view, required);
if let Some(shared_state) = shared_state.upgrade() {
trace!("Locked shared state in `toggle_full_screen_callback`");
let mut shared_state_lock = shared_state.lock().unwrap();
@@ -139,8 +139,8 @@ pub unsafe fn toggle_full_screen_async(
// Window level must be restored from `CGShieldingWindowLevel()
// + 1` back to normal in order for `toggleFullScreen` to do
// anything
ns_window.setLevel_(0);
ns_window.toggleFullScreen_(nil);
ns_window.setLevel(0);
ns_window.toggleFullScreen(None);
});
}
@@ -153,12 +153,12 @@ pub unsafe fn restore_display_mode_async(ns_screen: u32) {
// `setMaximized` is not thread-safe
pub unsafe fn set_maximized_async(
ns_window: id,
ns_window: &NSWindow,
is_zoomed: bool,
maximized: bool,
shared_state: Weak<Mutex<SharedState>>,
) {
let ns_window = MainThreadSafe(ns_window);
let ns_window = MainThreadSafe(ns_window.retain());
let shared_state = MainThreadSafe(shared_state);
Queue::main().exec_async(move || {
if let Some(shared_state) = shared_state.upgrade() {
@@ -167,7 +167,7 @@ pub unsafe fn set_maximized_async(
// Save the standard frame sized if it is not zoomed
if !is_zoomed {
shared_state_lock.standard_frame = Some(NSWindow::frame(*ns_window));
shared_state_lock.standard_frame = Some(NSWindow::frame(&ns_window));
}
shared_state_lock.maximized = maximized;
@@ -176,20 +176,21 @@ pub unsafe fn set_maximized_async(
if shared_state_lock.fullscreen.is_some() {
// Handle it in window_did_exit_fullscreen
return;
} else if curr_mask.contains(NSWindowStyleMask::NSResizableWindowMask)
&& curr_mask.contains(NSWindowStyleMask::NSTitledWindowMask)
} else if curr_mask.contains(NSWindowStyleMask::Resizable)
&& curr_mask.contains(NSWindowStyleMask::Titled)
{
// Just use the native zoom if resizable
ns_window.zoom_(nil);
ns_window.zoom(None);
} else {
// if it's not resizable, we set the frame directly
let new_rect = if maximized {
let screen = NSScreen::mainScreen(nil);
NSScreen::visibleFrame(screen)
let mtm = MainThreadMarker::new_unchecked();
let screen = NSScreen::mainScreen(mtm).unwrap();
NSScreen::visibleFrame(&screen)
} else {
shared_state_lock.saved_standard_frame()
};
let _: () = msg_send![*ns_window, setFrame:new_rect display:NO animate: YES];
let _: () = msg_send![&*ns_window, setFrame:new_rect display:NO animate: YES];
}
trace!("Unlocked shared state in `set_maximized`");
@@ -199,38 +200,38 @@ pub unsafe fn set_maximized_async(
// `orderOut:` isn't thread-safe. Calling it from another thread actually works,
// but with an odd delay.
pub unsafe fn order_out_sync(ns_window: id) {
let ns_window = MainThreadSafe(ns_window);
pub unsafe fn order_out_sync(ns_window: &NSWindow) {
let ns_window = MainThreadSafe(ns_window.retain());
run_on_main(move || {
ns_window.orderOut_(nil);
ns_window.orderOut(None);
});
}
// `makeKeyAndOrderFront:` isn't thread-safe. Calling it from another thread
// actually works, but with an odd delay.
pub unsafe fn make_key_and_order_front_sync(ns_window: id) {
let ns_window = MainThreadSafe(ns_window);
pub unsafe fn make_key_and_order_front_sync(ns_window: &NSWindow) {
let ns_window = MainThreadSafe(ns_window.retain());
run_on_main(move || {
ns_window.makeKeyAndOrderFront_(nil);
ns_window.makeKeyAndOrderFront(None);
});
}
// `setTitle:` isn't thread-safe. Calling it from another thread invalidates the
// window drag regions, which throws an exception when not done in the main
// thread
pub unsafe fn set_title_async(ns_window: id, title: String) {
let ns_window = MainThreadSafe(ns_window);
pub unsafe fn set_title_async(ns_window: &NSWindow, title: String) {
let ns_window = MainThreadSafe(ns_window.retain());
Queue::main().exec_async(move || {
let title = IdRef::new(NSString::alloc(nil).init_str(&title));
ns_window.setTitle_(*title);
let title = NSString::from_str(&title);
ns_window.setTitle(&title);
});
}
// `setFocus:` isn't thread-safe.
pub unsafe fn set_focus(ns_window: id) {
let ns_window = MainThreadSafe(ns_window);
pub unsafe fn set_focus(ns_window: &NSWindow) {
let ns_window = MainThreadSafe(ns_window.retain());
run_on_main(move || {
ns_window.makeKeyAndOrderFront_(nil);
ns_window.makeKeyAndOrderFront(None);
let app: id = msg_send![class!(NSApplication), sharedApplication];
let () = msg_send![app, activateIgnoringOtherApps: YES];
});
@@ -238,22 +239,19 @@ pub unsafe fn set_focus(ns_window: id) {
// `close:` is thread-safe, but we want the event to be triggered from the main
// thread. Though, it's a good idea to look into that more...
//
// ArturKovacs: It's important that this operation keeps the underlying window alive
// through the `IdRef` because otherwise it would dereference free'd memory
pub unsafe fn close_async(ns_window: IdRef) {
let ns_window = MainThreadSafe(ns_window);
pub unsafe fn close_async(ns_window: &NSWindow) {
let ns_window = MainThreadSafe(ns_window.retain());
run_on_main(move || {
autoreleasepool(move || {
autoreleasepool(move |_| {
ns_window.close();
});
});
}
// `setIgnoresMouseEvents_:` isn't thread-safe, and fails silently.
pub unsafe fn set_ignore_mouse_events(ns_window: id, ignore: bool) {
let ns_window = MainThreadSafe(ns_window);
pub unsafe fn set_ignore_mouse_events(ns_window: &NSWindow, ignore: bool) {
let ns_window = MainThreadSafe(ns_window.retain());
Queue::main().exec_async(move || {
ns_window.setIgnoresMouseEvents_(if ignore { YES } else { NO });
ns_window.setIgnoresMouseEvents(ignore);
});
}

View File

@@ -2,13 +2,16 @@
// Copyright 2021-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
use cocoa::{
appkit::NSImage,
base::{id, nil},
foundation::{NSDictionary, NSPoint, NSString},
use crate::platform_impl::platform::ffi::{id, nil, NO};
use objc2::{
msg_send_id,
rc::Retained,
runtime::{AnyObject, Sel},
ClassType,
};
use objc::runtime::{Sel, NO};
use std::{cell::RefCell, ptr::null_mut};
use objc2_app_kit::NSImage;
use objc2_foundation::{NSDictionary, NSPoint, NSString};
use std::{cell::RefCell, ffi::c_void, ptr::null_mut};
use crate::window::CursorIcon;
@@ -108,26 +111,32 @@ impl Cursor {
// instead you'll just get them all in a column.
pub unsafe fn load_webkit_cursor(cursor_name: &str) -> id {
static CURSOR_ROOT: &str = "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/Resources/cursors";
let cursor_root = NSString::alloc(nil).init_str(CURSOR_ROOT);
let cursor_name = NSString::alloc(nil).init_str(cursor_name);
let cursor_pdf = NSString::alloc(nil).init_str("cursor.pdf");
let cursor_plist = NSString::alloc(nil).init_str("info.plist");
let key_x = NSString::alloc(nil).init_str("hotx");
let key_y = NSString::alloc(nil).init_str("hoty");
let cursor_root = NSString::from_str(CURSOR_ROOT);
let cursor_name = NSString::from_str(cursor_name);
let cursor_pdf = NSString::from_str("cursor.pdf");
let cursor_plist = NSString::from_str("info.plist");
let key_x = NSString::from_str("hotx");
let key_y = NSString::from_str("hoty");
let cursor_path: id = msg_send![cursor_root, stringByAppendingPathComponent: cursor_name];
let pdf_path: id = msg_send![cursor_path, stringByAppendingPathComponent: cursor_pdf];
let info_path: id = msg_send![cursor_path, stringByAppendingPathComponent: cursor_plist];
let cursor_path: Retained<NSString> =
msg_send_id![&cursor_root, stringByAppendingPathComponent: &*cursor_name];
let pdf_path: Retained<NSString> =
msg_send_id![&cursor_path, stringByAppendingPathComponent: &*cursor_pdf];
let info_path: Retained<NSString> =
msg_send_id![&cursor_path, stringByAppendingPathComponent: &*cursor_plist];
let image = NSImage::alloc(nil).initByReferencingFile_(pdf_path);
let info = NSDictionary::dictionaryWithContentsOfFile_(nil, info_path);
let x = info.valueForKey_(key_x);
let y = info.valueForKey_(key_y);
let point = NSPoint::new(msg_send![x, doubleValue], msg_send![y, doubleValue]);
let image = NSImage::initByReferencingFile(NSImage::alloc(), &pdf_path).unwrap();
#[allow(deprecated)]
let info =
NSDictionary::<AnyObject, AnyObject>::dictionaryWithContentsOfFile(&info_path).unwrap();
let x = info.objectForKey(&key_x).unwrap();
let y = info.objectForKey(&key_y).unwrap();
let point = NSPoint::new(msg_send![&x, doubleValue], msg_send![&y, doubleValue]);
let cursor: id = msg_send![class!(NSCursor), alloc];
msg_send![cursor,
initWithImage:image
hotSpot:point
msg_send![
cursor,
initWithImage:&*image,
hotSpot:point,
]
}
@@ -150,10 +159,11 @@ pub unsafe fn invisible_cursor() -> id {
CURSOR_OBJECT.with(|cursor_obj| {
if *cursor_obj.borrow() == nil {
// Create a cursor from `CURSOR_BYTES`
let cursor_data: id = msg_send![class!(NSData),
dataWithBytesNoCopy:CURSOR_BYTES as *const [u8]
length:CURSOR_BYTES.len()
freeWhenDone:NO
let cursor_data: id = msg_send![
class!(NSData),
dataWithBytesNoCopy:CURSOR_BYTES.as_ptr().cast::<c_void>(),
length:CURSOR_BYTES.len(),
freeWhenDone:NO,
];
let ns_image: id = msg_send![class!(NSImage), alloc];

View File

@@ -7,26 +7,20 @@ mod cursor;
pub use self::{cursor::*, r#async::*};
use std::{
ops::{BitAnd, Deref},
slice, str,
};
use std::ops::{BitAnd, Deref};
use cocoa::{
appkit::{NSApp, NSWindowStyleMask},
base::{id, nil},
foundation::{NSAutoreleasePool, NSPoint, NSRect, NSString, NSUInteger},
};
use core_graphics::display::CGDisplay;
use objc::{
use objc2::{
class,
runtime::{Class, Object, Sel, BOOL, YES},
runtime::{AnyClass as Class, AnyObject as Object, Sel},
};
use objc2_app_kit::{NSApp, NSView, NSWindow, NSWindowStyleMask};
use objc2_foundation::{MainThreadMarker, NSAutoreleasePool, NSPoint, NSRange, NSRect, NSUInteger};
use crate::{
dpi::{LogicalPosition, PhysicalPosition},
error::ExternalError,
platform_impl::platform::ffi,
platform_impl::platform::ffi::{self, id, nil, BOOL, YES},
};
// Replace with `!` once stable
@@ -40,7 +34,7 @@ where
bitset & flag == flag
}
pub const EMPTY_RANGE: ffi::NSRange = ffi::NSRange {
pub const EMPTY_RANGE: NSRange = NSRange {
location: ffi::NSNotFound as NSUInteger,
length: 0,
};
@@ -60,23 +54,14 @@ impl IdRef {
}
IdRef(inner)
}
pub fn non_nil(self) -> Option<IdRef> {
if self.0 == nil {
None
} else {
Some(self)
}
}
}
impl Drop for IdRef {
fn drop(&mut self) {
if self.0 != nil {
unsafe {
let pool = NSAutoreleasePool::new(nil);
let _pool = NSAutoreleasePool::new();
let () = msg_send![self.0, release];
pool.drain();
};
}
}
@@ -120,23 +105,12 @@ pub fn cursor_position() -> Result<PhysicalPosition<f64>, ExternalError> {
Ok(point.to_physical(super::monitor::primary_monitor().scale_factor()))
}
pub unsafe fn ns_string_id_ref(s: &str) -> IdRef {
IdRef::new(NSString::alloc(nil).init_str(s))
}
/// Copies the contents of the ns string into a `String` which gets returned.
pub unsafe fn ns_string_to_rust(ns_string: id) -> String {
let slice = slice::from_raw_parts(ns_string.UTF8String() as *mut u8, ns_string.len());
let string = str::from_utf8_unchecked(slice);
string.to_owned()
}
pub unsafe fn superclass<'a>(this: &'a Object) -> &'a Class {
let superclass: *const Class = msg_send![this, superclass];
&*superclass
}
pub unsafe fn create_input_context(view: id) -> IdRef {
pub unsafe fn create_input_context(view: &NSView) -> IdRef {
let input_context: id = msg_send![class!(NSTextInputContext), alloc];
let input_context: id = msg_send![input_context, initWithClient: view];
IdRef::new(input_context)
@@ -144,23 +118,28 @@ pub unsafe fn create_input_context(view: id) -> IdRef {
#[allow(dead_code)]
pub unsafe fn open_emoji_picker() {
let () = msg_send![NSApp(), orderFrontCharacterPalette: nil];
// SAFETY: TODO
let mtm = unsafe { MainThreadMarker::new_unchecked() };
let () = msg_send![&NSApp(mtm), orderFrontCharacterPalette: nil];
}
pub extern "C" fn yes(_: &Object, _: Sel) -> BOOL {
YES
}
pub unsafe fn toggle_style_mask(window: id, view: id, mask: NSWindowStyleMask, on: bool) {
use cocoa::appkit::NSWindow;
pub unsafe fn toggle_style_mask(
window: &NSWindow,
view: &NSView,
mask: NSWindowStyleMask,
on: bool,
) {
let current_style_mask = window.styleMask();
if on {
window.setStyleMask_(current_style_mask | mask);
window.setStyleMask(current_style_mask | mask);
} else {
window.setStyleMask_(current_style_mask & (!mask));
window.setStyleMask(current_style_mask & (!mask));
}
// If we don't do this, key handling will break. Therefore, never call `setStyleMask` directly!
window.makeFirstResponder_(view);
window.makeFirstResponder(Some(view));
}

View File

@@ -1,23 +1,31 @@
// Copyright 2014-2021 The winit contributors
// Copyright 2021-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
#![allow(unused_unsafe)]
#![allow(deprecated)] // TODO: Use define_class!
use std::{
boxed::Box,
collections::{HashSet, VecDeque},
os::raw::*,
ptr, slice, str,
ptr,
sync::{Arc, Mutex, Weak},
};
use cocoa::{
appkit::{NSApp, NSEvent, NSEventModifierFlags, NSEventPhase, NSView, NSWindow, NSWindowButton},
base::{id, nil},
foundation::{NSInteger, NSPoint, NSRect, NSSize, NSString, NSUInteger},
use objc2::{
msg_send_id,
rc::Retained,
runtime::{
AnyClass as Class, AnyObject as Object, AnyProtocol as Protocol, ClassBuilder as ClassDecl, Sel,
},
ClassType,
};
use objc::{
declare::ClassDecl,
runtime::{Class, Object, Protocol, Sel, BOOL, NO, YES},
use objc2_app_kit::{
NSApp, NSEvent, NSEventModifierFlags, NSEventPhase, NSView, NSWindow, NSWindowButton,
};
use objc2_foundation::{
MainThreadMarker, NSAttributedString, NSInteger, NSMutableAttributedString, NSPoint, NSRange,
NSRect, NSSize, NSString, NSUInteger,
};
use crate::{
@@ -30,7 +38,7 @@ use crate::{
app_state::AppState,
event::{code_to_key, create_key_event, event_mods, get_scancode, EventWrapper},
ffi::*,
util::{self, IdRef},
util::{self},
window::get_window_id,
DEVICE_ID,
},
@@ -52,7 +60,7 @@ impl Default for CursorState {
}
pub(super) struct ViewState {
ns_window: id,
ns_window: objc2::rc::Weak<NSWindow>,
pub cursor_state: Arc<Mutex<CursorState>>,
ime_spot: Option<(f64, f64)>,
@@ -75,15 +83,15 @@ pub(super) struct ViewState {
impl ViewState {
fn get_scale_factor(&self) -> f64 {
(unsafe { NSWindow::backingScaleFactor(self.ns_window) }) as f64
(unsafe { NSWindow::backingScaleFactor(&self.ns_window.load().unwrap()) }) as f64
}
}
pub fn new_view(ns_window: id) -> (IdRef, Weak<Mutex<CursorState>>) {
pub fn new_view(ns_window: &NSWindow) -> (Option<Retained<NSView>>, Weak<Mutex<CursorState>>) {
let cursor_state = Default::default();
let cursor_access = Arc::downgrade(&cursor_state);
let state = ViewState {
ns_window,
ns_window: objc2::rc::Weak::from(ns_window),
cursor_state,
ime_spot: None,
in_ime_preedit: false,
@@ -97,19 +105,18 @@ pub fn new_view(ns_window: id) -> (IdRef, Weak<Mutex<CursorState>>) {
unsafe {
// This is free'd in `dealloc`
let state_ptr = Box::into_raw(Box::new(state)) as *mut c_void;
let ns_view: id = msg_send![VIEW_CLASS.0, alloc];
(
IdRef::new(msg_send![ns_view, initWithTao: state_ptr]),
cursor_access,
)
let ns_view = msg_send_id![VIEW_CLASS.0, alloc];
(msg_send_id![ns_view, initWithTao: state_ptr], cursor_access)
}
}
pub unsafe fn set_ime_position(ns_view: id, input_context: id, x: f64, y: f64) {
let state_ptr: *mut c_void = *(*ns_view).get_mut_ivar("taoState");
pub unsafe fn set_ime_position(ns_view: &NSView, input_context: id, x: f64, y: f64) {
let state_ptr: *mut c_void = *ns_view.get_ivar("taoState");
let state = &mut *(state_ptr as *mut ViewState);
let content_rect =
NSWindow::contentRectForFrameRect_(state.ns_window, NSWindow::frame(state.ns_window));
let content_rect = NSWindow::contentRectForFrameRect(
&state.ns_window.load().unwrap(),
NSWindow::frame(&state.ns_window.load().unwrap()),
);
let base_x = content_rect.origin.x as f64;
let base_y = (content_rect.origin.y + content_rect.size.height) as f64;
state.ime_spot = Some((base_x + x, base_y - y));
@@ -123,17 +130,7 @@ fn is_arrow_key(keycode: KeyCode) -> bool {
)
}
/// `view` must be the reference to the `TaoView` class
///
/// Returns the mutable reference to the `markedText` field.
unsafe fn clear_marked_text(view: &mut Object) -> &mut id {
let marked_text_ref: &mut id = view.get_mut_ivar("markedText");
let () = msg_send![(*marked_text_ref), release];
*marked_text_ref = NSMutableAttributedString::alloc(nil);
marked_text_ref
}
struct ViewClass(*const Class);
struct ViewClass(&'static Class);
unsafe impl Send for ViewClass {}
unsafe impl Sync for ViewClass {}
@@ -141,161 +138,121 @@ lazy_static! {
static ref VIEW_CLASS: ViewClass = unsafe {
let superclass = class!(NSView);
let mut decl = ClassDecl::new("TaoView", superclass).unwrap();
decl.add_method(sel!(dealloc), dealloc as extern "C" fn(&Object, Sel));
decl.add_method(sel!(dealloc), dealloc as extern "C" fn(_, _));
decl.add_method(
sel!(initWithTao:),
init_with_tao as extern "C" fn(&Object, Sel, *mut c_void) -> id,
init_with_tao as extern "C" fn(_, _, _) -> _,
);
decl.add_method(
sel!(viewDidMoveToWindow),
view_did_move_to_window as extern "C" fn(&Object, Sel),
);
decl.add_method(
sel!(drawRect:),
draw_rect as extern "C" fn(&Object, Sel, NSRect),
view_did_move_to_window as extern "C" fn(_, _),
);
decl.add_method(sel!(drawRect:), draw_rect as extern "C" fn(_, _, _));
decl.add_method(
sel!(acceptsFirstResponder),
accepts_first_responder as extern "C" fn(&Object, Sel) -> BOOL,
);
decl.add_method(
sel!(touchBar),
touch_bar as extern "C" fn(&Object, Sel) -> BOOL,
accepts_first_responder as extern "C" fn(_, _) -> _,
);
decl.add_method(sel!(touchBar), touch_bar as extern "C" fn(_, _) -> _);
decl.add_method(
sel!(resetCursorRects),
reset_cursor_rects as extern "C" fn(&Object, Sel),
reset_cursor_rects as extern "C" fn(_, _),
);
decl.add_method(
sel!(hasMarkedText),
has_marked_text as extern "C" fn(&Object, Sel) -> BOOL,
);
decl.add_method(
sel!(markedRange),
marked_range as extern "C" fn(&Object, Sel) -> NSRange,
has_marked_text as extern "C" fn(_, _) -> _,
);
decl.add_method(sel!(markedRange), marked_range as extern "C" fn(_, _) -> _);
decl.add_method(
sel!(selectedRange),
selected_range as extern "C" fn(&Object, Sel) -> NSRange,
selected_range as extern "C" fn(_, _) -> _,
);
decl.add_method(
sel!(setMarkedText:selectedRange:replacementRange:),
set_marked_text as extern "C" fn(&mut Object, Sel, id, NSRange, NSRange),
);
decl.add_method(
sel!(unmarkText),
unmark_text as extern "C" fn(&mut Object, Sel),
set_marked_text as extern "C" fn(_, _, _, _, _),
);
decl.add_method(sel!(unmarkText), unmark_text as extern "C" fn(_, _));
decl.add_method(
sel!(validAttributesForMarkedText),
valid_attributes_for_marked_text as extern "C" fn(&Object, Sel) -> id,
valid_attributes_for_marked_text as extern "C" fn(_, _) -> _,
);
decl.add_method(
sel!(attributedSubstringForProposedRange:actualRange:),
attributed_substring_for_proposed_range
as extern "C" fn(&Object, Sel, NSRange, *mut c_void) -> id,
attributed_substring_for_proposed_range as extern "C" fn(_, _, _, _) -> _,
);
decl.add_method(
sel!(insertText:replacementRange:),
insert_text as extern "C" fn(&Object, Sel, id, NSRange),
insert_text as extern "C" fn(_, _, _, _),
);
decl.add_method(
sel!(characterIndexForPoint:),
character_index_for_point as extern "C" fn(&Object, Sel, NSPoint) -> NSUInteger,
character_index_for_point as extern "C" fn(_, _, _) -> _,
);
decl.add_method(
sel!(firstRectForCharacterRange:actualRange:),
first_rect_for_character_range as extern "C" fn(&Object, Sel, NSRange, *mut c_void) -> NSRect,
first_rect_for_character_range as extern "C" fn(_, _, _, _) -> _,
);
decl.add_method(
sel!(doCommandBySelector:),
do_command_by_selector as extern "C" fn(&Object, Sel, Sel),
);
decl.add_method(
sel!(keyDown:),
key_down as extern "C" fn(&mut Object, Sel, id),
);
decl.add_method(sel!(keyUp:), key_up as extern "C" fn(&Object, Sel, id));
decl.add_method(
sel!(flagsChanged:),
flags_changed as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(insertTab:),
insert_tab as extern "C" fn(&Object, Sel, id),
do_command_by_selector as extern "C" fn(_, _, _),
);
decl.add_method(sel!(keyDown:), key_down as extern "C" fn(_, _, _));
decl.add_method(sel!(keyUp:), key_up as extern "C" fn(_, _, _));
decl.add_method(sel!(flagsChanged:), flags_changed as extern "C" fn(_, _, _));
decl.add_method(sel!(insertTab:), insert_tab as extern "C" fn(_, _, _));
decl.add_method(
sel!(insertBackTab:),
insert_back_tab as extern "C" fn(&Object, Sel, id),
insert_back_tab as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(mouseDown:),
mouse_down as extern "C" fn(&Object, Sel, id),
);
decl.add_method(sel!(mouseUp:), mouse_up as extern "C" fn(&Object, Sel, id));
decl.add_method(sel!(mouseDown:), mouse_down as extern "C" fn(_, _, _));
decl.add_method(sel!(mouseUp:), mouse_up as extern "C" fn(_, _, _));
decl.add_method(
sel!(rightMouseDown:),
right_mouse_down as extern "C" fn(&Object, Sel, id),
right_mouse_down as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(rightMouseUp:),
right_mouse_up as extern "C" fn(&Object, Sel, id),
right_mouse_up as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(otherMouseDown:),
other_mouse_down as extern "C" fn(&Object, Sel, id),
other_mouse_down as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(otherMouseUp:),
other_mouse_up as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(mouseMoved:),
mouse_moved as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(mouseDragged:),
mouse_dragged as extern "C" fn(&Object, Sel, id),
other_mouse_up as extern "C" fn(_, _, _),
);
decl.add_method(sel!(mouseMoved:), mouse_moved as extern "C" fn(_, _, _));
decl.add_method(sel!(mouseDragged:), mouse_dragged as extern "C" fn(_, _, _));
decl.add_method(
sel!(rightMouseDragged:),
right_mouse_dragged as extern "C" fn(&Object, Sel, id),
right_mouse_dragged as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(otherMouseDragged:),
other_mouse_dragged as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(mouseEntered:),
mouse_entered as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(mouseExited:),
mouse_exited as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(scrollWheel:),
scroll_wheel as extern "C" fn(&Object, Sel, id),
other_mouse_dragged as extern "C" fn(_, _, _),
);
decl.add_method(sel!(mouseEntered:), mouse_entered as extern "C" fn(_, _, _));
decl.add_method(sel!(mouseExited:), mouse_exited as extern "C" fn(_, _, _));
decl.add_method(sel!(scrollWheel:), scroll_wheel as extern "C" fn(_, _, _));
decl.add_method(
sel!(pressureChangeWithEvent:),
pressure_change_with_event as extern "C" fn(&Object, Sel, id),
pressure_change_with_event as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(_wantsKeyDownForEvent:),
wants_key_down_for_event as extern "C" fn(&Object, Sel, id) -> BOOL,
wants_key_down_for_event as extern "C" fn(_, _, _) -> _,
);
decl.add_method(
sel!(cancelOperation:),
cancel_operation as extern "C" fn(&Object, Sel, id),
cancel_operation as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(frameDidChange:),
frame_did_change as extern "C" fn(&Object, Sel, id),
frame_did_change as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(acceptsFirstMouse:),
accepts_first_mouse as extern "C" fn(&Object, Sel, id) -> BOOL,
accepts_first_mouse as extern "C" fn(_, _, _) -> _,
);
decl.add_ivar::<*mut c_void>("taoState");
decl.add_ivar::<id>("markedText");
@@ -308,7 +265,7 @@ lazy_static! {
extern "C" fn dealloc(this: &Object, _sel: Sel) {
unsafe {
let state: *mut c_void = *this.get_ivar("taoState");
let marked_text: id = *this.get_ivar("markedText");
let marked_text: *mut NSMutableAttributedString = *this.get_ivar("markedText");
let _: () = msg_send![marked_text, release];
drop(Box::from_raw(state as *mut ViewState));
}
@@ -318,19 +275,18 @@ extern "C" fn init_with_tao(this: &Object, _sel: Sel, state: *mut c_void) -> id
unsafe {
let this: id = msg_send![this, init];
if this != nil {
(*this).set_ivar("taoState", state);
let marked_text =
<id as NSMutableAttributedString>::init(NSMutableAttributedString::alloc(nil));
(*this).set_ivar("markedText", marked_text);
*(*this).get_mut_ivar("taoState") = state;
let marked_text = Retained::into_raw(NSMutableAttributedString::new());
*(*this).get_mut_ivar("markedText") = marked_text;
let _: () = msg_send![this, setPostsFrameChangedNotifications: YES];
let notification_center: &Object = msg_send![class!(NSNotificationCenter), defaultCenter];
let notification_name = NSString::alloc(nil).init_str("NSViewFrameDidChangeNotification");
let notification_name = NSString::from_str("NSViewFrameDidChangeNotification");
let _: () = msg_send![
notification_center,
addObserver: this
selector: sel!(frameDidChange:)
name: notification_name
name: &*notification_name
object: this
];
}
@@ -387,11 +343,11 @@ extern "C" fn draw_rect(this: &Object, _sel: Sel, rect: NSRect) {
let state = &mut *(state_ptr as *mut ViewState);
if let Some(position) = state.traffic_light_inset {
let window = state.ns_window;
inset_traffic_lights(window, position);
let window = state.ns_window.load().unwrap();
inset_traffic_lights(&window, position);
}
AppState::handle_redraw(WindowId(get_window_id(state.ns_window)));
AppState::handle_redraw(WindowId(get_window_id(&state.ns_window.load().unwrap())));
let superclass = util::superclass(this);
let () = msg_send![super(this, superclass), drawRect: rect];
@@ -405,8 +361,8 @@ extern "C" fn accepts_first_responder(_this: &Object, _sel: Sel) -> BOOL {
// This is necessary to prevent a beefy terminal error on MacBook Pros:
// IMKInputSession [0x7fc573576ff0 presentFunctionRowItemTextInputViewWithEndpoint:completionHandler:] : [self textInputContext]=0x7fc573558e10 *NO* NSRemoteViewController to client, NSError=Error Domain=NSCocoaErrorDomain Code=4099 "The connection from pid 0 was invalidated from this process." UserInfo={NSDebugDescription=The connection from pid 0 was invalidated from this process.}, com.apple.inputmethod.EmojiFunctionRowItem
// TODO: Add an API extension for using `NSTouchBar`
extern "C" fn touch_bar(_this: &Object, _sel: Sel) -> BOOL {
NO
extern "C" fn touch_bar(_this: &Object, _sel: Sel) -> id {
nil
}
extern "C" fn reset_cursor_rects(this: &Object, _sel: Sel) {
@@ -434,16 +390,16 @@ extern "C" fn reset_cursor_rects(this: &Object, _sel: Sel) {
extern "C" fn has_marked_text(this: &Object, _sel: Sel) -> BOOL {
unsafe {
trace!("Triggered `hasMarkedText`");
let marked_text: id = *this.get_ivar("markedText");
let marked_text: &NSMutableAttributedString = *this.get_ivar("markedText");
trace!("Completed `hasMarkedText`");
(marked_text.length() > 0) as BOOL
(marked_text.length() > 0).into()
}
}
extern "C" fn marked_range(this: &Object, _sel: Sel) -> NSRange {
unsafe {
trace!("Triggered `markedRange`");
let marked_text: id = *this.get_ivar("markedText");
let marked_text: &NSMutableAttributedString = *this.get_ivar("markedText");
let length = marked_text.length();
trace!("Completed `markedRange`");
if length > 0 {
@@ -472,13 +428,21 @@ extern "C" fn set_marked_text(
) {
trace!("Triggered `setMarkedText`");
unsafe {
let marked_text_ref = clear_marked_text(this);
let has_attr: BOOL = msg_send![string, isKindOfClass: class!(NSAttributedString)];
if has_attr != NO {
marked_text_ref.initWithAttributedString(string);
let has_attr: bool = msg_send![string, isKindOfClass: class!(NSAttributedString)];
let marked_text = if has_attr {
NSMutableAttributedString::initWithAttributedString(
NSMutableAttributedString::alloc(),
&*(string as *const NSAttributedString),
)
} else {
marked_text_ref.initWithString(string);
NSMutableAttributedString::initWithString(
NSMutableAttributedString::alloc(),
&*(string as *const NSString),
)
};
let marked_text_ref: &mut *mut NSMutableAttributedString = this.get_mut_ivar("markedText");
let () = msg_send![(*marked_text_ref), release];
*marked_text_ref = Retained::into_raw(marked_text);
let state_ptr: *mut c_void = *this.get_ivar("taoState");
let state = &mut *(state_ptr as *mut ViewState);
@@ -491,7 +455,9 @@ extern "C" fn set_marked_text(
extern "C" fn unmark_text(this: &mut Object, _sel: Sel) {
trace!("Triggered `unmarkText`");
unsafe {
clear_marked_text(this);
let marked_text_ref: &mut *mut NSMutableAttributedString = this.get_mut_ivar("markedText");
let () = msg_send![(*marked_text_ref), release];
*marked_text_ref = Retained::into_raw(NSMutableAttributedString::new());
let input_context: id = msg_send![this, inputContext];
let _: () = msg_send![input_context, discardMarkedText];
}
@@ -532,8 +498,10 @@ extern "C" fn first_rect_for_character_range(
let state_ptr: *mut c_void = *this.get_ivar("taoState");
let state = &mut *(state_ptr as *mut ViewState);
let (x, y) = state.ime_spot.unwrap_or_else(|| {
let content_rect =
NSWindow::contentRectForFrameRect_(state.ns_window, NSWindow::frame(state.ns_window));
let content_rect = NSWindow::contentRectForFrameRect(
&state.ns_window.load().unwrap(),
NSWindow::frame(&state.ns_window.load().unwrap()),
);
let x = content_rect.origin.x;
let y = util::bottom_left_to_top_left(content_rect);
(x, y)
@@ -543,14 +511,19 @@ extern "C" fn first_rect_for_character_range(
}
}
extern "C" fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_range: NSRange) {
extern "C" fn insert_text(
this: &Object,
_sel: Sel,
string: &NSString,
_replacement_range: NSRange,
) {
trace!("Triggered `insertText`");
unsafe {
let state_ptr: *mut c_void = *this.get_ivar("taoState");
let state = &mut *(state_ptr as *mut ViewState);
let has_attr: BOOL = msg_send![string, isKindOfClass: class!(NSAttributedString)];
let characters = if has_attr != NO {
let has_attr: bool = msg_send![string, isKindOfClass: class!(NSAttributedString)];
let characters = if has_attr {
// This is a *mut NSAttributedString
msg_send![string, string]
} else {
@@ -558,8 +531,8 @@ extern "C" fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_ran
string
};
let slice = slice::from_raw_parts(characters.UTF8String() as *const c_uchar, characters.len());
let string: String = str::from_utf8_unchecked(slice)
let string: String = characters
.to_string()
.chars()
.filter(|c| !is_corporate_character(*c))
.collect();
@@ -569,7 +542,7 @@ extern "C" fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_ran
//let event: id = msg_send![NSApp(), currentEvent];
AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: WindowId(get_window_id(state.ns_window)),
window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())),
event: WindowEvent::ReceivedImeText(string),
}));
if state.in_ime_preedit {
@@ -598,7 +571,7 @@ extern "C" fn do_command_by_selector(_this: &Object, _sel: Sel, _command: Sel) {
// // 1) as a reminder for how `doCommandBySelector` works
// // 2) to make our use of carriage return explicit
// events.push_back(EventWrapper::StaticEvent(Event::WindowEvent {
// window_id: WindowId(get_window_id(state.ns_window)),
// window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())),
// event: WindowEvent::ReceivedCharacter('\r'),
// }));
// } else {
@@ -609,7 +582,7 @@ extern "C" fn do_command_by_selector(_this: &Object, _sel: Sel, _command: Sel) {
// .filter(|c| !is_corporate_character(*c))
// {
// events.push_back(EventWrapper::StaticEvent(Event::WindowEvent {
// window_id: WindowId(get_window_id(state.ns_window)),
// window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())),
// event: WindowEvent::ReceivedCharacter(character),
// }));
// }
@@ -679,45 +652,46 @@ fn is_corporate_character(c: char) -> bool {
// }
// Update `state.modifiers` if `event` has something different
fn update_potentially_stale_modifiers(state: &mut ViewState, event: id) {
fn update_potentially_stale_modifiers(state: &mut ViewState, event: &NSEvent) {
let event_modifiers = event_mods(event);
if state.modifiers != event_modifiers {
state.modifiers = event_modifiers;
AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: WindowId(get_window_id(state.ns_window)),
window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())),
event: WindowEvent::ModifiersChanged(state.modifiers),
}));
}
}
extern "C" fn key_down(this: &mut Object, _sel: Sel, event: id) {
extern "C" fn key_down(this: &mut Object, _sel: Sel, event: &NSEvent) {
trace!("Triggered `keyDown`");
unsafe {
let state_ptr: *mut c_void = *this.get_ivar("taoState");
let state = &mut *(state_ptr as *mut ViewState);
let window_id = WindowId(get_window_id(state.ns_window));
let window_id = WindowId(get_window_id(&state.ns_window.load().unwrap()));
// keyboard refactor: don't seems to be needed anymore
// let characters = get_characters(event, false);
//state.raw_characters = Some(characters);
let is_repeat: BOOL = msg_send![event, isARepeat];
let is_repeat = is_repeat == YES;
let is_repeat: bool = msg_send![event, isARepeat];
update_potentially_stale_modifiers(state, event);
let pass_along = !is_repeat || !state.is_key_down;
if pass_along {
// See below for why we do this.
clear_marked_text(this);
let marked_text_ref: &mut *mut NSMutableAttributedString = this.get_mut_ivar("markedText");
let () = msg_send![(*marked_text_ref), release];
*marked_text_ref = Retained::into_raw(NSMutableAttributedString::new());
state.key_triggered_ime = false;
// Some keys (and only *some*, with no known reason) don't trigger `insertText`, while others do...
// So, we don't give repeats the opportunity to trigger that, since otherwise our hack will cause some
// keys to generate twice as many characters.
let array: id = msg_send![class!(NSArray), arrayWithObject: event];
let () = msg_send![this, interpretKeyEvents: array];
let () = msg_send![&*this, interpretKeyEvents: array];
}
// The `interpretKeyEvents` above, may invoke `set_marked_text` or `insert_text`,
// if the event corresponds to an IME event.
@@ -751,7 +725,7 @@ extern "C" fn key_down(this: &mut Object, _sel: Sel, event: id) {
trace!("Completed `keyDown`");
}
extern "C" fn key_up(this: &Object, _sel: Sel, event: id) {
extern "C" fn key_up(this: &Object, _sel: Sel, event: &NSEvent) {
trace!("Triggered `keyUp`");
unsafe {
let state_ptr: *mut c_void = *this.get_ivar("taoState");
@@ -762,7 +736,7 @@ extern "C" fn key_up(this: &Object, _sel: Sel, event: id) {
update_potentially_stale_modifiers(state, event);
let window_event = Event::WindowEvent {
window_id: WindowId(get_window_id(state.ns_window)),
window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())),
event: WindowEvent::KeyboardInput {
device_id: DEVICE_ID,
event: create_key_event(event, false, false, false, None),
@@ -774,7 +748,7 @@ extern "C" fn key_up(this: &Object, _sel: Sel, event: id) {
trace!("Completed `keyUp`");
}
extern "C" fn flags_changed(this: &Object, _sel: Sel, ns_event: id) {
extern "C" fn flags_changed(this: &Object, _sel: Sel, ns_event: &NSEvent) {
use KeyCode::{
AltLeft, AltRight, ControlLeft, ControlRight, ShiftLeft, ShiftRight, SuperLeft, SuperRight,
};
@@ -844,46 +818,46 @@ extern "C" fn flags_changed(this: &Object, _sel: Sel, ns_event: id) {
}
process_event!(
ModifiersState::SHIFT,
NSEventModifierFlags::NSShiftKeyMask,
NSEventModifierFlags::NSEventModifierFlagShift,
ShiftLeft
);
process_event!(
ModifiersState::SHIFT,
NSEventModifierFlags::NSShiftKeyMask,
NSEventModifierFlags::NSEventModifierFlagShift,
ShiftRight
);
process_event!(
ModifiersState::CONTROL,
NSEventModifierFlags::NSControlKeyMask,
NSEventModifierFlags::NSEventModifierFlagControl,
ControlLeft
);
process_event!(
ModifiersState::CONTROL,
NSEventModifierFlags::NSControlKeyMask,
NSEventModifierFlags::NSEventModifierFlagControl,
ControlRight
);
process_event!(
ModifiersState::ALT,
NSEventModifierFlags::NSAlternateKeyMask,
NSEventModifierFlags::NSEventModifierFlagOption,
AltLeft
);
process_event!(
ModifiersState::ALT,
NSEventModifierFlags::NSAlternateKeyMask,
NSEventModifierFlags::NSEventModifierFlagOption,
AltRight
);
process_event!(
ModifiersState::SUPER,
NSEventModifierFlags::NSCommandKeyMask,
NSEventModifierFlags::NSEventModifierFlagCommand,
SuperLeft
);
process_event!(
ModifiersState::SUPER,
NSEventModifierFlags::NSCommandKeyMask,
NSEventModifierFlags::NSEventModifierFlagCommand,
SuperRight
);
let window_id = WindowId(get_window_id(state.ns_window));
let window_id = WindowId(get_window_id(&state.ns_window.load().unwrap()));
for event in events {
AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent {
@@ -927,21 +901,22 @@ extern "C" fn insert_back_tab(this: &Object, _sel: Sel, _sender: id) {
extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
trace!("Triggered `cancelOperation`");
unsafe {
let mtm = MainThreadMarker::new_unchecked();
let state_ptr: *mut c_void = *this.get_ivar("taoState");
let state = &mut *(state_ptr as *mut ViewState);
let event: id = msg_send![NSApp(), currentEvent];
update_potentially_stale_modifiers(state, event);
let event: Retained<NSEvent> = msg_send_id![&NSApp(mtm), currentEvent];
update_potentially_stale_modifiers(state, &event);
let scancode = 0x2f;
let key = KeyCode::from_scancode(scancode);
debug_assert_eq!(key, KeyCode::Period);
let window_event = Event::WindowEvent {
window_id: WindowId(get_window_id(state.ns_window)),
window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())),
event: WindowEvent::KeyboardInput {
device_id: DEVICE_ID,
event: create_key_event(event, true, false, false, Some(key)),
event: create_key_event(&event, true, false, false, Some(key)),
is_synthetic: false,
},
};
@@ -950,7 +925,7 @@ extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
trace!("Completed `cancelOperation`");
}
fn mouse_click(this: &Object, event: id, button: MouseButton, button_state: ElementState) {
fn mouse_click(this: &Object, event: &NSEvent, button: MouseButton, button_state: ElementState) {
unsafe {
let state_ptr: *mut c_void = *this.get_ivar("taoState");
let state = &mut *(state_ptr as *mut ViewState);
@@ -958,7 +933,7 @@ fn mouse_click(this: &Object, event: id, button: MouseButton, button_state: Elem
update_potentially_stale_modifiers(state, event);
let window_event = Event::WindowEvent {
window_id: WindowId(get_window_id(state.ns_window)),
window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())),
event: WindowEvent::MouseInput {
device_id: DEVICE_ID,
state: button_state,
@@ -971,47 +946,44 @@ fn mouse_click(this: &Object, event: id, button: MouseButton, button_state: Elem
}
}
extern "C" fn mouse_down(this: &Object, _sel: Sel, event: id) {
extern "C" fn mouse_down(this: &NSView, _sel: Sel, event: &NSEvent) {
mouse_motion(this, event);
mouse_click(this, event, MouseButton::Left, ElementState::Pressed);
}
extern "C" fn mouse_up(this: &Object, _sel: Sel, event: id) {
extern "C" fn mouse_up(this: &NSView, _sel: Sel, event: &NSEvent) {
mouse_motion(this, event);
mouse_click(this, event, MouseButton::Left, ElementState::Released);
}
extern "C" fn right_mouse_down(this: &Object, _sel: Sel, event: id) {
extern "C" fn right_mouse_down(this: &NSView, _sel: Sel, event: &NSEvent) {
mouse_motion(this, event);
mouse_click(this, event, MouseButton::Right, ElementState::Pressed);
}
extern "C" fn right_mouse_up(this: &Object, _sel: Sel, event: id) {
extern "C" fn right_mouse_up(this: &NSView, _sel: Sel, event: &NSEvent) {
mouse_motion(this, event);
mouse_click(this, event, MouseButton::Right, ElementState::Released);
}
extern "C" fn other_mouse_down(this: &Object, _sel: Sel, event: id) {
extern "C" fn other_mouse_down(this: &NSView, _sel: Sel, event: &NSEvent) {
mouse_motion(this, event);
mouse_click(this, event, MouseButton::Middle, ElementState::Pressed);
}
extern "C" fn other_mouse_up(this: &Object, _sel: Sel, event: id) {
extern "C" fn other_mouse_up(this: &NSView, _sel: Sel, event: &NSEvent) {
mouse_motion(this, event);
mouse_click(this, event, MouseButton::Middle, ElementState::Released);
}
fn mouse_motion(this: &Object, event: id) {
fn mouse_motion(this: &NSView, event: &NSEvent) {
unsafe {
let state_ptr: *mut c_void = *this.get_ivar("taoState");
let state = &mut *(state_ptr as *mut ViewState);
// We have to do this to have access to the `NSView` trait...
let view: id = this as *const _ as *mut _;
let window_point = event.locationInWindow();
let view_point = view.convertPoint_fromView_(window_point, nil);
let view_rect = NSView::frame(view);
let view_point = this.convertPoint_fromView(window_point, None);
let view_rect = NSView::frame(this);
if view_point.x.is_sign_negative()
|| view_point.y.is_sign_negative()
@@ -1032,7 +1004,7 @@ fn mouse_motion(this: &Object, event: id) {
update_potentially_stale_modifiers(state, event);
let window_event = Event::WindowEvent {
window_id: WindowId(get_window_id(state.ns_window)),
window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())),
event: WindowEvent::CursorMoved {
device_id: DEVICE_ID,
position: logical_position.to_physical(state.get_scale_factor()),
@@ -1044,19 +1016,19 @@ fn mouse_motion(this: &Object, event: id) {
}
}
extern "C" fn mouse_moved(this: &Object, _sel: Sel, event: id) {
extern "C" fn mouse_moved(this: &NSView, _sel: Sel, event: &NSEvent) {
mouse_motion(this, event);
}
extern "C" fn mouse_dragged(this: &Object, _sel: Sel, event: id) {
extern "C" fn mouse_dragged(this: &NSView, _sel: Sel, event: &NSEvent) {
mouse_motion(this, event);
}
extern "C" fn right_mouse_dragged(this: &Object, _sel: Sel, event: id) {
extern "C" fn right_mouse_dragged(this: &NSView, _sel: Sel, event: &NSEvent) {
mouse_motion(this, event);
}
extern "C" fn other_mouse_dragged(this: &Object, _sel: Sel, event: id) {
extern "C" fn other_mouse_dragged(this: &NSView, _sel: Sel, event: &NSEvent) {
mouse_motion(this, event);
}
@@ -1067,7 +1039,7 @@ extern "C" fn mouse_entered(this: &Object, _sel: Sel, _event: id) {
let state = &mut *(state_ptr as *mut ViewState);
let enter_event = Event::WindowEvent {
window_id: WindowId(get_window_id(state.ns_window)),
window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())),
event: WindowEvent::CursorEntered {
device_id: DEVICE_ID,
},
@@ -1085,7 +1057,7 @@ extern "C" fn mouse_exited(this: &Object, _sel: Sel, _event: id) {
let state = &mut *(state_ptr as *mut ViewState);
let window_event = Event::WindowEvent {
window_id: WindowId(get_window_id(state.ns_window)),
window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())),
event: WindowEvent::CursorLeft {
device_id: DEVICE_ID,
},
@@ -1096,7 +1068,7 @@ extern "C" fn mouse_exited(this: &Object, _sel: Sel, _event: id) {
trace!("Completed `mouseExited`");
}
extern "C" fn scroll_wheel(this: &Object, _sel: Sel, event: id) {
extern "C" fn scroll_wheel(this: &NSView, _sel: Sel, event: &NSEvent) {
trace!("Triggered `scrollWheel`");
mouse_motion(this, event);
@@ -1108,7 +1080,7 @@ extern "C" fn scroll_wheel(this: &Object, _sel: Sel, event: id) {
let delta = {
// macOS horizontal sign convention is the inverse of tao.
let (x, y) = (event.scrollingDeltaX() * -1.0, event.scrollingDeltaY());
if event.hasPreciseScrollingDeltas() == YES {
if event.hasPreciseScrollingDeltas() {
let delta = LogicalPosition::new(x, y).to_physical(state.get_scale_factor());
MouseScrollDelta::PixelDelta(delta)
} else {
@@ -1116,8 +1088,8 @@ extern "C" fn scroll_wheel(this: &Object, _sel: Sel, event: id) {
}
};
let phase = match event.phase() {
NSEventPhase::NSEventPhaseMayBegin | NSEventPhase::NSEventPhaseBegan => TouchPhase::Started,
NSEventPhase::NSEventPhaseEnded => TouchPhase::Ended,
NSEventPhase::MayBegin | NSEventPhase::Began => TouchPhase::Started,
NSEventPhase::Ended => TouchPhase::Ended,
_ => TouchPhase::Moved,
};
@@ -1132,7 +1104,7 @@ extern "C" fn scroll_wheel(this: &Object, _sel: Sel, event: id) {
update_potentially_stale_modifiers(state, event);
let window_event = Event::WindowEvent {
window_id: WindowId(get_window_id(state.ns_window)),
window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())),
event: WindowEvent::MouseWheel {
device_id: DEVICE_ID,
delta,
@@ -1147,7 +1119,7 @@ extern "C" fn scroll_wheel(this: &Object, _sel: Sel, event: id) {
trace!("Completed `scrollWheel`");
}
extern "C" fn pressure_change_with_event(this: &Object, _sel: Sel, event: id) {
extern "C" fn pressure_change_with_event(this: &NSView, _sel: Sel, event: &NSEvent) {
trace!("Triggered `pressureChangeWithEvent`");
mouse_motion(this, event);
@@ -1160,11 +1132,11 @@ extern "C" fn pressure_change_with_event(this: &Object, _sel: Sel, event: id) {
let stage = event.stage();
let window_event = Event::WindowEvent {
window_id: WindowId(get_window_id(state.ns_window)),
window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())),
event: WindowEvent::TouchpadPressure {
device_id: DEVICE_ID,
pressure,
stage,
stage: stage as i64,
},
};
@@ -1184,27 +1156,33 @@ extern "C" fn accepts_first_mouse(_this: &Object, _sel: Sel, _event: id) -> BOOL
YES
}
pub unsafe fn inset_traffic_lights<W: NSWindow + Copy>(window: W, position: LogicalPosition<f64>) {
pub unsafe fn inset_traffic_lights(window: &NSWindow, position: LogicalPosition<f64>) {
let (x, y) = (position.x, position.y);
let close = window.standardWindowButton_(NSWindowButton::NSWindowCloseButton);
let miniaturize = window.standardWindowButton_(NSWindowButton::NSWindowMiniaturizeButton);
let zoom = window.standardWindowButton_(NSWindowButton::NSWindowZoomButton);
let close = window
.standardWindowButton(NSWindowButton::NSWindowCloseButton)
.unwrap();
let miniaturize = window
.standardWindowButton(NSWindowButton::NSWindowMiniaturizeButton)
.unwrap();
let zoom = window
.standardWindowButton(NSWindowButton::NSWindowZoomButton)
.unwrap();
let title_bar_container_view = close.superview().superview();
let title_bar_container_view = close.superview().unwrap().superview().unwrap();
let close_rect = NSView::frame(close);
let close_rect = NSView::frame(&close);
let title_bar_frame_height = close_rect.size.height + y;
let mut title_bar_rect = NSView::frame(title_bar_container_view);
let mut title_bar_rect = NSView::frame(&title_bar_container_view);
title_bar_rect.size.height = title_bar_frame_height;
title_bar_rect.origin.y = window.frame().size.height - title_bar_frame_height;
let _: () = msg_send![title_bar_container_view, setFrame: title_bar_rect];
let _: () = msg_send![&title_bar_container_view, setFrame: title_bar_rect];
let window_buttons = vec![close, miniaturize, zoom];
let space_between = NSView::frame(miniaturize).origin.x - close_rect.origin.x;
let window_buttons = vec![close, miniaturize.clone(), zoom];
let space_between = NSView::frame(&miniaturize).origin.x - close_rect.origin.x;
for (i, button) in window_buttons.into_iter().enumerate() {
let mut rect = NSView::frame(button);
let mut rect = NSView::frame(&button);
rect.origin.x = x + (i as f64 * space_between);
button.setFrameOrigin(rect.origin);
}

File diff suppressed because it is too large Load Diff

View File

@@ -4,19 +4,20 @@
use std::{
f64,
ffi::CStr,
os::raw::c_void,
sync::{Arc, Weak},
};
use cocoa::{
appkit::{self, NSApplicationPresentationOptions, NSView, NSWindow},
base::{id, nil},
foundation::{NSAutoreleasePool, NSString, NSUInteger},
use objc2::{
msg_send_id,
rc::Retained,
runtime::{AnyClass as Class, AnyObject as Object, ClassBuilder as ClassDecl, Sel},
};
use objc::{
declare::ClassDecl,
runtime::{Class, Object, Sel, BOOL, NO, YES},
use objc2_app_kit::{
self as appkit, NSApplicationPresentationOptions, NSPasteboard, NSView, NSWindow,
};
use objc2_foundation::{NSArray, NSAutoreleasePool, NSString, NSUInteger};
use crate::{
dpi::{LogicalPosition, LogicalSize},
@@ -25,6 +26,7 @@ use crate::{
platform_impl::platform::{
app_state::AppState,
event::{EventProxy, EventWrapper},
ffi::{id, nil, BOOL, NO, YES},
util::{self, IdRef},
view::ViewState,
window::{get_ns_theme, get_window_id, UnownedWindow},
@@ -33,10 +35,10 @@ use crate::{
};
pub struct WindowDelegateState {
ns_window: IdRef, // never changes
ns_window: Retained<NSWindow>, // never changes
// We keep this ns_view because we still need its view state for some extern function
// like didResignKey
ns_view: IdRef, // never changes
ns_view: Retained<NSView>, // never changes
window: Weak<UnownedWindow>,
@@ -77,8 +79,8 @@ impl WindowDelegateState {
delegate_state
}
fn ns_view(&self) -> id {
unsafe { (*self.ns_window).contentView() }
fn ns_view(&self) -> Retained<NSView> {
self.ns_window.contentView().unwrap()
}
fn with_window<F, T>(&mut self, callback: F) -> Option<T>
@@ -90,7 +92,7 @@ impl WindowDelegateState {
pub fn emit_event(&mut self, event: WindowEvent<'static>) {
let event = Event::WindowEvent {
window_id: WindowId(get_window_id(*self.ns_window)),
window_id: WindowId(get_window_id(&self.ns_window)),
event,
};
AppState::queue_event(EventWrapper::StaticEvent(event));
@@ -104,7 +106,7 @@ impl WindowDelegateState {
self.previous_scale_factor = scale_factor;
let wrapper = EventWrapper::EventProxy(EventProxy::DpiChangedProxy {
ns_window: IdRef::retain(*self.ns_window),
ns_window: self.ns_window.clone(),
suggested_size: self.view_size(),
scale_factor,
});
@@ -112,7 +114,7 @@ impl WindowDelegateState {
}
pub fn emit_resize_event(&mut self) {
let rect = unsafe { NSView::frame(self.ns_view()) };
let rect = NSView::frame(&self.ns_view());
let scale_factor = self.get_scale_factor();
let logical_size = LogicalSize::new(rect.size.width as f64, rect.size.height as f64);
let size = logical_size.to_physical(scale_factor);
@@ -120,7 +122,7 @@ impl WindowDelegateState {
}
fn emit_move_event(&mut self) {
let rect = unsafe { NSWindow::frame(*self.ns_window) };
let rect = NSWindow::frame(&self.ns_window);
let x = rect.origin.x as f64;
let y = util::bottom_left_to_top_left(rect);
let moved = self.previous_position != Some((x, y));
@@ -133,11 +135,11 @@ impl WindowDelegateState {
}
fn get_scale_factor(&self) -> f64 {
(unsafe { NSWindow::backingScaleFactor(*self.ns_window) }) as f64
NSWindow::backingScaleFactor(&self.ns_window) as f64
}
fn view_size(&self) -> LogicalSize<f64> {
let ns_size = unsafe { NSView::frame(self.ns_view()).size };
let ns_size = NSView::frame(&self.ns_view()).size;
LogicalSize::new(ns_size.width as f64, ns_size.height as f64)
}
}
@@ -161,102 +163,101 @@ lazy_static! {
let superclass = class!(NSResponder);
let mut decl = ClassDecl::new("TaoWindowDelegate", superclass).unwrap();
decl.add_method(sel!(dealloc), dealloc as extern "C" fn(&Object, Sel));
decl.add_method(sel!(dealloc), dealloc as extern "C" fn(_, _));
decl.add_method(
sel!(initWithTao:),
init_with_tao as extern "C" fn(&Object, Sel, *mut c_void) -> id,
init_with_tao as extern "C" fn(_, _, _) -> _,
);
decl.add_method(
sel!(markIsCheckingZoomedIn),
mark_is_checking_zoomed_in as extern "C" fn(&Object, Sel),
mark_is_checking_zoomed_in as extern "C" fn(_, _),
);
decl.add_method(
sel!(clearIsCheckingZoomedIn),
clear_is_checking_zoomed_in as extern "C" fn(&Object, Sel),
clear_is_checking_zoomed_in as extern "C" fn(_, _),
);
decl.add_method(
sel!(windowShouldClose:),
window_should_close as extern "C" fn(&Object, Sel, id) -> BOOL,
window_should_close as extern "C" fn(_, _, _) -> _,
);
decl.add_method(
sel!(windowWillClose:),
window_will_close as extern "C" fn(&Object, Sel, id),
window_will_close as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(windowDidResize:),
window_did_resize as extern "C" fn(&Object, Sel, id),
window_did_resize as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(windowDidMove:),
window_did_move as extern "C" fn(&Object, Sel, id),
window_did_move as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(windowDidChangeBackingProperties:),
window_did_change_backing_properties as extern "C" fn(&Object, Sel, id),
window_did_change_backing_properties as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(windowDidBecomeKey:),
window_did_become_key as extern "C" fn(&Object, Sel, id),
window_did_become_key as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(windowDidResignKey:),
window_did_resign_key as extern "C" fn(&Object, Sel, id),
window_did_resign_key as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(draggingEntered:),
dragging_entered as extern "C" fn(&Object, Sel, id) -> BOOL,
dragging_entered as extern "C" fn(_, _, _) -> _,
);
decl.add_method(
sel!(prepareForDragOperation:),
prepare_for_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL,
prepare_for_drag_operation as extern "C" fn(_, _, _) -> _,
);
decl.add_method(
sel!(performDragOperation:),
perform_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL,
perform_drag_operation as extern "C" fn(_, _, _) -> _,
);
decl.add_method(
sel!(concludeDragOperation:),
conclude_drag_operation as extern "C" fn(&Object, Sel, id),
conclude_drag_operation as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(draggingExited:),
dragging_exited as extern "C" fn(&Object, Sel, id),
dragging_exited as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(window:willUseFullScreenPresentationOptions:),
window_will_use_fullscreen_presentation_options
as extern "C" fn(&Object, Sel, id, NSUInteger) -> NSUInteger,
window_will_use_fullscreen_presentation_options as extern "C" fn(_, _, _, _) -> _,
);
decl.add_method(
sel!(windowDidEnterFullScreen:),
window_did_enter_fullscreen as extern "C" fn(&Object, Sel, id),
window_did_enter_fullscreen as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(windowWillEnterFullScreen:),
window_will_enter_fullscreen as extern "C" fn(&Object, Sel, id),
window_will_enter_fullscreen as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(windowDidExitFullScreen:),
window_did_exit_fullscreen as extern "C" fn(&Object, Sel, id),
window_did_exit_fullscreen as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(windowWillExitFullScreen:),
window_will_exit_fullscreen as extern "C" fn(&Object, Sel, id),
window_will_exit_fullscreen as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(windowDidFailToEnterFullScreen:),
window_did_fail_to_enter_fullscreen as extern "C" fn(&Object, Sel, id),
window_did_fail_to_enter_fullscreen as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(effectiveAppearanceDidChange:),
effective_appearance_did_change as extern "C" fn(&Object, Sel, id),
effective_appearance_did_change as extern "C" fn(_, _, _),
);
decl.add_method(
sel!(effectiveAppearanceDidChangedOnMainThread:),
effective_appearance_did_changed_on_main_thread as extern "C" fn(&Object, Sel, id),
effective_appearance_did_changed_on_main_thread as extern "C" fn(_, _, _),
);
decl.add_ivar::<*mut c_void>("taoState");
@@ -267,6 +268,7 @@ lazy_static! {
// This function is definitely unsafe, but labeling that would increase
// boilerplate and wouldn't really clarify anything...
fn with_state<F: FnOnce(&mut WindowDelegateState) -> T, T>(this: &Object, callback: F) {
#[allow(deprecated)] // TODO: Use define_class!
let state_ptr = unsafe {
let state_ptr: *mut c_void = *this.get_ivar("taoState");
&mut *(state_ptr as *mut WindowDelegateState)
@@ -281,23 +283,24 @@ extern "C" fn dealloc(this: &Object, _sel: Sel) {
}
extern "C" fn init_with_tao(this: &Object, _sel: Sel, state: *mut c_void) -> id {
#[allow(deprecated)] // TODO: Use define_class!
unsafe {
let this: id = msg_send![this, init];
if this != nil {
(*this).set_ivar("taoState", state);
*(*this).get_mut_ivar("taoState") = state;
with_state(&*this, |state| {
let () = msg_send![*state.ns_window, setDelegate: this];
let () = msg_send![&state.ns_window, setDelegate: this];
});
}
let notification_center: &Object =
msg_send![class!(NSDistributedNotificationCenter), defaultCenter];
let notification_name = NSString::alloc(nil).init_str("AppleInterfaceThemeChangedNotification");
let notification_name = NSString::from_str("AppleInterfaceThemeChangedNotification");
let _: () = msg_send![
notification_center,
addObserver: this
selector: sel!(effectiveAppearanceDidChange:)
name: notification_name
name: &*notification_name
object: nil
];
@@ -328,11 +331,10 @@ extern "C" fn window_will_close(this: &Object, _: Sel, _: id) {
trace!("Triggered `windowWillClose:`");
with_state(this, |state| unsafe {
// `setDelegate:` retains the previous value and then autoreleases it
let pool = NSAutoreleasePool::new(nil);
let _pool = NSAutoreleasePool::new();
// Since El Capitan, we need to be careful that delegate methods can't
// be called after the window closes.
let () = msg_send![*state.ns_window, setDelegate: nil];
pool.drain();
let () = msg_send![&state.ns_window, setDelegate: nil];
state.emit_event(WindowEvent::Destroyed);
});
trace!("Completed `windowWillClose:`");
@@ -390,8 +392,9 @@ extern "C" fn window_did_resign_key(this: &Object, _: Sel, _: id) {
// Here we (very unsafely) acquire the taoState (a ViewState) from the
// Object referenced by state.ns_view (an IdRef, which is dereferenced
// to an id)
#[allow(deprecated)] // TODO: Use define_class!
let view_state: &mut ViewState = unsafe {
let ns_view: &Object = (*state.ns_view).as_ref().expect("failed to deref");
let ns_view: &Object = &state.ns_view;
let state_ptr: *mut c_void = *ns_view.get_ivar("taoState");
&mut *(state_ptr as *mut ViewState)
};
@@ -411,17 +414,17 @@ extern "C" fn window_did_resign_key(this: &Object, _: Sel, _: id) {
extern "C" fn dragging_entered(this: &Object, _: Sel, sender: id) -> BOOL {
trace!("Triggered `draggingEntered:`");
use cocoa::{appkit::NSPasteboard, foundation::NSFastEnumeration};
use std::path::PathBuf;
let pb: id = unsafe { msg_send![sender, draggingPasteboard] };
let filenames = unsafe { NSPasteboard::propertyListForType(pb, appkit::NSFilenamesPboardType) };
let pb: Retained<NSPasteboard> = unsafe { msg_send_id![sender, draggingPasteboard] };
let filenames =
unsafe { NSPasteboard::propertyListForType(&pb, appkit::NSFilenamesPboardType) }.unwrap();
for file in unsafe { filenames.iter() } {
use std::ffi::CStr;
for file in unsafe { Retained::cast::<NSArray>(filenames) } {
let file = unsafe { Retained::cast::<NSString>(file) };
unsafe {
let f = NSString::UTF8String(file);
let f = NSString::UTF8String(&file);
let path = CStr::from_ptr(f).to_string_lossy().into_owned();
with_state(this, |state| {
@@ -445,17 +448,17 @@ extern "C" fn prepare_for_drag_operation(_: &Object, _: Sel, _: id) -> BOOL {
extern "C" fn perform_drag_operation(this: &Object, _: Sel, sender: id) -> BOOL {
trace!("Triggered `performDragOperation:`");
use cocoa::{appkit::NSPasteboard, foundation::NSFastEnumeration};
use std::path::PathBuf;
let pb: id = unsafe { msg_send![sender, draggingPasteboard] };
let filenames = unsafe { NSPasteboard::propertyListForType(pb, appkit::NSFilenamesPboardType) };
let pb: Retained<NSPasteboard> = unsafe { msg_send_id![sender, draggingPasteboard] };
let filenames =
unsafe { NSPasteboard::propertyListForType(&pb, appkit::NSFilenamesPboardType) }.unwrap();
for file in unsafe { filenames.iter() } {
use std::ffi::CStr;
for file in unsafe { Retained::cast::<NSArray>(filenames) } {
let file = unsafe { Retained::cast::<NSString>(file) };
unsafe {
let f = NSString::UTF8String(file);
let f = NSString::UTF8String(&file);
let path = CStr::from_ptr(f).to_string_lossy().into_owned();
with_state(this, |state| {
@@ -634,7 +637,8 @@ extern "C" fn window_did_fail_to_enter_fullscreen(this: &Object, _: Sel, _: id)
});
if state.initial_fullscreen {
let _: () = unsafe {
msg_send![*state.ns_window,
msg_send![
&state.ns_window,
performSelector:sel!(toggleFullScreen:)
withObject:nil
afterDelay: 0.5