From aea8b22689e39fe9d237fe07d21a93eb5069e22b Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Mon, 12 Jun 2023 13:13:44 -0700 Subject: [PATCH] Foundation: unmask Windows events on the main RunLoop When processing events on the main runloop, we should unmask the Windows message pump events. If we do not unmask these events, the main RunLoop is unable to process the UI events and the user has no mechanism for integrating the Windows message pump events into the run loop. This is a slightly restrictive compromise to enable the use of RunLoop on Windows for pumping a UI application. Because CoreFoundation is not a supported API surface a user cannot simply invoke the appropriate CoreFoundation function on the RunLoop's inner (CFRunLoop) object. In order to not expose any new CoreFoundation APIs, we need to expose the functionality from the Swift layer. This is motivated by the desire to minimise the divergence from the reference implementation of CoreFoundation. The Swift API surface is meant to be near identical to the Objective-C implementation. An option would be to expose a SPI for users to control the behvaiour of the `RunLoop` with regards to the Windows message loop. One caveat that exists is that CoreFoundation seems to exempt the common modes from being able to participate in the windows message loop. Fortunately, the main run loop, which is tied to the main thread, is configured to run in the default mode which may participate in the OS message loop. Additionally, the Windows UI system expects to perform the operations on the main thread, which gives us a natural point to alter the behaviour of `RunLoop`. There are two instances of `CFRunLoopGetMain`, one which provides the storage for the main runloop, and one which is the accessor for the main runloop. Because the accessor will perform additional reference counting and bridging behavioural changes, use the `CFRunLoop` storage of the `_mainRunLoop` to acquire the main run loop. Co-authored-by: Darin Fisher --- Sources/Foundation/RunLoop.swift | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Sources/Foundation/RunLoop.swift b/Sources/Foundation/RunLoop.swift index 66dbf209e5..5ad595e6ef 100644 --- a/Sources/Foundation/RunLoop.swift +++ b/Sources/Foundation/RunLoop.swift @@ -62,9 +62,16 @@ open class RunLoop: NSObject { get { unsafeBitCast(_cfRunLoopStorage, to: CFRunLoop?.self) } set { _cfRunLoopStorage = newValue } } - - internal static var _mainRunLoop : RunLoop = { - return RunLoop(cfObject: CFRunLoopGetMain()) + + internal static var _mainRunLoop: RunLoop = { + let cfObject: CFRunLoop! = CFRunLoopGetMain() +#if os(Windows) + // Enable the main runloop on Windows to process the Windows UI events. + // Windows, similar to AppKit and UIKit, expects to process the UI + // events on the main thread. + _CFRunLoopSetWindowsMessageQueueMask(cfObject, QS_ALLINPUT, kCFRunLoopDefaultMode) +#endif + return RunLoop(cfObject: cfObject) }() internal init(cfObject : CFRunLoop) { @@ -76,7 +83,7 @@ open class RunLoop: NSObject { } open class var main: RunLoop { - return _CFRunLoopGet2(CFRunLoopGetMain()) as! RunLoop + return _CFRunLoopGet2(_mainRunLoop._cfRunLoop) as! RunLoop } open var currentMode: RunLoop.Mode? {