Skip to content

Commit 64bd984

Browse files
authored
Merge pull request #1 from skiptools/looper-system-choreographer
Add AndroidLooper, AndroidChoreographer, and AndroidSystem targets
2 parents 95b2534 + c3663e2 commit 64bd984

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+10033
-16
lines changed

Package.swift

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,55 @@ let package = Package(
55
name: "swift-android-native",
66
products: [
77
.library(name: "AndroidNative", targets: ["AndroidNative"]),
8-
.library(name: "AndroidLogging", targets: ["AndroidLogging"])
8+
.library(name: "AndroidLogging", targets: ["AndroidLogging"]),
9+
.library(name: "AndroidLooper", targets: ["AndroidLooper"]),
10+
.library(name: "AndroidChoreographer", targets: ["AndroidLooper"]),
11+
],
12+
dependencies: [
913
],
1014
targets: [
11-
.systemLibrary(name: "AndroidNDK"),
12-
.target(name: "AndroidLogging", dependencies: [.target(name: "AndroidNDK", condition: .when(platforms: [.android]))]),
13-
.testTarget(name: "AndroidLoggingTests", dependencies: ["AndroidLogging"]),
14-
.target(name: "AndroidNative", dependencies: ["AndroidLogging"]),
15-
.testTarget(name: "AndroidNativeTests", dependencies: ["AndroidNative"]),
15+
.target(name: "AndroidNDK", linkerSettings: [
16+
.linkedLibrary("android", .when(platforms: [.android])),
17+
.linkedLibrary("log", .when(platforms: [.android])),
18+
]),
19+
.target(name: "ConcurrencyRuntimeC"),
20+
.target(name: "AndroidSystem", dependencies: [
21+
.target(name: "AndroidNDK", condition: .when(platforms: [.android]))
22+
], swiftSettings: [
23+
.define("SYSTEM_PACKAGE_DARWIN", .when(platforms: [.macOS, .macCatalyst, .iOS, .watchOS, .tvOS, .visionOS])),
24+
.define("SYSTEM_PACKAGE"),
25+
]),
26+
.testTarget(name: "AndroidSystemTests", dependencies: [
27+
"AndroidSystem",
28+
]),
29+
.target(name: "AndroidLogging", dependencies: [
30+
.target(name: "AndroidNDK", condition: .when(platforms: [.android])),
31+
]),
32+
.testTarget(name: "AndroidLoggingTests", dependencies: [
33+
"AndroidLogging",
34+
]),
35+
.target(name: "AndroidLooper", dependencies: [
36+
"AndroidSystem",
37+
"AndroidLogging",
38+
"ConcurrencyRuntimeC",
39+
]),
40+
.testTarget(name: "AndroidLooperTests", dependencies: [
41+
"AndroidLooper",
42+
]),
43+
.target(name: "AndroidChoreographer", dependencies: [
44+
"AndroidSystem",
45+
"AndroidLogging",
46+
]),
47+
.testTarget(name: "AndroidChoreographerTests", dependencies: [
48+
"AndroidChoreographer",
49+
]),
50+
.target(name: "AndroidNative", dependencies: [
51+
"AndroidLogging",
52+
"AndroidLooper",
53+
"AndroidChoreographer",
54+
]),
55+
.testTarget(name: "AndroidNativeTests", dependencies: [
56+
"AndroidNative",
57+
]),
1658
]
1759
)
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#if os(Android)
2+
import Android
3+
import AndroidNDK
4+
import AndroidLogging
5+
import CoreFoundation
6+
7+
let logger = Logger(subsystem: "AndroidChoreographer", category: "AndroidChoreographer")
8+
9+
public final class AndroidChoreographer : @unchecked Sendable {
10+
private let _choreographer: OpaquePointer
11+
12+
/// Get the AChoreographer instance for the main thread.
13+
///
14+
/// Must be initialized at startup time with `setupMainChoreographer()`
15+
public private(set) static var main: AndroidChoreographer!
16+
17+
/// Get the AChoreographer instance for the current thread.
18+
///
19+
/// This must be called on an ALooper thread.
20+
public static var current: AndroidChoreographer {
21+
AndroidChoreographer(choreographer: AChoreographer_getInstance())
22+
}
23+
24+
init(choreographer: OpaquePointer) {
25+
self._choreographer = choreographer
26+
}
27+
28+
/// Add a callback to the Choreographer to invoke `_dispatch_main_queue_callback_4CF` on each frame to drain the main queue
29+
public static func setupMainChoreographer() {
30+
if Self.main == nil {
31+
logger.info("setupMainQueue")
32+
Self.main = AndroidChoreographer.current
33+
//enqueueMainChoreographer()
34+
}
35+
}
36+
37+
public func postFrameCallback(_ callback: @convention(c)(Int, UnsafeMutableRawPointer?) -> ()) {
38+
AChoreographer_postFrameCallback(_choreographer, callback, nil)
39+
}
40+
}
41+
42+
// no longer used: we use the AndroidLooper instead, which will be more efficient than trying to drain the main queue on every frame render
43+
44+
//private func enqueueMainChoreographer() {
45+
// AndroidChoreographer.current.postFrameCallback(choreographerCallback)
46+
//}
47+
//
48+
//// C-compatible callback wrapper
49+
//private var choreographerCallback: AChoreographer_frameCallback64 = { _, _ in
50+
// // Drain the main queue
51+
// //_dispatch_main_queue_callback_4CF()
52+
// while CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, true) == CFRunLoopRunResult.handledSource {
53+
// // continue handling queued events without a timeout
54+
// }
55+
//
56+
// // AChoreographer_postFrameCallback64 is single-shot, so we need to re-enqueue the callback each frame
57+
// enqueueMainChoreographer()
58+
//}
59+
60+
//// https://github.com/apple-oss-distributions/libdispatch/blob/bd82a60ee6a73b4eca50af028b48643d51aaf1ea/src/queue.c#L8237
61+
//// https://forums.swift.org/t/main-dispatch-queue-in-linux-sdl-app/31708/3
62+
//@_silgen_name("_dispatch_main_queue_callback_4CF")
63+
//func _dispatch_main_queue_callback_4CF()
64+
65+
#endif

0 commit comments

Comments
 (0)