@@ -7,12 +7,15 @@ use core_graphics::base::{
7
7
use core_graphics:: color_space:: CGColorSpace ;
8
8
use core_graphics:: data_provider:: CGDataProvider ;
9
9
use core_graphics:: image:: CGImage ;
10
+ use objc2:: runtime:: AnyObject ;
10
11
use raw_window_handle:: { HasDisplayHandle , HasWindowHandle , RawWindowHandle } ;
11
12
12
- use cocoa:: appkit:: { NSView , NSViewHeightSizable , NSViewWidthSizable , NSWindow } ;
13
- use cocoa:: base:: { id, nil} ;
14
- use cocoa:: quartzcore:: { transaction, CALayer , ContentsGravity } ;
15
13
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 } ;
16
19
17
20
use std:: marker:: PhantomData ;
18
21
use std:: num:: NonZeroU32 ;
@@ -27,14 +30,19 @@ impl AsRef<[u8]> for Buffer {
27
30
}
28
31
29
32
pub struct CGImpl < D , W > {
30
- layer : CALayer ,
31
- window : id ,
33
+ layer : Id < CALayer > ,
34
+ window : Id < NSWindow > ,
32
35
color_space : CGColorSpace ,
33
36
size : Option < ( NonZeroU32 , NonZeroU32 ) > ,
34
37
window_handle : W ,
35
38
_display : PhantomData < D > ,
36
39
}
37
40
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
+
38
46
impl < D : HasDisplayHandle , W : HasWindowHandle > SurfaceInterface < D , W > for CGImpl < D , W > {
39
47
type Context = D ;
40
48
type Buffer < ' a > = BufferImpl < ' a , D , W > where Self : ' a ;
@@ -45,20 +53,35 @@ impl<D: HasDisplayHandle, W: HasWindowHandle> SurfaceInterface<D, W> for CGImpl<
45
53
RawWindowHandle :: AppKit ( handle) => handle,
46
54
_ => return Err ( InitError :: Unsupported ( window_src) ) ,
47
55
} ;
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 ( ) ;
51
67
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) ;
52
72
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) } ;
62
85
let color_space = CGColorSpace :: create_device_rgb ( ) ;
63
86
Ok ( Self {
64
87
layer,
@@ -131,17 +154,20 @@ impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for BufferImpl
131
154
// The CALayer has a default action associated with a change in the layer contents, causing
132
155
// a quarter second fade transition to happen every time a new buffer is applied. This can
133
156
// 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 ( ) ) ;
136
163
137
164
unsafe {
138
165
self . imp
139
166
. 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 ( ) ) ;
142
168
} ;
143
169
144
- transaction :: commit ( ) ;
170
+ CATransaction :: commit ( ) ;
145
171
146
172
Ok ( ( ) )
147
173
}
@@ -150,11 +176,3 @@ impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for BufferImpl
150
176
self . present ( )
151
177
}
152
178
}
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
- }
0 commit comments