Skip to content

Commit 28ebe6e

Browse files
authored
deps: Use objc2
Fixes #181. This also prevents accessing the `NSView`/`NSWindow` from other threads than the main thread. SoftBufferError is `#[non_exhaustive]`, so it's fine to add further variants to it. I decided against doing that though, as the error is platform-specific, and not one that the program can really handle in any reasonable way (it's closer to a programmer error).
1 parent a84c79e commit 28ebe6e

File tree

3 files changed

+52
-36
lines changed

3 files changed

+52
-36
lines changed

Cargo.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,12 @@ features = ["Win32_Graphics_Gdi", "Win32_UI_WindowsAndMessaging", "Win32_Foundat
4747

4848
[target.'cfg(target_os = "macos")'.dependencies]
4949
bytemuck = { version = "1.12.3", features = ["extern_crate_alloc"] }
50-
cocoa = "0.25.0"
5150
core-graphics = "0.23.1"
5251
foreign-types = "0.5.0"
53-
objc = "0.2.7"
52+
objc2 = "0.5.1"
53+
objc2-foundation = { version = "0.2.0", features = ["NSThread"] }
54+
objc2-app-kit = { version = "0.2.0", features = ["NSResponder", "NSView", "NSWindow"] }
55+
objc2-quartz-core = { version = "0.2.0", features = ["CALayer", "CATransaction"] }
5456

5557
[target.'cfg(target_arch = "wasm32")'.dependencies]
5658
js-sys = "0.3.63"
@@ -119,4 +121,3 @@ targets = [
119121
"x86_64-unknown-linux-gnu",
120122
"wasm32-unknown-unknown",
121123
]
122-

src/backends/cg.rs

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@ use core_graphics::base::{
77
use core_graphics::color_space::CGColorSpace;
88
use core_graphics::data_provider::CGDataProvider;
99
use core_graphics::image::CGImage;
10+
use objc2::runtime::AnyObject;
1011
use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawWindowHandle};
1112

12-
use cocoa::appkit::{NSView, NSViewHeightSizable, NSViewWidthSizable, NSWindow};
13-
use cocoa::base::{id, nil};
14-
use cocoa::quartzcore::{transaction, CALayer, ContentsGravity};
1513
use foreign_types::ForeignType;
14+
use objc2::msg_send;
15+
use objc2::rc::Id;
16+
use objc2_app_kit::{NSAutoresizingMaskOptions, NSView, NSWindow};
17+
use objc2_foundation::MainThreadMarker;
18+
use objc2_quartz_core::{kCAGravityTopLeft, CALayer, CATransaction};
1619

1720
use std::marker::PhantomData;
1821
use std::num::NonZeroU32;
@@ -27,14 +30,19 @@ impl AsRef<[u8]> for Buffer {
2730
}
2831

2932
pub struct CGImpl<D, W> {
30-
layer: CALayer,
31-
window: id,
33+
layer: Id<CALayer>,
34+
window: Id<NSWindow>,
3235
color_space: CGColorSpace,
3336
size: Option<(NonZeroU32, NonZeroU32)>,
3437
window_handle: W,
3538
_display: PhantomData<D>,
3639
}
3740

41+
// TODO(madsmtm): Expose this in `objc2_app_kit`.
42+
fn set_layer(view: &NSView, layer: &CALayer) {
43+
unsafe { msg_send![view, setLayer: layer] }
44+
}
45+
3846
impl<D: HasDisplayHandle, W: HasWindowHandle> SurfaceInterface<D, W> for CGImpl<D, W> {
3947
type Context = D;
4048
type Buffer<'a> = BufferImpl<'a, D, W> where Self: 'a;
@@ -45,20 +53,35 @@ impl<D: HasDisplayHandle, W: HasWindowHandle> SurfaceInterface<D, W> for CGImpl<
4553
RawWindowHandle::AppKit(handle) => handle,
4654
_ => return Err(InitError::Unsupported(window_src)),
4755
};
48-
let view = handle.ns_view.as_ptr() as id;
49-
let window: id = unsafe { msg_send![view, window] };
50-
let window: id = unsafe { msg_send![window, retain] };
56+
57+
// `NSView` can only be accessed from the main thread.
58+
let mtm = MainThreadMarker::new().ok_or(SoftBufferError::PlatformError(
59+
Some("can only access AppKit / macOS handles from the main thread".to_string()),
60+
None,
61+
))?;
62+
let view = handle.ns_view.as_ptr();
63+
// SAFETY: The pointer came from `WindowHandle`, which ensures that
64+
// the `AppKitWindowHandle` contains a valid pointer to an `NSView`.
65+
// Unwrap is fine, since the pointer came from `NonNull`.
66+
let view: Id<NSView> = unsafe { Id::retain(view.cast()) }.unwrap();
5167
let layer = CALayer::new();
68+
let subview = unsafe { NSView::initWithFrame(mtm.alloc(), view.frame()) };
69+
layer.setContentsGravity(unsafe { kCAGravityTopLeft });
70+
layer.setNeedsDisplayOnBoundsChange(false);
71+
set_layer(&subview, &layer);
5272
unsafe {
53-
let subview: id = NSView::alloc(nil).initWithFrame_(NSView::frame(view));
54-
layer.set_contents_gravity(ContentsGravity::TopLeft);
55-
layer.set_needs_display_on_bounds_change(false);
56-
subview.setLayer(layer.id());
57-
subview.setAutoresizingMask_(NSViewWidthSizable | NSViewHeightSizable);
58-
59-
view.addSubview_(subview); // retains subview (+1) = 2
60-
let _: () = msg_send![subview, release]; // releases subview (-1) = 1
61-
}
73+
subview.setAutoresizingMask(NSAutoresizingMaskOptions(
74+
NSAutoresizingMaskOptions::NSViewWidthSizable.0
75+
| NSAutoresizingMaskOptions::NSViewHeightSizable.0,
76+
))
77+
};
78+
79+
let window = view.window().ok_or(SoftBufferError::PlatformError(
80+
Some("view must be inside a window".to_string()),
81+
None,
82+
))?;
83+
84+
unsafe { view.addSubview(&subview) };
6285
let color_space = CGColorSpace::create_device_rgb();
6386
Ok(Self {
6487
layer,
@@ -131,17 +154,20 @@ impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for BufferImpl
131154
// The CALayer has a default action associated with a change in the layer contents, causing
132155
// a quarter second fade transition to happen every time a new buffer is applied. This can
133156
// be mitigated by wrapping the operation in a transaction and disabling all actions.
134-
transaction::begin();
135-
transaction::set_disable_actions(true);
157+
CATransaction::begin();
158+
CATransaction::setDisableActions(true);
159+
160+
self.imp
161+
.layer
162+
.setContentsScale(self.imp.window.backingScaleFactor());
136163

137164
unsafe {
138165
self.imp
139166
.layer
140-
.set_contents_scale(self.imp.window.backingScaleFactor());
141-
self.imp.layer.set_contents(image.as_ptr() as id);
167+
.setContents((image.as_ptr() as *mut AnyObject).as_ref());
142168
};
143169

144-
transaction::commit();
170+
CATransaction::commit();
145171

146172
Ok(())
147173
}
@@ -150,11 +176,3 @@ impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for BufferImpl
150176
self.present()
151177
}
152178
}
153-
154-
impl<D, W> Drop for CGImpl<D, W> {
155-
fn drop(&mut self) {
156-
unsafe {
157-
let _: () = msg_send![self.window, release];
158-
}
159-
}
160-
}

src/lib.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@
33
#![warn(missing_docs)]
44
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
55

6-
#[cfg(target_os = "macos")]
7-
#[macro_use]
8-
extern crate objc;
96
extern crate core;
107

118
mod backend_dispatch;

0 commit comments

Comments
 (0)