@@ -4,24 +4,63 @@ use alloc::borrow::ToOwned as _;
4
4
use core:: mem:: ManuallyDrop ;
5
5
use core:: ptr:: NonNull ;
6
6
use std:: thread;
7
+ use std:: { borrow:: ToOwned as _, sync:: Once } ;
7
8
8
9
use core_graphics_types:: {
9
10
base:: CGFloat ,
10
11
geometry:: { CGRect , CGSize } ,
11
12
} ;
12
13
use metal:: { foreign_types:: ForeignType , MTLTextureType } ;
14
+ use objc:: declare:: ClassDecl ;
15
+ use objc:: runtime:: Sel ;
13
16
use objc:: {
14
17
class, msg_send,
15
18
rc:: { autoreleasepool, StrongPtr } ,
16
- runtime:: { Object , BOOL , NO , YES } ,
19
+ runtime:: { Class , Object , BOOL , NO , YES } ,
17
20
sel, sel_impl,
18
21
} ;
19
22
use parking_lot:: { Mutex , RwLock } ;
20
23
21
- use crate :: metal:: layer_observer:: new_observer_layer;
22
-
23
24
#[ link( name = "QuartzCore" , kind = "framework" ) ]
24
- extern "C" { }
25
+ extern "C" {
26
+ #[ allow( non_upper_case_globals) ]
27
+ static kCAGravityResize: * mut Object ;
28
+ }
29
+
30
+ extern "C" fn layer_should_inherit_contents_scale_from_window (
31
+ _: & Class ,
32
+ _: Sel ,
33
+ _layer : * mut Object ,
34
+ _new_scale : CGFloat ,
35
+ _from_window : * mut Object ,
36
+ ) -> BOOL {
37
+ YES
38
+ }
39
+
40
+ static CAML_DELEGATE_REGISTER : Once = Once :: new ( ) ;
41
+
42
+ #[ derive( Debug ) ]
43
+ pub struct HalManagedMetalLayerDelegate ( & ' static Class ) ;
44
+
45
+ impl HalManagedMetalLayerDelegate {
46
+ pub fn new ( ) -> Self {
47
+ let class_name = format ! ( "HalManagedMetalLayerDelegate@{:p}" , & CAML_DELEGATE_REGISTER ) ;
48
+
49
+ CAML_DELEGATE_REGISTER . call_once ( || {
50
+ type Fun = extern "C" fn ( & Class , Sel , * mut Object , CGFloat , * mut Object ) -> BOOL ;
51
+ let mut decl = ClassDecl :: new ( & class_name, class ! ( NSObject ) ) . unwrap ( ) ;
52
+ unsafe {
53
+ // <https://developer.apple.com/documentation/appkit/nsviewlayercontentscaledelegate/3005294-layer?language=objc>
54
+ decl. add_class_method :: < Fun > (
55
+ sel ! ( layer: shouldInheritContentsScale: fromWindow: ) ,
56
+ layer_should_inherit_contents_scale_from_window,
57
+ ) ;
58
+ }
59
+ decl. register ( ) ;
60
+ } ) ;
61
+ Self ( Class :: get ( & class_name) . unwrap ( ) )
62
+ }
63
+ }
25
64
26
65
impl super :: Surface {
27
66
fn new ( layer : metal:: MetalLayer ) -> Self {
@@ -98,14 +137,58 @@ impl super::Surface {
98
137
// they expect us to do.
99
138
unsafe { StrongPtr :: retain ( root_layer) }
100
139
} else {
101
- // The view does not have a `CAMetalLayer` as the root layer (this
102
- // is the default for most views).
140
+ // Create a new sublayer.
141
+ let new_layer: * mut Object = msg_send ! [ class!( CAMetalLayer ) , new] ;
142
+ let ( ) = msg_send ! [ view. as_ptr( ) , setLayer: new_layer] ;
143
+
144
+ #[ cfg( target_os = "macos" ) ]
145
+ if false {
146
+ // Automatically resize the sublayer's frame to match the
147
+ // superlayer's bounds.
148
+ //
149
+ // Note that there is a somewhat hidden design decision in this:
150
+ // We define the `width` and `height` in `configure` to control
151
+ // the `drawableSize` of the layer, while `bounds` and `frame` are
152
+ // outside of the user's direct control - instead, though, they
153
+ // can control the size of the view (or root layer), and get the
154
+ // desired effect that way.
155
+ //
156
+ // We _could_ also let `configure` set the `bounds` size, however
157
+ // that would be inconsistent with using the root layer directly
158
+ // (as we may do, see above).
159
+ let width_sizable = 1 << 1 ; // kCALayerWidthSizable
160
+ let height_sizable = 1 << 4 ; // kCALayerHeightSizable
161
+ let mask: std:: ffi:: c_uint = width_sizable | height_sizable;
162
+ let ( ) = msg_send ! [ new_layer, setAutoresizingMask: mask] ;
163
+ }
164
+
165
+ // Specify the relative size that the auto resizing mask above
166
+ // will keep (i.e. tell it to fill out its superlayer).
167
+ let frame: CGRect = msg_send ! [ root_layer, bounds] ;
168
+ let ( ) = msg_send ! [ new_layer, setFrame: frame] ;
169
+
170
+ // The gravity to use when the layer's `drawableSize` isn't the
171
+ // same as the bounds rectangle.
103
172
//
104
- // This case is trickier! We cannot use the existing layer with
105
- // Metal, so we must do something else. There are a few options,
106
- // we do the same as outlined in:
107
- // https://docs.rs/raw-window-metal/1.1.0/raw_window_metal/#reasoning-behind-creating-a-sublayer
108
- unsafe { new_observer_layer ( root_layer) }
173
+ // The desired content gravity is `kCAGravityResize`, because it
174
+ // masks / alleviates issues with resizing when
175
+ // `present_with_transaction` is disabled, and behaves better when
176
+ // moving the window between monitors.
177
+ //
178
+ // Unfortunately, it also makes it harder to see changes to
179
+ // `width` and `height` in `configure`. When debugging resize
180
+ // issues, swap this for `kCAGravityTopLeft` instead.
181
+ let _: ( ) = msg_send ! [ new_layer, setContentsGravity: unsafe { kCAGravityResize } ] ;
182
+
183
+ // Set initial scale factor of the layer. This is kept in sync by
184
+ // `configure` (on UIKit), and the delegate below (on AppKit).
185
+ let scale_factor: CGFloat = msg_send ! [ root_layer, contentsScale] ;
186
+ let ( ) = msg_send ! [ new_layer, setContentsScale: scale_factor] ;
187
+
188
+ let delegate = HalManagedMetalLayerDelegate :: new ( ) ;
189
+ let ( ) = msg_send ! [ new_layer, setDelegate: delegate. 0 ] ;
190
+
191
+ unsafe { StrongPtr :: new ( new_layer) }
109
192
}
110
193
}
111
194
0 commit comments