Skip to content

Commit

Permalink
Use Swift C++ interop to more closely integrate with Flutter engine
Browse files Browse the repository at this point in the history
  • Loading branch information
lhoward committed Feb 11, 2025
1 parent d12d24a commit 885160b
Show file tree
Hide file tree
Showing 11 changed files with 116 additions and 78 deletions.
22 changes: 17 additions & 5 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ var targetPluginUsages = [Target.PluginUsage]()
var platformCxxSettings: [CXXSetting] = []
var platformSwiftSettings: [SwiftSetting] = [.swiftLanguageMode(.v5)]

func tryGuessSwiftLibRoot() -> String {
func tryGuessSwiftRoot() -> String {
let task = Process()
task.executableURL = URL(fileURLWithPath: "/bin/sh")
task.arguments = ["-c", "which swift"]
Expand All @@ -22,13 +22,14 @@ func tryGuessSwiftLibRoot() -> String {
try task.run()
let outputData = (task.standardOutput as! Pipe).fileHandleForReading.readDataToEndOfFile()
let path = URL(fileURLWithPath: String(decoding: outputData, as: UTF8.self))
return path.deletingLastPathComponent().path + "/../lib/swift"
return path.deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent()
.path
} catch {
return "/usr/lib/swift"
return ""
}
}

let SwiftLibRoot = tryGuessSwiftLibRoot()
let SwiftRoot = tryGuessSwiftRoot()
var FlutterPlatform: String
var FlutterUnsafeLinkerFlags: [String] = []

Expand Down Expand Up @@ -231,10 +232,19 @@ enum FlutterELinuxBackendType {

let FlutterELinuxBackend = FlutterELinuxBackendType.defaultBackend

let CxxIncludeDirs: [String] = [
"\(SwiftRoot)/usr/include",
"\(SwiftRoot)/usr/lib/swift",
"/usr/include/drm",
]

let CxxIncludeFlags = CxxIncludeDirs.flatMap { ["-I", $0] }

platformSwiftSettings += [
.define("DISPLAY_BACKEND_TYPE_\(FlutterELinuxBackend.displayBackendType)"),
.define("FLUTTER_TARGET_BACKEND_\(FlutterELinuxBackend.flutterTargetBackend)"),
.interoperabilityMode(.Cxx),
.unsafeFlags(CxxIncludeFlags),
]

targets += [
Expand Down Expand Up @@ -401,7 +411,7 @@ targets += [
.headerSearchPath("flutter-embedded-linux/src/third_party/rapidjson/include"),
// FIXME: .cxxLanguageStandard breaks Foundation compile
// FIXME: include path for swift/bridging.h
.unsafeFlags(["-pthread", "-I", SwiftLibRoot, "-I", "/usr/include/drm", "-std=c++17"]),
.unsafeFlags(["-pthread", "-std=c++17"] + CxxIncludeFlags),
],
linkerSettings: [
// .unsafeFlags(["-pthread"]),
Expand Down Expand Up @@ -443,6 +453,8 @@ platformCxxSettings += [
.headerSearchPath(
"../CxxFlutterSwift/flutter-embedded-linux/src/flutter/shell/platform/common/client_wrapper/include"
),
.headerSearchPath("../CxxFlutterSwift/flutter-embedded-linux/src/third_party/rapidjson/include"),
.unsafeFlags(CxxIncludeFlags),
]

#else
Expand Down
6 changes: 4 additions & 2 deletions Sources/CxxFlutterSwift/include/CxxFlutterSwift.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright (c) 2023-2024 PADL Software Pty Ltd
// Copyright (c) 2023-2025 PADL Software Pty Ltd
//
// Licensed under the Apache License, Version 2.0 (the License);
// you may not use this file except in compliance with the License.
Expand All @@ -21,8 +21,10 @@

#include <flutter_messenger.h>
#include <flutter_elinux.h>
#include <flutter_elinux_engine.h>
#include <flutter_elinux_state.h>
#include <flutter_elinux_view.h>
#include <flutter_plugin_registrar.h>
// #include <flutter_elinux_state.h>
#include <flutter_platform_views.h>

#ifdef __cplusplus
Expand Down
3 changes: 3 additions & 0 deletions Sources/CxxFlutterSwift/include/module.modulemap
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module CxxFlutterSwift {
header "CxxFlutterSwift.h"
}
2 changes: 1 addition & 1 deletion Sources/FlutterSwift/Channel/FlutterEventChannel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public final class FlutterEventChannel: _FlutterBinaryMessengerConnectionReprese
public let codec: FlutterMessageCodec
public let priority: TaskPriority?

private typealias EventStreamTask = Task<Void, Never>
private typealias EventStreamTask = Task<(), Never>

private let _connection: ManagedAtomic<FlutterBinaryMessengerConnection>
private let tasks: ManagedCriticalState<[String: EventStreamTask]>
Expand Down
64 changes: 45 additions & 19 deletions Sources/FlutterSwift/Client/FlutterEngine.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright (c) 2023-2024 PADL Software Pty Ltd
// Copyright (c) 2023-2025 PADL Software Pty Ltd
//
// Licensed under the Apache License, Version 2.0 (the License);
// you may not use this file except in compliance with the License.
Expand All @@ -17,12 +17,13 @@
#if os(Linux) && canImport(Glibc)
@_implementationOnly
import CxxFlutterSwift
import CxxStdlib

public final class FlutterEngine: FlutterPluginRegistry, @unchecked Sendable {
var engine: FlutterDesktopEngineRef! // strong or weak ref
var pluginPublications = [String: Any]()
let project: DartProject
weak var viewController: FlutterViewController?
private var engine: flutter.FlutterELinuxEngine! // strong or weak ref
private var _binaryMessenger: FlutterDesktopMessenger!
private var ownsEngine = true
private var hasBeenRun = false
Expand All @@ -48,7 +49,8 @@ public final class FlutterEngine: FlutterPluginRegistry, @unchecked Sendable {
)
cStrings.withUnsafeMutableBufferPointer { pointer in
properties.dart_entrypoint_argv = pointer.baseAddress
self.engine = FlutterDesktopEngineCreate(&properties)
let engine = FlutterDesktopEngineCreate(&properties)
self.engine = unsafeBitCast(engine, to: flutter.FlutterELinuxEngine.self)
self._binaryMessenger = FlutterDesktopMessenger(engine: self.engine)
}
}
Expand All @@ -61,6 +63,10 @@ public final class FlutterEngine: FlutterPluginRegistry, @unchecked Sendable {
shutDown()
}

private var _handle: FlutterDesktopEngineRef {
unsafeBitCast(engine, to: FlutterDesktopEngineRef.self)
}

// note we can't use public private(set) because we need the type to be FlutterDesktopMessenger!
// in order for callbacks to work (otherwise self must be first initialized). But we want to
// present a non-optional type to callers.
Expand All @@ -69,39 +75,31 @@ public final class FlutterEngine: FlutterPluginRegistry, @unchecked Sendable {
}

public func run(entryPoint: String? = nil) -> Bool {
if hasBeenRun {
debugPrint("Cannot run an engine more than once.")
return false
}
let runSucceeded = FlutterDesktopEngineRun(engine, entryPoint)
if !runSucceeded {
debugPrint("Failed to start engine.")
}
hasBeenRun = true
return runSucceeded
guard !hasBeenRun else { return false }
hasBeenRun = engine.RunWithEntrypoint(entryPoint)
return hasBeenRun
}

public func shutDown() {
pluginPublications.removeAll()
if let engine, ownsEngine {
FlutterDesktopEngineDestroy(engine)
if engine != nil, ownsEngine {
FlutterDesktopEngineDestroy(_handle)
}
engine = nil
}

public func processMessages() -> UInt64 {
precondition(engine != nil)
return FlutterDesktopEngineProcessMessages(engine)
return FlutterDesktopEngineProcessMessages(_handle)
}

public func reloadSystemFonts() {
precondition(engine != nil)
FlutterDesktopEngineReloadSystemFonts(engine)
engine.ReloadSystemFonts()
}

func relinquishEngine() -> FlutterDesktopEngineRef {
ownsEngine = false
return engine
return _handle
}

public func registrar(for pluginKey: String) -> FlutterPluginRegistrar? {
Expand All @@ -116,5 +114,33 @@ public final class FlutterEngine: FlutterPluginRegistry, @unchecked Sendable {
public func valuePublished(by pluginKey: String) -> Any? {
pluginPublications[pluginKey]
}

public var isRunning: Bool {
engine.running()
}

func onVsync(lastFrameTimeNS: UInt64, vsyncIntervalTimeNS: UInt64) {
engine.OnVsync(lastFrameTimeNS, vsyncIntervalTimeNS)
}

public var isImpellerEnabled: Bool {
engine.IsImpellerEnabled()
}

public func setSystemSettings(textScalingFactor: Float, enableHighContrast: Bool) {
engine.SetSystemSettings(textScalingFactor, enableHighContrast)
}

public func setView(_ view: FlutterView) {
engine.SetView(view.view)
}

func getRegistrar(pluginName: String) -> FlutterDesktopPluginRegistrarRef? {
FlutterDesktopEngineGetPluginRegistrar(_handle, pluginName)
}

var textureRegistrar: FlutterDesktopTextureRegistrarRef {
FlutterDesktopEngineGetTextureRegistrar(_handle)
}
}
#endif
8 changes: 6 additions & 2 deletions Sources/FlutterSwift/Client/FlutterPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public final class FlutterDesktopPluginRegistrar: FlutterPluginRegistrar {
) {
self.engine = engine
pluginKey = pluginName
registrar = FlutterDesktopEngineGetPluginRegistrar(engine.engine, pluginName)
registrar = engine.getRegistrar(pluginName: pluginName)
FlutterDesktopPluginRegistrarSetDestructionHandlerBlock(registrar!) { _ in
self.registrar = nil
}
Expand Down Expand Up @@ -184,13 +184,17 @@ public struct FlutterDesktopTextureRegistrar {
private let registrar: FlutterDesktopTextureRegistrarRef

public init(engine: FlutterEngine) {
registrar = FlutterDesktopEngineGetTextureRegistrar(engine.engine)
registrar = engine.textureRegistrar
}

init?(plugin: FlutterDesktopPluginRegistrar) {
guard let registrar = plugin.registrar else { return nil }
self.registrar = FlutterDesktopRegistrarGetTextureRegistrar(registrar)
}

public func markExternalTextureFrameAvailable(textureID: Int64) {
FlutterDesktopTextureRegistrarMarkExternalTextureFrameAvailable(registrar, textureID)
}
}

#endif
14 changes: 9 additions & 5 deletions Sources/FlutterSwift/Client/FlutterView.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright (c) 2023-2024 PADL Software Pty Ltd
// Copyright (c) 2023-2025 PADL Software Pty Ltd
//
// Licensed under the Apache License, Version 2.0 (the License);
// you may not use this file except in compliance with the License.
Expand All @@ -21,7 +21,7 @@ import CxxFlutterSwift
let kChannelName = "flutter/platform_views"

public struct FlutterView {
let view: FlutterDesktopViewRef
let view: flutter.FlutterELinuxView
var platformViewsPluginRegistrar: FlutterPluginRegistrar?
var platformViewsHandler: FlutterPlatformViewsPlugin?
var viewController: FlutterViewController? {
Expand All @@ -38,16 +38,20 @@ public struct FlutterView {
}
}

init(_ view: FlutterDesktopViewRef) {
init(_ view: flutter.FlutterELinuxView) {
self.view = view
}

init(_ view: FlutterDesktopViewRef) {
self.init(unsafeBitCast(view, to: flutter.FlutterELinuxView.self))
}

public func dispatchEvent() -> Bool {
FlutterDesktopViewDispatchEvent(view)
view.DispatchEvent()
}

public var frameRate: Int32 {
FlutterDesktopViewGetFrameRate(view)
view.GetFrameRate()
}
}
#endif
2 changes: 1 addition & 1 deletion Sources/FlutterSwift/Client/FlutterViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ public final class FlutterViewController {

public var view: FlutterView {
didSet {
FlutterDesktopEngineSetView(engine.engine, view.view)
engine.setView(view)
}
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/FlutterSwift/Client/FlutterWindow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public struct FlutterWindow {

public func run() {
let runLoop = RunLoop.main
self.schedule(in: runLoop, forMode: .common)
schedule(in: runLoop, forMode: .common)
runLoop.run()
}
}
Expand Down
Loading

0 comments on commit 885160b

Please sign in to comment.