@@ -4,24 +4,63 @@ use alloc::borrow::ToOwned as _;
44use core:: mem:: ManuallyDrop ;
55use core:: ptr:: NonNull ;
66use std:: thread;
7+ use std:: { borrow:: ToOwned as _, sync:: Once } ;
78
89use core_graphics_types:: {
910 base:: CGFloat ,
1011 geometry:: { CGRect , CGSize } ,
1112} ;
1213use metal:: { foreign_types:: ForeignType , MTLTextureType } ;
14+ use objc:: declare:: ClassDecl ;
15+ use objc:: runtime:: Sel ;
1316use objc:: {
1417 class, msg_send,
1518 rc:: { autoreleasepool, StrongPtr } ,
16- runtime:: { Object , BOOL , NO , YES } ,
19+ runtime:: { Class , Object , BOOL , NO , YES } ,
1720 sel, sel_impl,
1821} ;
1922use parking_lot:: { Mutex , RwLock } ;
2023
21- use crate :: metal:: layer_observer:: new_observer_layer;
22-
2324#[ 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+ }
2564
2665impl super :: Surface {
2766 fn new ( layer : metal:: MetalLayer ) -> Self {
@@ -98,14 +137,58 @@ impl super::Surface {
98137 // they expect us to do.
99138 unsafe { StrongPtr :: retain ( root_layer) }
100139 } 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.
103172 //
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) }
109192 }
110193 }
111194
0 commit comments