Skip to content

Commit

Permalink
Initial conversion to objc2 (#30)
Browse files Browse the repository at this point in the history
* Use objc2

* Replace `objc_id`

* Remove sel_impl import

* Fix `add_method` calls

* Fix accessing raw FFI functions

* Fix Encode impl

* Fix message sends arguments that do not implement `Encode`

* Use immutable reference in a few places where now necessary

See madsmtm/objc2#150 for a bit of background

* Add a few Send + Sync bounds where examples require it

This is something we'll need to look into properly

* Use `&'static Class` instead of `*const Class`

Safer and more ergonomic. Also required for `msg_send_id!` macro

* Use msg_send_id! and rc::Id

* Update objc2 to v0.3.0-beta.2

* Replace `BOOL` with `Bool` when declaring delegates

This makes cacao compile on Aarch64 again

* Remove a few impossible to use correctly `into_inner` functions

These consumed `self`, and hence also dropped `Id` variable that was responsible for keeping the returned pointer alive

* Remove a few impossible to use correctly `From` implementations

* Quickly fix UB with using BACKGROUND_COLOR ivar

* Fix double-freeing of windows

* Fix double freeing of strings

* Fix a few remaining double-frees
  • Loading branch information
madsmtm authored Sep 11, 2023
1 parent 4f40d62 commit 094ed59
Show file tree
Hide file tree
Showing 114 changed files with 1,248 additions and 1,455 deletions.
31 changes: 15 additions & 16 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,9 +209,7 @@ impl View {
center_x: LayoutAnchorX::center(view),
center_y: LayoutAnchorY::center(view),

layer: Layer::wrap(unsafe {
msg_send![view, layer]
}),
layer: Layer::from_id(unsafe { msg_send_id![view, layer] }),

objc: ObjcProperty::retain(view),
}
Expand Down Expand Up @@ -293,7 +291,8 @@ impl<T> View<T> {

#[cfg(target_os = "macos")]
self.objc.with_mut(|obj| unsafe {
(&mut *obj).set_ivar(BACKGROUND_COLOR, color);
// TODO: Fix this unnecessary retain!
(&mut *obj).set_ivar::<id>(BACKGROUND_COLOR, msg_send![color, retain]);
});

#[cfg(target_os = "ios")]
Expand Down Expand Up @@ -352,22 +351,22 @@ We'll step through an example (abridged) `View` bridge below, for macOS. You sho
For our basic `View` type, we want to just map to the corresponding class on the Objective-C side (in this case, `NSView`), and maybe do a bit of tweaking for sanity reasons.

``` rust
pub(crate) fn register_view_class() -> *const Class {
static mut VIEW_CLASS: *const Class = 0 as *const Class;
pub(crate) fn register_view_class() -> &'static Class {
static mut VIEW_CLASS: Option<'static Class> = None;
static INIT: Once = Once::new();

INIT.call_once(|| unsafe {
let superclass = class!(NSView);
let mut decl = ClassDecl::new("RSTView", superclass).unwrap();

decl.add_method(sel!(isFlipped), enforce_normalcy as extern "C" fn(&Object, _) -> BOOL);
decl.add_method(sel!(isFlipped), enforce_normalcy as extern "C" fn(_, _) -> _);

decl.add_ivar::<id>(BACKGROUND_COLOR);

VIEW_CLASS = decl.register();
VIEW_CLASS = Some(decl.register());
});

unsafe { VIEW_CLASS }
unsafe { VIEW_CLASS.unwrap() }
}
```

Expand All @@ -377,19 +376,19 @@ Objective-C method signatures, as well as provision space for variable storage (
For our _delegate_ types, we need a different class creation method - one that creates a subclass per-unique-type:

``` rust
pub(crate) fn register_view_class_with_delegate<T: ViewDelegate>(instance: &T) -> *const Class {
pub(crate) fn register_view_class_with_delegate<T: ViewDelegate>(instance: &T) -> &'static Class {
load_or_register_class("NSView", instance.subclass_name(), |decl| unsafe {
decl.add_ivar::<usize>(VIEW_DELEGATE_PTR);
decl.add_ivar::<id>(BACKGROUND_COLOR);

decl.add_method(
sel!(isFlipped),
enforce_normalcy as extern "C" fn(&Object, _) -> BOOL
enforce_normalcy as extern "C" fn(_, _) -> _,
);

decl.add_method(
sel!(draggingEntered:),
dragging_entered::<T> as extern "C" fn (&mut Object, _, _) -> NSUInteger
dragging_entered::<T> as extern "C" fn (_, _, _) -> _,
);
})
}
Expand All @@ -401,18 +400,18 @@ to the Rust `ViewDelegate` implementation.
The methods we're setting up can range from simple to complex - take `isFlipped`:

``` rust
extern "C" fn is_flipped(_: &Object, _: Sel) -> BOOL {
return YES;
extern "C" fn is_flipped(_: &Object, _: Sel) -> Bool {
return Bool::YES;
}
```

Here, we just want to tell `NSView` to use top,left as the origin point, so we need to respond `YES` in this subclass method.
Here, we just want to tell `NSView` to use top,left as the origin point, so we need to respond `Bool::YES` in this subclass method.

``` rust
extern "C" fn dragging_entered<T: ViewDelegate>(this: &mut Object, _: Sel, info: id) -> NSUInteger {
let view = utils::load::<T>(this, VIEW_DELEGATE_PTR);
view.dragging_entered(DragInfo {
info: unsafe { Id::from_ptr(info) }
info: unsafe { Id::retain(info).unwrap() }
}).into()
}
```
Expand Down
11 changes: 6 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@ rustdoc-args = ["--cfg", "docsrs"]

[dependencies]
bitmask-enum = "2.2.1"
block = "0.1.6"
core-foundation = "0.9"
core-graphics = "0.23"
objc = { version = "=0.3.0-beta.2", package = "objc2" }
block = { version = "=0.2.0-alpha.6", package = "block2" }
# Temporary: Patched versions that implement `Encode` for common types
# Branch: `objc2`
core-foundation = { git = "https://github.com/madsmtm/core-foundation-rs.git", rev = "7d593d016175755e492a92ef89edca68ac3bd5cd" }
core-graphics = { git = "https://github.com/madsmtm/core-foundation-rs.git", rev = "7d593d016175755e492a92ef89edca68ac3bd5cd" }
dispatch = "0.2.0"
infer = { version = "0.15", optional = true }
lazy_static = "1.4.0"
libc = "0.2"
objc = "0.2.7"
objc_id = "0.1.1"
os_info = "3.0.1"
url = "2.1.1"
uuid = { version = "1.1", features = ["v4"], optional = true }
Expand Down
6 changes: 3 additions & 3 deletions examples/browser/toolbar.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use cacao::objc::{msg_send, sel, sel_impl};
use cacao::objc::{msg_send, sel};

use cacao::button::Button;
use cacao::input::{TextField, TextFieldDelegate};
Expand Down Expand Up @@ -35,12 +35,12 @@ impl BrowserToolbar {
let back_button = Button::new("Back");
let mut back_item = ToolbarItem::new(BACK_BUTTON);
back_item.set_button(back_button);
back_item.set_action(|| Action::Back.dispatch());
back_item.set_action(|_| Action::Back.dispatch());

let forwards_button = Button::new("Forwards");
let mut forwards_item = ToolbarItem::new(FWDS_BUTTON);
forwards_item.set_button(forwards_button);
forwards_item.set_action(|| Action::Forwards.dispatch());
forwards_item.set_action(|_| Action::Forwards.dispatch());

let url_bar = TextField::with(URLBar);
let url_bar_item = ToolbarItem::new(URL_BAR);
Expand Down
16 changes: 8 additions & 8 deletions src/appkit/alert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,16 @@
//! }
//! ```

use objc::rc::{Id, Owned};
use objc::runtime::Object;
use objc::{class, msg_send, sel, sel_impl};
use objc_id::Id;
use objc::{class, msg_send, msg_send_id, sel};

use crate::foundation::{id, NSString};

/// Represents an `NSAlert`. Has no information other than the retained pointer to the Objective C
/// side, so... don't bother inspecting this.
#[derive(Debug)]
pub struct Alert(Id<Object>);
pub struct Alert(Id<Object, Owned>);

impl Alert {
/// Creates a basic `NSAlert`, storing a pointer to it in the Objective C runtime.
Expand All @@ -44,11 +44,11 @@ impl Alert {
let ok = NSString::new("OK");

Alert(unsafe {
let alert: id = msg_send![class!(NSAlert), new];
let _: () = msg_send![alert, setMessageText: title];
let _: () = msg_send![alert, setInformativeText: message];
let _: () = msg_send![alert, addButtonWithTitle: ok];
Id::from_ptr(alert)
let mut alert = msg_send_id![class!(NSAlert), new];
let _: () = msg_send![&mut alert, setMessageText: &*title];
let _: () = msg_send![&mut alert, setInformativeText: &*message];
let _: () = msg_send![&mut alert, addButtonWithTitle: &*ok];
alert
})
}

Expand Down
11 changes: 7 additions & 4 deletions src/appkit/animation.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use block::ConcreteBlock;
use objc::{class, msg_send, sel, sel_impl};
use objc::{class, msg_send, sel};

use crate::foundation::id;

Expand Down Expand Up @@ -39,7 +39,7 @@ impl AnimationContext {

unsafe {
//let context: id = msg_send![class!(NSAnimationContext), currentContext];
let _: () = msg_send![class!(NSAnimationContext), runAnimationGroup: block];
let _: () = msg_send![class!(NSAnimationContext), runAnimationGroup: &*block];
}
}

Expand All @@ -66,8 +66,11 @@ impl AnimationContext {

unsafe {
//let context: id = msg_send![class!(NSAnimationContext), currentContext];
let _: () = msg_send![class!(NSAnimationContext), runAnimationGroup:block
completionHandler:completion_block];
let _: () = msg_send![
class!(NSAnimationContext),
runAnimationGroup: &*block,
completionHandler: &*completion_block,
];
}
}
}
2 changes: 1 addition & 1 deletion src/appkit/app/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ use objc::runtime::Class;
use crate::foundation::load_or_register_class;

/// Used for injecting a custom NSApplication. Currently does nothing.
pub(crate) fn register_app_class() -> *const Class {
pub(crate) fn register_app_class() -> &'static Class {
load_or_register_class("NSApplication", "RSTApplication", |decl| unsafe {})
}
Loading

0 comments on commit 094ed59

Please sign in to comment.