Files
xianyan/macos/Runner/AppDelegate.swift
22 7ea4a068a1 feat(macos+flutter): 新增 Impeller 渲染引擎开关与 Intel Mac 渲染兼容修复
本次提交包含以下核心变更:
1. 修复 RawKeyboard 断言错误,添加 HardwareKeyboard 事件处理器
2. 实现 Intel Mac 自动降级玻璃渲染质量,避免黑屏闪烁
3. 新增 macOS 端 Impeller 渲染引擎开关设置,支持动态切换
4. 修复 macOS 双标题栏问题,隐藏系统原生交通灯按钮
5. 更新多语言国际化支持,新增 Impeller 相关翻译
6. 优化 WebRTC 依赖下载,使用国内镜像避免超时
2026-06-25 08:44:00 +08:00

239 lines
9.9 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/// ============================================================
/// APP macOS
/// : 2026-06-02
/// : 2026-06-25
/// : MethodChannelDock //Spotlight /CPU /Impeller /
/// : 1. getImpellerRunningStatus Impeller
/// getImpellerEnabled UserDefaults
/// 2. restartApp 800ms
/// ============================================================
import Cocoa
import FlutterMacOS
import CoreSpotlight
@main
class AppDelegate: FlutterAppDelegate {
// ============================================================
// MARK: -
// ============================================================
/// NSStatusItem
private var statusItem: NSStatusItem?
/// MethodChannelapps.xy.xianyan/macos.app
private var methodChannel: FlutterMethodChannel?
///
private var currentSentence: String = ""
// ============================================================
// MARK: -
// ============================================================
/// channel MainFlutterWindow.awakeFromNib
override func applicationDidFinishLaunching(_ notification: Notification) {
super.applicationDidFinishLaunching(notification)
}
/// MethodChannelapps.xy.xianyan/macos.app
///
/// Flutter Dart Dart getCpuArchitecture
/// MissingPluginException
/// MainFlutterWindow.awakeFromNib FlutterViewController
static func registerAppChannel(controller: FlutterViewController) {
let appDelegate = NSApp.delegate as? AppDelegate
let channel = FlutterMethodChannel(
name: "apps.xy.xianyan/macos.app",
binaryMessenger: controller.engine.binaryMessenger
)
channel.setMethodCallHandler { call, result in
switch call.method {
// ---------- CPU ----------
case "getCpuArchitecture":
let arch = getCpuArchitecture()
result(arch)
// ---------- Impeller ----------
// / Impeller
// Apple Silicon Intel Mac Metal bug
// Impeller UserDefaults
//
case "getImpellerEnabled":
let enabled = getImpellerEnabledWithDefault()
result(enabled)
// Impeller
// MainFlutterWindow.currentImpellerEnabled
// UI ""
case "getImpellerRunningStatus":
let running = MainFlutterWindow.currentImpellerEnabled
result(running)
case "setImpellerEnabled":
let args = call.arguments as? [String: Any]
let enabled = args?["enabled"] as? Bool ?? false
UserDefaults.standard.set(enabled, forKey: "xianyan_enable_impeller")
result(nil)
// ---------- ----------
// Impeller 使
//
case "restartApp":
let url = URL(fileURLWithPath: Bundle.main.bundlePath)
let config = NSWorkspace.OpenConfiguration()
config.activates = true
NSLog("[restartApp] 正在启动新实例: \(url.path)")
NSWorkspace.shared.openApplication(at: url, configuration: config) { newApp, error in
if let error = error {
NSLog("[restartApp] 启动新实例失败: \(error.localizedDescription)")
return
}
NSLog("[restartApp] 新实例已启动,延迟 800ms 后终止当前实例")
// 800ms
//
DispatchQueue.main.asyncAfter(deadline: .now() + 0.8) {
NSLog("[restartApp] 终止当前实例")
NSApp.terminate(nil)
}
}
result(nil)
// ---------- Dock NSDockTile ----------
case "setDockBadge":
let args = call.arguments as? [String: Any]
let count = args?["count"] as? Int ?? 0
NSApp.dockTile.badgeLabel = count > 0 ? "\(count)" : nil
result(nil)
// ---------- NSStatusItem ----------
case "updateStatusBarSentence":
let args = call.arguments as? [String: Any]
let sentence = args?["sentence"] as? String ?? ""
appDelegate?.updateStatusItem(sentence: sentence)
result(nil)
// ---------- Spotlight CoreSpotlight ----------
case "indexSpotlightItems":
let args = call.arguments as? [String: Any]
let items = args?["items"] as? [[String: Any]] ?? []
appDelegate?.indexSpotlightItems(items: items)
result(nil)
case "clearSpotlightIndex":
CSSearchableIndex.default().deleteAllSearchableItems { error in
if let error = error {
result(FlutterError(code: "SPOTLIGHT_ERROR",
message: error.localizedDescription,
details: nil))
} else {
result(nil)
}
}
default:
result(FlutterMethodNotImplemented)
}
}
}
/// 退
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return false
}
override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
return true
}
// ============================================================
// MARK: - NSStatusItem
// ============================================================
/// 30
func updateStatusItem(sentence: String) {
if statusItem == nil {
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
statusItem?.button?.target = self
statusItem?.button?.action = #selector(copySentence(_:))
}
currentSentence = sentence
let display = sentence.count > 30 ? String(sentence.prefix(30)) + "" : sentence
statusItem?.button?.title = "💬 \(display)"
}
///
@objc private func copySentence(_ sender: Any) {
guard !currentSentence.isEmpty else { return }
NSPasteboard.general.clearContents()
NSPasteboard.general.setString(currentSentence, forType: .string)
}
// ============================================================
// MARK: - CPU
// ============================================================
/// CPU Dart Intel Mac
///
/// `uname`
/// - `arm64` Apple Silicon (M1/M2/M3+)
/// - `x86_64` Intel CPU
///
/// Dart MethodChannel
/// Intel Mac
static func getCpuArchitecture() -> String {
var systemInfo = utsname()
uname(&systemInfo)
let machineMirror = Mirror(reflecting: systemInfo.machine)
return machineMirror.children.reduce("") { identifier, element in
guard let value = element.value as? Int8, value != 0 else { return identifier }
return identifier + String(UnicodeScalar(UInt8(value)))
}
}
/// Impeller
///
/// MainFlutterWindow.shouldEnableImpeller()
/// -
/// - Apple Silicon Intel Mac
///
/// Dart UI
static func getImpellerEnabledWithDefault() -> Bool {
let defaults = UserDefaults.standard
if defaults.object(forKey: "xianyan_enable_impeller") != nil {
return defaults.bool(forKey: "xianyan_enable_impeller")
}
// Apple Silicon Intel Mac
return getCpuArchitecture() != "x86_64"
}
// ============================================================
// MARK: - Spotlight CoreSpotlight
// ============================================================
/// Spotlight
func indexSpotlightItems(items: [[String: Any]]) {
let searchableItems = items.map { item in
let attributeSet = CSSearchableItemAttributeSet(itemContentType: "public.text")
attributeSet.title = item["title"] as? String
attributeSet.contentDescription = item["content"] as? String
attributeSet.keywords = [item["type"] as? String ?? "xianyan"]
return CSSearchableItem(
uniqueIdentifier: item["id"] as? String ?? "",
domainIdentifier: "apps.xy.xianyan.\(item["type"] as? String ?? "item")",
attributeSet: attributeSet
)
}
CSSearchableIndex.default().indexSearchableItems(searchableItems) { error in
if let error = error {
print("Spotlight indexing error: \(error)")
}
}
}
}