2020 cell:: RefCell ,
2121 os:: raw:: c_void,
2222 sync:: { mpsc, Arc , Mutex } ,
23- thread:: { self } ,
2423 } ,
2524} ;
2625
@@ -30,7 +29,6 @@ struct MainThreadState {
3029 update_requested : bool ,
3130 view : * mut Object ,
3231 keymods : KeyMods ,
33- cur_msg : Message ,
3432}
3533
3634struct IosDisplay {
@@ -44,6 +42,12 @@ struct IosDisplay {
4442 _gles2 : bool ,
4543 f : Option < Box < dyn ' static + FnOnce ( ) -> Box < dyn EventHandler > > > ,
4644 state : Arc < Mutex < MainThreadState > > ,
45+ /// UIKit-side events; drained at the start of every `drawInMTKView:`.
46+ messages_rx : mpsc:: Receiver < Message > ,
47+ /// User-code requests (`schedule_update` etc.); same drain
48+ /// cadence as `messages_rx`.
49+ requests_rx : mpsc:: Receiver < crate :: native:: Request > ,
50+ blocking_event_loop : bool ,
4751}
4852
4953impl IosDisplay {
@@ -79,6 +83,77 @@ fn get_window_payload(this: &Object) -> &mut IosDisplay {
7983 }
8084}
8185
86+ /// Apply a `Message` to the payload's event handler + state. Called
87+ /// inline at the start of each `drawInMTKView:` for every pending
88+ /// message.
89+ fn dispatch_message ( payload : & mut IosDisplay , msg : Message ) {
90+ match msg {
91+ Message :: Pause => {
92+ payload. state . lock ( ) . unwrap ( ) . paused = true ;
93+ }
94+ Message :: Resume => {
95+ payload. state . lock ( ) . unwrap ( ) . paused = false ;
96+ }
97+ Message :: Destroy => {
98+ payload. state . lock ( ) . unwrap ( ) . quit = true ;
99+ }
100+ Message :: Touch {
101+ phase,
102+ touch_id,
103+ x,
104+ y,
105+ } => {
106+ if let Some ( ref mut event_handler) = payload. event_handler {
107+ event_handler. touch_event ( phase, touch_id, x, y) ;
108+ }
109+ }
110+ Message :: Character { character } => {
111+ if let Some ( character) = char:: from_u32 ( character) {
112+ if let Some ( ref mut event_handler) = payload. event_handler {
113+ event_handler. char_event ( character, Default :: default ( ) , false ) ;
114+ }
115+ }
116+ }
117+ Message :: KeyDown { keycode } => {
118+ let keymods = {
119+ let mut state = payload. state . lock ( ) . unwrap ( ) ;
120+ match keycode {
121+ KeyCode :: LeftShift | KeyCode :: RightShift => state. keymods . shift = true ,
122+ KeyCode :: LeftControl | KeyCode :: RightControl => state. keymods . ctrl = true ,
123+ KeyCode :: LeftAlt | KeyCode :: RightAlt => state. keymods . alt = true ,
124+ KeyCode :: LeftSuper | KeyCode :: RightSuper => state. keymods . logo = true ,
125+ _ => { }
126+ }
127+ state. keymods
128+ } ;
129+ if let Some ( ref mut event_handler) = payload. event_handler {
130+ event_handler. key_down_event ( keycode, keymods, false ) ;
131+ }
132+ }
133+ Message :: KeyUp { keycode } => {
134+ let keymods = {
135+ let mut state = payload. state . lock ( ) . unwrap ( ) ;
136+ match keycode {
137+ KeyCode :: LeftShift | KeyCode :: RightShift => state. keymods . shift = false ,
138+ KeyCode :: LeftControl | KeyCode :: RightControl => state. keymods . ctrl = false ,
139+ KeyCode :: LeftAlt | KeyCode :: RightAlt => state. keymods . alt = false ,
140+ KeyCode :: LeftSuper | KeyCode :: RightSuper => state. keymods . logo = false ,
141+ _ => { }
142+ }
143+ state. keymods
144+ } ;
145+ if let Some ( ref mut event_handler) = payload. event_handler {
146+ event_handler. key_up_event ( keycode, keymods) ;
147+ }
148+ }
149+ Message :: Resize { width, height } => {
150+ if let Some ( ref mut event_handler) = payload. event_handler {
151+ event_handler. resize_event ( width as _ , height as _ ) ;
152+ }
153+ }
154+ }
155+ }
156+
82157#[ derive( Debug , Clone , Copy ) ]
83158enum Message {
84159 Resize {
@@ -188,79 +263,6 @@ pub fn define_glk_or_mtk_view(superclass: &Class) -> *const Class {
188263 on_touch ( this, event, TouchPhase :: Cancelled ) ;
189264 }
190265
191- extern "C" fn process_message ( this : & Object , _: Sel , _: ObjcId ) {
192- let payload = get_window_payload ( this) ;
193- if payload. event_handler . is_none ( ) {
194- payload. init_event_handler ( ) ;
195- }
196- let msg = {
197- let state = payload. state . lock ( ) . unwrap ( ) ;
198- state. cur_msg
199- } ;
200- match msg {
201- Message :: Pause => {
202- let mut state = payload. state . lock ( ) . unwrap ( ) ;
203- state. paused = true ;
204- }
205- Message :: Resume => {
206- let mut state = payload. state . lock ( ) . unwrap ( ) ;
207- state. paused = false ;
208- }
209- Message :: Destroy => {
210- let mut state = payload. state . lock ( ) . unwrap ( ) ;
211- state. quit = true ;
212- }
213- Message :: Touch {
214- phase,
215- touch_id,
216- x,
217- y,
218- } => {
219- if let Some ( ref mut event_handler) = payload. event_handler {
220- event_handler. touch_event ( phase, touch_id, x, y) ;
221- }
222- }
223- Message :: Character { character } => {
224- if let Some ( character) = char:: from_u32 ( character) {
225- if let Some ( ref mut event_handler) = payload. event_handler {
226- event_handler. char_event ( character, Default :: default ( ) , false ) ;
227- }
228- }
229- }
230- Message :: KeyDown { keycode } => {
231- let mut state = payload. state . lock ( ) . unwrap ( ) ;
232- match keycode {
233- KeyCode :: LeftShift | KeyCode :: RightShift => state. keymods . shift = true ,
234- KeyCode :: LeftControl | KeyCode :: RightControl => state. keymods . ctrl = true ,
235- KeyCode :: LeftAlt | KeyCode :: RightAlt => state. keymods . alt = true ,
236- KeyCode :: LeftSuper | KeyCode :: RightSuper => state. keymods . logo = true ,
237- _ => { }
238- }
239- if let Some ( ref mut event_handler) = payload. event_handler {
240- event_handler. key_down_event ( keycode, state. keymods , false ) ;
241- }
242- }
243- Message :: KeyUp { keycode } => {
244- let mut state = payload. state . lock ( ) . unwrap ( ) ;
245- match keycode {
246- KeyCode :: LeftShift | KeyCode :: RightShift => state. keymods . shift = false ,
247- KeyCode :: LeftControl | KeyCode :: RightControl => state. keymods . ctrl = false ,
248- KeyCode :: LeftAlt | KeyCode :: RightAlt => state. keymods . alt = false ,
249- KeyCode :: LeftSuper | KeyCode :: RightSuper => state. keymods . logo = false ,
250- _ => { }
251- }
252- if let Some ( ref mut event_handler) = payload. event_handler {
253- event_handler. key_up_event ( keycode, state. keymods ) ;
254- }
255- }
256- Message :: Resize { width, height } => {
257- if let Some ( ref mut event_handler) = payload. event_handler {
258- event_handler. resize_event ( width as _ , height as _ ) ;
259- }
260- }
261- }
262- }
263-
264266 unsafe {
265267 decl. add_method ( sel ! ( isOpaque) , yes as extern "C" fn ( & Object , Sel ) -> BOOL ) ;
266268 decl. add_method (
@@ -279,10 +281,6 @@ pub fn define_glk_or_mtk_view(superclass: &Class) -> *const Class {
279281 sel ! ( touchesCanceled: withEvent: ) ,
280282 touches_canceled as extern "C" fn ( & Object , Sel , ObjcId , ObjcId ) ,
281283 ) ;
282- decl. add_method (
283- sel ! ( processMessage: ) ,
284- process_message as extern "C" fn ( & Object , Sel , ObjcId ) ,
285- ) ;
286284 }
287285
288286 decl. add_ivar :: < * mut c_void > ( "display_ptr" ) ;
@@ -326,6 +324,25 @@ pub fn define_glk_or_mtk_view_dlg(superclass: &Class) -> *const Class {
326324 payload. init_event_handler ( ) ;
327325 }
328326
327+ // Drain requests + UIKit-side messages and dispatch inline
328+ // before drawing this frame.
329+ while let Ok ( request) = payload. requests_rx . try_recv ( ) {
330+ payload. state . lock ( ) . unwrap ( ) . process_request ( request) ;
331+ }
332+ while let Ok ( msg) = payload. messages_rx . try_recv ( ) {
333+ dispatch_message ( payload, msg) ;
334+ }
335+
336+ // Skip the draw if paused or, in `blocking_event_loop` mode,
337+ // if no update is pending. `CADisplayLink` keeps ticking
338+ // cheaply.
339+ if payload. state . lock ( ) . unwrap ( ) . paused {
340+ return ;
341+ }
342+ if payload. blocking_event_loop && !payload. state . lock ( ) . unwrap ( ) . update_requested {
343+ return ;
344+ }
345+
329346 let main_screen: ObjcId = unsafe { msg_send ! [ class!( UIScreen ) , mainScreen] } ;
330347 let screen_rect: NSRect = unsafe { msg_send ! [ main_screen, bounds] } ;
331348 let high_dpi = native_display ( ) . lock ( ) . unwrap ( ) . high_dpi ;
@@ -463,8 +480,10 @@ unsafe fn create_metal_view(screen_rect: NSRect, _sample_count: i32, _high_dpi:
463480
464481 msg_send_ ! [ view_ctrl_obj, setView: mtk_view_obj] ;
465482
466- msg_send_ ! [ mtk_view_obj, setEnableSetNeedsDisplay: YES ] ;
467- msg_send_ ! [ mtk_view_obj, setPaused: YES ] ;
483+ // Continuous draw — `CADisplayLink` drives `drawInMTKView:` on
484+ // the main thread at `preferredFramesPerSecond`.
485+ msg_send_ ! [ mtk_view_obj, setEnableSetNeedsDisplay: NO ] ;
486+ msg_send_ ! [ mtk_view_obj, setPaused: NO ] ;
468487 msg_send_ ! [ mtk_view_obj, setPreferredFramesPerSecond: 60 ] ;
469488 msg_send_ ! [ mtk_view_obj, setDelegate: mtk_view_dlg_obj] ;
470489 let device = MTLCreateSystemDefaultDevice ( ) ;
@@ -499,7 +518,11 @@ pub fn define_app_delegate() -> *const Class {
499518 _: ObjcId ,
500519 ) -> BOOL {
501520 unsafe {
502- let ( f, conf) = RUN_ARGS . take ( ) . unwrap ( ) ;
521+ // Routed through a raw pointer to satisfy the Rust 2024
522+ // `static_mut_refs` lint. Split across two statements so
523+ // clippy's `deref_addrof` doesn't fold it back.
524+ let run_args_ptr = & raw mut RUN_ARGS ;
525+ let ( f, conf) = ( * run_args_ptr) . take ( ) . unwrap ( ) ;
503526
504527 let main_screen: ObjcId = msg_send ! [ class!( UIScreen ) , mainScreen] ;
505528 let screen_rect: NSRect = msg_send ! [ main_screen, bounds] ;
@@ -582,7 +605,6 @@ pub fn define_app_delegate() -> *const Class {
582605 alt : false ,
583606 logo : false ,
584607 } ,
585- cur_msg : Message :: Resume ,
586608 } ) ) ;
587609
588610 let payload = Box :: new ( IosDisplay {
@@ -596,6 +618,9 @@ pub fn define_app_delegate() -> *const Class {
596618 event_handler : None ,
597619 _gles2 : view. _gles2 ,
598620 state : state_original. clone ( ) ,
621+ messages_rx : rx,
622+ requests_rx,
623+ blocking_event_loop : conf. platform . blocking_event_loop ,
599624 } ) ;
600625 let payload_ptr = Box :: into_raw ( payload) as * mut std:: ffi:: c_void ;
601626
@@ -609,79 +634,8 @@ pub fn define_app_delegate() -> *const Class {
609634
610635 msg_send_ ! [ window_obj, makeKeyAndVisible] ;
611636
612- struct SendHack < F > ( F ) ;
613- unsafe impl < F > Send for SendHack < F > { }
614-
615- let state = SendHack ( state_original. clone ( ) ) ;
616- thread:: spawn ( move || {
617- let s = state. 0 ;
618-
619- loop {
620- while let Ok ( request) = requests_rx. try_recv ( ) {
621- s. lock ( ) . unwrap ( ) . process_request ( request) ;
622- }
623-
624- let block_on_wait = {
625- let s = s. lock ( ) . unwrap ( ) ;
626- ( conf. platform . blocking_event_loop && !s. update_requested ) || s. paused
627- } ;
628-
629- if block_on_wait {
630- let res = rx. recv ( ) ;
631-
632- if let Ok ( msg) = res {
633- let view;
634- {
635- let mut s = s. lock ( ) . unwrap ( ) ;
636- view = s. view ;
637- s. cur_msg = msg;
638- }
639- msg_send_ ! [ & * view, performSelectorOnMainThread: sel!( processMessage: ) withObject: nil waitUntilDone: YES ] ;
640- }
641- } else {
642- // process all the messages from the main thread
643- while let Ok ( msg) = rx. try_recv ( ) {
644- let view;
645- {
646- let mut s = s. lock ( ) . unwrap ( ) ;
647- view = s. view ;
648- s. cur_msg = msg;
649- }
650- msg_send_ ! [ & * view, performSelectorOnMainThread: sel!( processMessage: ) withObject: nil waitUntilDone: YES ] ;
651- }
652- }
653-
654- let update_requested;
655- let view;
656- {
657- let s = s. lock ( ) . unwrap ( ) ;
658- update_requested = s. update_requested ;
659- view = s. view ;
660- }
661-
662- if !conf. platform . blocking_event_loop || update_requested {
663- match conf. platform . apple_gfx_api {
664- AppleGfxApi :: OpenGl => {
665- // Why it differs from Metal? I don't realy know. Looks like a bug.
666- // Somehow it needs `setNeedsDisplay` to redraw after touch.
667- // With plain `display` it draws only after another touch.
668- // But when it's not blocking_event_loop it makes fps really drop with `setNeedsDisplay`.
669- // I hope it will work the same on the real device.
670- if conf. platform . blocking_event_loop {
671- msg_send_ ! [ & * view, performSelectorOnMainThread: sel!( setNeedsDisplay) withObject: nil waitUntilDone: NO ] ;
672- } else {
673- msg_send_ ! [ & * view, performSelectorOnMainThread: sel!( display) withObject: nil waitUntilDone: YES ] ;
674- }
675- }
676- AppleGfxApi :: Metal => {
677- msg_send_ ! [ & * view, performSelectorOnMainThread: sel!( setNeedsDisplay) withObject: nil waitUntilDone: NO ] ;
678- }
679- }
680- }
681-
682- thread:: yield_now ( ) ;
683- }
684- } ) ;
637+ // No background render thread — `CADisplayLink` drives
638+ // `drawInMTKView:` directly.
685639 }
686640 YES
687641 }
0 commit comments