mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 23:24:32 +00:00
Fix: Brightness and Volume Control
This commit is contained in:
parent
f4bcb96906
commit
98c826aaae
11 changed files with 340 additions and 97 deletions
|
|
@ -10,6 +10,7 @@
|
||||||
7E25ECA12E6C6C3800700E72 /* CoreBrightness.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7E25ECA02E6C6C3800700E72 /* CoreBrightness.framework */; };
|
7E25ECA12E6C6C3800700E72 /* CoreBrightness.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7E25ECA02E6C6C3800700E72 /* CoreBrightness.framework */; };
|
||||||
7E25ECA32E6C6E2600700E72 /* OSD.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7E25ECA22E6C6E2600700E72 /* OSD.framework */; };
|
7E25ECA32E6C6E2600700E72 /* OSD.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7E25ECA22E6C6E2600700E72 /* OSD.framework */; };
|
||||||
7E25ECA52E6C6E7800700E72 /* DisplayServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7E25ECA42E6C6E7800700E72 /* DisplayServices.framework */; };
|
7E25ECA52E6C6E7800700E72 /* DisplayServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7E25ECA42E6C6E7800700E72 /* DisplayServices.framework */; };
|
||||||
|
7E25ECB52E6C8BFB00700E72 /* MediaKeyTap in Frameworks */ = {isa = PBXBuildFile; productRef = 7E25ECB42E6C8BFB00700E72 /* MediaKeyTap */; };
|
||||||
7E6118142E24071E0050006C /* LaunchAtLogin in Frameworks */ = {isa = PBXBuildFile; productRef = 14D032192C68F32E0096E6A1 /* LaunchAtLogin */; };
|
7E6118142E24071E0050006C /* LaunchAtLogin in Frameworks */ = {isa = PBXBuildFile; productRef = 14D032192C68F32E0096E6A1 /* LaunchAtLogin */; };
|
||||||
7E6118152E24071E0050006C /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = 14D0321C2C68F3350096E6A1 /* Sparkle */; };
|
7E6118152E24071E0050006C /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = 14D0321C2C68F3350096E6A1 /* Sparkle */; };
|
||||||
7E6118162E24071E0050006C /* KeyboardShortcuts in Frameworks */ = {isa = PBXBuildFile; productRef = B18654382C6F4990000B926A /* KeyboardShortcuts */; };
|
7E6118162E24071E0050006C /* KeyboardShortcuts in Frameworks */ = {isa = PBXBuildFile; productRef = B18654382C6F4990000B926A /* KeyboardShortcuts */; };
|
||||||
|
|
@ -75,6 +76,7 @@
|
||||||
7E61181A2E24071E0050006C /* SwiftUIIntrospect in Frameworks */,
|
7E61181A2E24071E0050006C /* SwiftUIIntrospect in Frameworks */,
|
||||||
7E25ECA32E6C6E2600700E72 /* OSD.framework in Frameworks */,
|
7E25ECA32E6C6E2600700E72 /* OSD.framework in Frameworks */,
|
||||||
7E25ECA52E6C6E7800700E72 /* DisplayServices.framework in Frameworks */,
|
7E25ECA52E6C6E7800700E72 /* DisplayServices.framework in Frameworks */,
|
||||||
|
7E25ECB52E6C8BFB00700E72 /* MediaKeyTap in Frameworks */,
|
||||||
7E6118192E24071E0050006C /* Defaults in Frameworks */,
|
7E6118192E24071E0050006C /* Defaults in Frameworks */,
|
||||||
7E6118182E24071E0050006C /* TheBoringWorkerNotifier in Frameworks */,
|
7E6118182E24071E0050006C /* TheBoringWorkerNotifier in Frameworks */,
|
||||||
7E6118172E24071E0050006C /* Collections in Frameworks */,
|
7E6118172E24071E0050006C /* Collections in Frameworks */,
|
||||||
|
|
@ -157,6 +159,7 @@
|
||||||
B1628B912CC260C0003D8DF3 /* SwiftUIIntrospect */,
|
B1628B912CC260C0003D8DF3 /* SwiftUIIntrospect */,
|
||||||
B194240A2CD10017003E5DC2 /* LottieUI */,
|
B194240A2CD10017003E5DC2 /* LottieUI */,
|
||||||
146BC4362D366E9B0047C66A /* MacroVisionKit */,
|
146BC4362D366E9B0047C66A /* MacroVisionKit */,
|
||||||
|
7E25ECB42E6C8BFB00700E72 /* MediaKeyTap */,
|
||||||
);
|
);
|
||||||
productName = DynamicIsland;
|
productName = DynamicIsland;
|
||||||
productReference = F54406E72CD4987A00C7AFD9 /* DynamicIsland.app */;
|
productReference = F54406E72CD4987A00C7AFD9 /* DynamicIsland.app */;
|
||||||
|
|
@ -197,6 +200,7 @@
|
||||||
B1628B902CC260C0003D8DF3 /* XCRemoteSwiftPackageReference "swiftui-introspect" */,
|
B1628B902CC260C0003D8DF3 /* XCRemoteSwiftPackageReference "swiftui-introspect" */,
|
||||||
B19424072CD0DB52003E5DC2 /* XCRemoteSwiftPackageReference "LottieUI" */,
|
B19424072CD0DB52003E5DC2 /* XCRemoteSwiftPackageReference "LottieUI" */,
|
||||||
146BC4352D366E9B0047C66A /* XCRemoteSwiftPackageReference "MacroVisionKit" */,
|
146BC4352D366E9B0047C66A /* XCRemoteSwiftPackageReference "MacroVisionKit" */,
|
||||||
|
7E25ECB32E6C8BFB00700E72 /* XCRemoteSwiftPackageReference "MediaKeyTap" */,
|
||||||
);
|
);
|
||||||
preferredProjectObjectVersion = 77;
|
preferredProjectObjectVersion = 77;
|
||||||
productRefGroup = F54406E82CD4987A00C7AFD9 /* Products */;
|
productRefGroup = F54406E82CD4987A00C7AFD9 /* Products */;
|
||||||
|
|
@ -487,6 +491,14 @@
|
||||||
minimumVersion = 2.6.4;
|
minimumVersion = 2.6.4;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
7E25ECB32E6C8BFB00700E72 /* XCRemoteSwiftPackageReference "MediaKeyTap" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/nhurden/MediaKeyTap";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMajorVersion;
|
||||||
|
minimumVersion = 2.3.0;
|
||||||
|
};
|
||||||
|
};
|
||||||
9A987A112C73CAA1005CA465 /* XCRemoteSwiftPackageReference "Pow" */ = {
|
9A987A112C73CAA1005CA465 /* XCRemoteSwiftPackageReference "Pow" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/EmergeTools/Pow";
|
repositoryURL = "https://github.com/EmergeTools/Pow";
|
||||||
|
|
@ -558,6 +570,11 @@
|
||||||
package = 14D0321B2C68F3350096E6A1 /* XCRemoteSwiftPackageReference "Sparkle" */;
|
package = 14D0321B2C68F3350096E6A1 /* XCRemoteSwiftPackageReference "Sparkle" */;
|
||||||
productName = Sparkle;
|
productName = Sparkle;
|
||||||
};
|
};
|
||||||
|
7E25ECB42E6C8BFB00700E72 /* MediaKeyTap */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = 7E25ECB32E6C8BFB00700E72 /* XCRemoteSwiftPackageReference "MediaKeyTap" */;
|
||||||
|
productName = MediaKeyTap;
|
||||||
|
};
|
||||||
9A987A0F2C73CA8D005CA465 /* Collections */ = {
|
9A987A0F2C73CA8D005CA465 /* Collections */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = F54407082CD4A23F00C7AFD9 /* XCRemoteSwiftPackageReference "swift-collections" */;
|
package = F54407082CD4A23F00C7AFD9 /* XCRemoteSwiftPackageReference "swift-collections" */;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"originHash" : "2741287b65cf0758abd89baadf314f5f8cbfcb46cb8d236a83f98731e5501ee3",
|
"originHash" : "424f1d62134dd1f9036d13269149d3ce62d9433217dddecb8fb77a10d8189a85",
|
||||||
"pins" : [
|
"pins" : [
|
||||||
{
|
{
|
||||||
"identity" : "defaults",
|
"identity" : "defaults",
|
||||||
|
|
@ -55,6 +55,15 @@
|
||||||
"version" : "0.1.0"
|
"version" : "0.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"identity" : "mediakeytap",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/nhurden/MediaKeyTap",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "7d1b8169d228ef6e5ec7db676a348f2f3e50c734",
|
||||||
|
"version" : "2.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"identity" : "pow",
|
"identity" : "pow",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
|
|
|
||||||
|
|
@ -193,6 +193,16 @@ struct ContentView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.onChange(of: coordinator.sneakPeek.show) { _, sneakPeekShowing in
|
||||||
|
// When sneak peek finishes, check if user is still hovering and open notch if needed
|
||||||
|
if !sneakPeekShowing {
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
|
||||||
|
if isHovering && vm.notchState == .closed {
|
||||||
|
doOpen()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
.sensoryFeedback(.alignment, trigger: haptics)
|
.sensoryFeedback(.alignment, trigger: haptics)
|
||||||
.contextMenu {
|
.contextMenu {
|
||||||
SettingsLink(label: {
|
SettingsLink(label: {
|
||||||
|
|
@ -507,15 +517,23 @@ struct ContentView: View {
|
||||||
haptics.toggle()
|
haptics.toggle()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't open notch if there's a sneak peek showing
|
|
||||||
if coordinator.sneakPeek.show {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delay opening the notch
|
// Delay opening the notch
|
||||||
let task = DispatchWorkItem {
|
let task = DispatchWorkItem {
|
||||||
// ContentView is a struct, so we don't use weak self here
|
// ContentView is a struct, so we don't use weak self here
|
||||||
guard vm.notchState == .closed, isHovering else { return }
|
guard vm.notchState == .closed, isHovering else { return }
|
||||||
|
|
||||||
|
// Additional check: don't open if a sneak peek just started showing
|
||||||
|
// to avoid conflicts during system HUD display
|
||||||
|
if coordinator.sneakPeek.show {
|
||||||
|
// Schedule a retry after the sneak peek should be gone (default duration + small buffer)
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1.8) {
|
||||||
|
if isHovering && vm.notchState == .closed && !coordinator.sneakPeek.show {
|
||||||
|
doOpen()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
doOpen()
|
doOpen()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -87,8 +87,21 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
private var onboardingWindowController: NSWindowController?
|
private var onboardingWindowController: NSWindowController?
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
|
// Media key monitoring
|
||||||
|
private var globalEventMonitor: Any?
|
||||||
|
private var localEventMonitor: Any?
|
||||||
|
|
||||||
// Debouncing mechanism for window size updates
|
// Debouncing mechanism for window size updates
|
||||||
private var windowSizeUpdateWorkItem: DispatchWorkItem?
|
private var windowSizeUpdateWorkItem: DispatchWorkItem?
|
||||||
|
// let calendarManager = CalendarManager.shared
|
||||||
|
// let webcamManager = WebcamManager.shared
|
||||||
|
// var closeNotchWorkItem: DispatchWorkItem?
|
||||||
|
// private var previousScreens: [NSScreen]?
|
||||||
|
// private var onboardingWindowController: NSWindowController?
|
||||||
|
// private var cancellables = Set<AnyCancellable>()
|
||||||
|
//
|
||||||
|
// // Debouncing mechanism for window size updates
|
||||||
|
// private var windowSizeUpdateWorkItem: DispatchWorkItem?
|
||||||
|
|
||||||
private func debouncedUpdateWindowSize() {
|
private func debouncedUpdateWindowSize() {
|
||||||
// Cancel any existing work item
|
// Cancel any existing work item
|
||||||
|
|
@ -108,10 +121,76 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Media Key Monitoring
|
||||||
|
|
||||||
|
private func setupMediaKeyMonitoring() {
|
||||||
|
// Monitor both global and local events to catch media keys
|
||||||
|
globalEventMonitor = NSEvent.addGlobalMonitorForEvents(matching: .systemDefined) { [weak self] event in
|
||||||
|
self?.handleSystemEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
localEventMonitor = NSEvent.addLocalMonitorForEvents(matching: .systemDefined) { [weak self] event in
|
||||||
|
self?.handleSystemEvent(event)
|
||||||
|
return event
|
||||||
|
}
|
||||||
|
|
||||||
|
print("🎹 Media key monitoring started using NSEvent")
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleSystemEvent(_ event: NSEvent) {
|
||||||
|
guard event.subtype.rawValue == 8 else { return }
|
||||||
|
|
||||||
|
let data1 = event.data1
|
||||||
|
let keyCode = Int32((data1 & 0xFFFF0000) >> 16)
|
||||||
|
let keyFlags = Int(data1 & 0x0000FFFF)
|
||||||
|
let keyState = ((keyFlags & 0xFF00) >> 8) == 0xA // KeyDown
|
||||||
|
let keyRepeat = (keyFlags & 0x1) != 0
|
||||||
|
|
||||||
|
guard keyState && !keyRepeat else { return } // Only handle key down, ignore repeats
|
||||||
|
|
||||||
|
switch keyCode {
|
||||||
|
case NX_KEYTYPE_SOUND_UP, NX_KEYTYPE_SOUND_DOWN, NX_KEYTYPE_MUTE:
|
||||||
|
print("🔊 Volume key detected: \(keyCode)")
|
||||||
|
SystemHUDManager.shared.handleVolumeKeyEvent()
|
||||||
|
case NX_KEYTYPE_BRIGHTNESS_UP, NX_KEYTYPE_BRIGHTNESS_DOWN:
|
||||||
|
print("☀️ Brightness key detected: \(keyCode)")
|
||||||
|
SystemHUDManager.shared.handleBrightnessKeyEvent()
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func stopMediaKeyMonitoring() {
|
||||||
|
if let globalMonitor = globalEventMonitor {
|
||||||
|
NSEvent.removeMonitor(globalMonitor)
|
||||||
|
globalEventMonitor = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if let localMonitor = localEventMonitor {
|
||||||
|
NSEvent.removeMonitor(localMonitor)
|
||||||
|
localEventMonitor = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
print("🎹 Media key monitoring stopped")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - MediaKeyApplicationDelegate (for future use)
|
||||||
|
|
||||||
|
func didDetectVolumeKey() {
|
||||||
|
SystemHUDManager.shared.handleVolumeKeyEvent()
|
||||||
|
}
|
||||||
|
|
||||||
|
func didDetectBrightnessKey() {
|
||||||
|
SystemHUDManager.shared.handleBrightnessKeyEvent()
|
||||||
|
}
|
||||||
|
|
||||||
func applicationWillTerminate(_ notification: Notification) {
|
func applicationWillTerminate(_ notification: Notification) {
|
||||||
// Cancel any pending window size updates
|
// Cancel any pending window size updates
|
||||||
windowSizeUpdateWorkItem?.cancel()
|
windowSizeUpdateWorkItem?.cancel()
|
||||||
|
|
||||||
|
// Stop media key monitoring
|
||||||
|
stopMediaKeyMonitoring()
|
||||||
|
|
||||||
print("🚪 Application terminating - ensuring system HUD is restored...")
|
print("🚪 Application terminating - ensuring system HUD is restored...")
|
||||||
|
|
||||||
// Re-enable system HUD on app termination
|
// Re-enable system HUD on app termination
|
||||||
|
|
@ -302,6 +381,9 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
// Setup SystemHUD Manager
|
// Setup SystemHUD Manager
|
||||||
SystemHUDManager.shared.setup(coordinator: coordinator)
|
SystemHUDManager.shared.setup(coordinator: coordinator)
|
||||||
|
|
||||||
|
// Setup media key monitoring
|
||||||
|
setupMediaKeyMonitoring()
|
||||||
|
|
||||||
// Observe tab changes - use debounced updates
|
// Observe tab changes - use debounced updates
|
||||||
coordinator.$currentView.sink { [weak self] newView in
|
coordinator.$currentView.sink { [weak self] newView in
|
||||||
self?.debouncedUpdateWindowSize()
|
self?.debouncedUpdateWindowSize()
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ struct ExpandedItem {
|
||||||
|
|
||||||
class DynamicIslandViewCoordinator: ObservableObject {
|
class DynamicIslandViewCoordinator: ObservableObject {
|
||||||
static let shared = DynamicIslandViewCoordinator()
|
static let shared = DynamicIslandViewCoordinator()
|
||||||
var notifier: TheBoringWorkerNotifier = .init()
|
var notifier: TheBoringWorkerNotifier
|
||||||
|
|
||||||
@Published var currentView: NotchViews = .home
|
@Published var currentView: NotchViews = .home
|
||||||
|
|
||||||
|
|
@ -97,8 +97,8 @@ class DynamicIslandViewCoordinator: ObservableObject {
|
||||||
@Published var optionKeyPressed: Bool = true
|
@Published var optionKeyPressed: Bool = true
|
||||||
|
|
||||||
private init() {
|
private init() {
|
||||||
selectedScreen = preferredScreen
|
|
||||||
notifier = TheBoringWorkerNotifier()
|
notifier = TheBoringWorkerNotifier()
|
||||||
|
selectedScreen = preferredScreen
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupWorkersNotificationObservers() {
|
func setupWorkersNotificationObservers() {
|
||||||
|
|
@ -171,7 +171,8 @@ class DynamicIslandViewCoordinator: ObservableObject {
|
||||||
guard let self = self, !Task.isCancelled else { return }
|
guard let self = self, !Task.isCancelled else { return }
|
||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
withAnimation {
|
withAnimation {
|
||||||
self.toggleSneakPeek(status: false, type: .music)
|
// Hide the sneak peek with the correct type that was showing
|
||||||
|
self.toggleSneakPeek(status: false, type: self.sneakPeek.type)
|
||||||
self.sneakPeekDuration = 1.5
|
self.sneakPeekDuration = 1.5
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -72,23 +72,30 @@ class SettingsWindowController: NSWindowController {
|
||||||
|
|
||||||
// If window is already visible, bring it to front properly
|
// If window is already visible, bring it to front properly
|
||||||
if window?.isVisible == true {
|
if window?.isVisible == true {
|
||||||
NSApp.activate(ignoringOtherApps: true)
|
forceWindowToFront()
|
||||||
window?.orderFrontRegardless()
|
|
||||||
window?.makeKeyAndOrderFront(nil)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show the window with proper ordering
|
// Show the window with proper ordering
|
||||||
|
window?.center()
|
||||||
|
forceWindowToFront()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func forceWindowToFront() {
|
||||||
|
// Multi-step approach to ensure window gets focus
|
||||||
window?.orderFrontRegardless()
|
window?.orderFrontRegardless()
|
||||||
window?.makeKeyAndOrderFront(nil)
|
window?.makeKeyAndOrderFront(nil)
|
||||||
window?.center()
|
|
||||||
|
|
||||||
// Activate the app and ensure window gets focus
|
// Activate the app with maximum priority
|
||||||
NSApp.activate(ignoringOtherApps: true)
|
NSApp.activate(ignoringOtherApps: true)
|
||||||
|
|
||||||
// Force window to front after activation
|
// Double-check after a brief delay
|
||||||
DispatchQueue.main.async { [weak self] in
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
|
||||||
self?.window?.makeKeyAndOrderFront(nil)
|
guard let window = self?.window else { return }
|
||||||
|
if !window.isKeyWindow {
|
||||||
|
window.makeKeyAndOrderFront(nil)
|
||||||
|
NSApp.activate(ignoringOtherApps: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,10 @@ class DynamicIslandExtensionManager: ObservableObject {
|
||||||
|
|
||||||
@Published var installedExtensions: [Extension] = [] {
|
@Published var installedExtensions: [Extension] = [] {
|
||||||
didSet {
|
didSet {
|
||||||
print("Extensions installed: \(installedExtensions)")
|
// Only log when there's an actual change in content, not just initialization
|
||||||
|
if oldValue != installedExtensions {
|
||||||
|
print("📦 Extensions installed: \(installedExtensions.map { $0.name })")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,82 +11,102 @@ import Foundation
|
||||||
import Defaults
|
import Defaults
|
||||||
|
|
||||||
class SystemChangesObserver {
|
class SystemChangesObserver {
|
||||||
private var oldVolume: Float
|
private var oldVolume: Float = 0
|
||||||
private var oldMuted: Bool
|
private var oldMuted: Bool = false
|
||||||
private var oldBrightness: Float = 0
|
private var oldBrightness: Float = 0
|
||||||
|
|
||||||
private weak var coordinator: DynamicIslandViewCoordinator?
|
private weak var coordinator: DynamicIslandViewCoordinator?
|
||||||
private var timer: Timer?
|
|
||||||
|
// Backup timer for edge cases only
|
||||||
|
private var backupTimer: Timer?
|
||||||
|
|
||||||
init(coordinator: DynamicIslandViewCoordinator) {
|
init(coordinator: DynamicIslandViewCoordinator) {
|
||||||
self.coordinator = coordinator
|
self.coordinator = coordinator
|
||||||
|
|
||||||
|
// Initialize baseline values only once
|
||||||
oldVolume = SystemVolumeManager.getOutputVolume()
|
oldVolume = SystemVolumeManager.getOutputVolume()
|
||||||
oldMuted = SystemVolumeManager.isMuted()
|
oldMuted = SystemVolumeManager.isMuted()
|
||||||
|
|
||||||
do {
|
do {
|
||||||
oldBrightness = try SystemDisplayManager.getDisplayBrightness()
|
oldBrightness = try SystemDisplayManager.getDisplayBrightness()
|
||||||
} catch {
|
} catch {
|
||||||
NSLog("Failed to retrieve display brightness: \(error)")
|
NSLog("Failed to retrieve initial display brightness: \(error)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func startObserving() {
|
func startObserving() {
|
||||||
guard Defaults[.enableSystemHUD] else { return }
|
guard Defaults[.enableSystemHUD] else { return }
|
||||||
|
|
||||||
createObservers()
|
startMinimalBackupTimer()
|
||||||
createTimerForContinuousChangesCheck(with: 0.2)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func createTimerForContinuousChangesCheck(with seconds: TimeInterval) {
|
|
||||||
timer = Timer(
|
|
||||||
timeInterval: seconds, target: self, selector: #selector(checkChanges), userInfo: nil,
|
|
||||||
repeats: true)
|
|
||||||
let mainLoop = RunLoop.main
|
|
||||||
mainLoop.add(timer!, forMode: .common)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func createObservers() {
|
|
||||||
NotificationCenter.default.addObserver(
|
|
||||||
self,
|
|
||||||
selector: #selector(showVolumeHUD),
|
|
||||||
name: SystemKeyObserver.volumeChanged,
|
|
||||||
object: nil)
|
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(
|
|
||||||
self,
|
|
||||||
selector: #selector(showBrightnessHUD),
|
|
||||||
name: SystemKeyObserver.brightnessChanged,
|
|
||||||
object: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func showVolumeHUD() {
|
|
||||||
guard Defaults[.enableSystemHUD] && Defaults[.enableVolumeHUD] else { return }
|
|
||||||
|
|
||||||
if oldVolume == 0.0 || oldVolume == 1.0 {
|
|
||||||
sendVolumeNotification(value: oldVolume)
|
|
||||||
}
|
|
||||||
print("Showing volume HUD")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func showBrightnessHUD() {
|
private func startMinimalBackupTimer() {
|
||||||
guard Defaults[.enableSystemHUD] && Defaults[.enableBrightnessHUD] else { return }
|
// More frequent backup check (every 300ms) for better responsiveness
|
||||||
|
// This is a reasonable compromise between the original 200ms and the previous 5s
|
||||||
if oldBrightness == 0.0 || oldBrightness == 1.0 {
|
backupTimer = Timer.scheduledTimer(withTimeInterval: 0.3, repeats: true) { [weak self] _ in
|
||||||
sendBrightnessNotification(value: oldBrightness)
|
guard Defaults[.enableSystemHUD] else { return }
|
||||||
|
self?.performBackupCheck()
|
||||||
}
|
}
|
||||||
print("Showing brightness HUD")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func checkChanges() {
|
private func performBackupCheck() {
|
||||||
guard Defaults[.enableSystemHUD] else { return }
|
// Only check if we haven't had recent activity
|
||||||
|
|
||||||
checkBrightnessChanges()
|
|
||||||
checkVolumeChanges()
|
checkVolumeChanges()
|
||||||
|
checkBrightnessChanges()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func isAlmost(firstNumber: Float, secondNumber: Float) -> Bool {
|
// MARK: - Public Media Key Event Handlers (called from MediaKeyApplication)
|
||||||
let marginValue = 5 / 100.0
|
|
||||||
return (firstNumber + Float(marginValue) >= secondNumber && firstNumber - Float(marginValue) <= secondNumber)
|
func handleVolumeKeyEvent() {
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
|
||||||
|
self.handleVolumeKeyPress()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleBrightnessKeyEvent() {
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
|
||||||
|
self.handleBrightnessKeyPress()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Key Press Event Handlers
|
||||||
|
|
||||||
|
private func handleVolumeKeyPress() {
|
||||||
|
guard Defaults[.enableVolumeHUD] else { return }
|
||||||
|
|
||||||
|
let newVolume = SystemVolumeManager.getOutputVolume()
|
||||||
|
let newMuted = SystemVolumeManager.isMuted()
|
||||||
|
|
||||||
|
// Always show HUD for key press feedback, even if value doesn't change
|
||||||
|
print("🔊 Volume key pressed - showing HUD: volume=\(newVolume), muted=\(newMuted)")
|
||||||
|
|
||||||
|
if newMuted {
|
||||||
|
sendVolumeNotification(value: 0.0)
|
||||||
|
} else {
|
||||||
|
sendVolumeNotification(value: newVolume)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update stored values
|
||||||
|
oldVolume = newVolume
|
||||||
|
oldMuted = newMuted
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleBrightnessKeyPress() {
|
||||||
|
guard Defaults[.enableBrightnessHUD] else { return }
|
||||||
|
guard NSScreen.screens.count > 0 else { return }
|
||||||
|
|
||||||
|
do {
|
||||||
|
let newBrightness = try SystemDisplayManager.getDisplayBrightness()
|
||||||
|
|
||||||
|
// Always show HUD for key press feedback, even if value doesn't change
|
||||||
|
print("☀️ Brightness key pressed - showing HUD: brightness=\(newBrightness)")
|
||||||
|
sendBrightnessNotification(value: newBrightness)
|
||||||
|
|
||||||
|
// Update stored value
|
||||||
|
oldBrightness = newBrightness
|
||||||
|
} catch {
|
||||||
|
// Silently ignore brightness errors to reduce log spam
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func checkVolumeChanges() {
|
private func checkVolumeChanges() {
|
||||||
|
|
@ -95,7 +115,12 @@ class SystemChangesObserver {
|
||||||
let newVolume = SystemVolumeManager.getOutputVolume()
|
let newVolume = SystemVolumeManager.getOutputVolume()
|
||||||
let newMuted = SystemVolumeManager.isMuted()
|
let newMuted = SystemVolumeManager.isMuted()
|
||||||
|
|
||||||
if !isAlmost(firstNumber: oldVolume, secondNumber: newVolume) || newMuted != oldMuted {
|
let volumeChanged = !isAlmost(firstNumber: oldVolume, secondNumber: newVolume)
|
||||||
|
let muteChanged = newMuted != oldMuted
|
||||||
|
|
||||||
|
if volumeChanged || muteChanged {
|
||||||
|
print("🔊 Volume change detected: \(oldVolume) → \(newVolume), muted: \(oldMuted) → \(newMuted)")
|
||||||
|
|
||||||
if newMuted {
|
if newMuted {
|
||||||
sendVolumeNotification(value: 0.0)
|
sendVolumeNotification(value: 0.0)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -108,22 +133,30 @@ class SystemChangesObserver {
|
||||||
|
|
||||||
private func checkBrightnessChanges() {
|
private func checkBrightnessChanges() {
|
||||||
guard Defaults[.enableBrightnessHUD] else { return }
|
guard Defaults[.enableBrightnessHUD] else { return }
|
||||||
|
guard NSScreen.screens.count > 0 else { return }
|
||||||
if NSScreen.screens.count == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let newBrightness = try SystemDisplayManager.getDisplayBrightness()
|
let newBrightness = try SystemDisplayManager.getDisplayBrightness()
|
||||||
if !isAlmost(firstNumber: oldBrightness, secondNumber: newBrightness) {
|
let brightnessChanged = !isAlmost(firstNumber: oldBrightness, secondNumber: newBrightness)
|
||||||
|
|
||||||
|
if brightnessChanged {
|
||||||
|
print("☀️ Brightness change detected: \(oldBrightness) → \(newBrightness)")
|
||||||
sendBrightnessNotification(value: newBrightness)
|
sendBrightnessNotification(value: newBrightness)
|
||||||
oldBrightness = newBrightness
|
oldBrightness = newBrightness
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
NSLog("Failed to retrieve display brightness: \(error)")
|
// Silently ignore brightness errors to reduce log spam
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func isAlmost(firstNumber: Float, secondNumber: Float) -> Bool {
|
||||||
|
// Convert sensitivity setting to a more responsive threshold
|
||||||
|
// Sensitivity 1-10 scale maps to 0.01-0.10 threshold range
|
||||||
|
let marginValue = Float(Defaults[.systemHUDSensitivity]) / 100.0
|
||||||
|
let threshold = max(0.01, marginValue) // Minimum threshold for responsiveness
|
||||||
|
return abs(firstNumber - secondNumber) <= threshold
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Internal Notification Methods
|
// MARK: - Internal Notification Methods
|
||||||
|
|
||||||
private func sendVolumeNotification(value: Float) {
|
private func sendVolumeNotification(value: Float) {
|
||||||
|
|
@ -149,8 +182,12 @@ class SystemChangesObserver {
|
||||||
}
|
}
|
||||||
|
|
||||||
func stopObserving() {
|
func stopObserving() {
|
||||||
timer?.invalidate()
|
// Clean up backup timer
|
||||||
timer = nil
|
backupTimer?.invalidate()
|
||||||
NotificationCenter.default.removeObserver(self)
|
backupTimer = nil
|
||||||
|
|
||||||
|
print("🎹 Media key detection stopped")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ class SystemHUDManager {
|
||||||
|
|
||||||
private var changesObserver: SystemChangesObserver?
|
private var changesObserver: SystemChangesObserver?
|
||||||
private weak var coordinator: DynamicIslandViewCoordinator?
|
private weak var coordinator: DynamicIslandViewCoordinator?
|
||||||
|
private var isSetupComplete = false
|
||||||
|
private var isSystemOperationInProgress = false
|
||||||
|
|
||||||
private init() {
|
private init() {
|
||||||
// Set up observer for settings changes
|
// Set up observer for settings changes
|
||||||
|
|
@ -23,11 +25,14 @@ class SystemHUDManager {
|
||||||
private func setupSettingsObserver() {
|
private func setupSettingsObserver() {
|
||||||
// Use Defaults publisher instead of @Default property wrapper
|
// Use Defaults publisher instead of @Default property wrapper
|
||||||
Defaults.publisher(.enableSystemHUD, options: []).sink { [weak self] change in
|
Defaults.publisher(.enableSystemHUD, options: []).sink { [weak self] change in
|
||||||
|
guard let self = self, self.isSetupComplete else {
|
||||||
|
return
|
||||||
|
}
|
||||||
Task { @MainActor in
|
Task { @MainActor in
|
||||||
if change.newValue {
|
if change.newValue {
|
||||||
await self?.startSystemHUD()
|
await self.startSystemHUD()
|
||||||
} else {
|
} else {
|
||||||
await self?.stopSystemHUD()
|
await self.stopSystemHUD()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.store(in: &cancellables)
|
}.store(in: &cancellables)
|
||||||
|
|
@ -35,34 +40,46 @@ class SystemHUDManager {
|
||||||
|
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
|
/// Public property to check if system operations are in progress
|
||||||
|
var isOperationInProgress: Bool {
|
||||||
|
return isSystemOperationInProgress
|
||||||
|
}
|
||||||
|
|
||||||
func setup(coordinator: DynamicIslandViewCoordinator) {
|
func setup(coordinator: DynamicIslandViewCoordinator) {
|
||||||
self.coordinator = coordinator
|
self.coordinator = coordinator
|
||||||
|
|
||||||
if Defaults[.enableSystemHUD] {
|
if Defaults[.enableSystemHUD] {
|
||||||
Task { @MainActor in
|
Task { @MainActor in
|
||||||
await startSystemHUD()
|
await startSystemHUD()
|
||||||
|
self.isSetupComplete = true
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
isSetupComplete = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
private func startSystemHUD() async {
|
private func startSystemHUD() async {
|
||||||
guard let coordinator = coordinator else { return }
|
guard let coordinator = coordinator, !isSystemOperationInProgress else { return }
|
||||||
|
|
||||||
|
isSystemOperationInProgress = true
|
||||||
await stopSystemHUD() // Stop any existing observer
|
await stopSystemHUD() // Stop any existing observer
|
||||||
|
|
||||||
changesObserver = SystemChangesObserver(coordinator: coordinator)
|
changesObserver = SystemChangesObserver(coordinator: coordinator)
|
||||||
changesObserver?.startObserving()
|
changesObserver?.startObserving()
|
||||||
|
|
||||||
// Disable system HUD if possible
|
// Disable system HUD if possible
|
||||||
// Note: This would require additional implementation similar to OSDUIManager
|
|
||||||
disableSystemHUD()
|
disableSystemHUD()
|
||||||
|
|
||||||
print("System HUD replacement started")
|
print("System HUD replacement started")
|
||||||
|
isSystemOperationInProgress = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
private func stopSystemHUD() async {
|
private func stopSystemHUD() async {
|
||||||
|
guard !isSystemOperationInProgress else { return }
|
||||||
|
|
||||||
|
isSystemOperationInProgress = true
|
||||||
changesObserver?.stopObserving()
|
changesObserver?.stopObserving()
|
||||||
changesObserver = nil
|
changesObserver = nil
|
||||||
|
|
||||||
|
|
@ -70,6 +87,7 @@ class SystemHUDManager {
|
||||||
enableOriginalSystemHUD()
|
enableOriginalSystemHUD()
|
||||||
|
|
||||||
print("System HUD replacement stopped")
|
print("System HUD replacement stopped")
|
||||||
|
isSystemOperationInProgress = false
|
||||||
}
|
}
|
||||||
|
|
||||||
private func disableSystemHUD() {
|
private func disableSystemHUD() {
|
||||||
|
|
@ -83,21 +101,32 @@ class SystemHUDManager {
|
||||||
// Re-enable the system HUD using SystemOSDManager
|
// Re-enable the system HUD using SystemOSDManager
|
||||||
SystemOSDManager.enableSystemHUD()
|
SystemOSDManager.enableSystemHUD()
|
||||||
|
|
||||||
// Verify it's working after a short delay
|
// Check status after a reasonable delay, using async to avoid blocking
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
|
Task {
|
||||||
let isRunning = SystemOSDManager.isOSDUIHelperRunning()
|
try await Task.sleep(nanoseconds: 2_000_000_000) // 2 seconds instead of 1
|
||||||
|
let isRunning = await SystemOSDManager.isOSDUIHelperRunningAsync()
|
||||||
print("🔍 OSDUIHelper status after re-enable: \(isRunning ? "✅ Running" : "❌ Not running")")
|
print("🔍 OSDUIHelper status after re-enable: \(isRunning ? "✅ Running" : "❌ Not running")")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Media Key Event Handling
|
||||||
|
|
||||||
|
/// Handle volume key press events from MediaKeyApplication
|
||||||
|
public func handleVolumeKeyEvent() {
|
||||||
|
changesObserver?.handleVolumeKeyEvent()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle brightness key press events from MediaKeyApplication
|
||||||
|
public func handleBrightnessKeyEvent() {
|
||||||
|
changesObserver?.handleBrightnessKeyEvent()
|
||||||
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
print("🧹 SystemHUDManager deinitializing - ensuring system HUD is restored")
|
|
||||||
cancellables.removeAll()
|
cancellables.removeAll()
|
||||||
Task { @MainActor in
|
Task { @MainActor in
|
||||||
await stopSystemHUD()
|
await stopSystemHUD()
|
||||||
}
|
}
|
||||||
// Also call the emergency restore as backup
|
// Emergency restore is available via SystemHUDManager.emergencyRestoreSystemHUD() if needed
|
||||||
SystemHUDDebugger.forceRestartOSDUIHelper()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Emergency restore function - call this if system HUD is not working
|
/// Emergency restore function - call this if system HUD is not working
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,10 @@
|
||||||
// Adapted from TheBoringWorker-HUD KeyPressObserver.swift
|
// Adapted from TheBoringWorker-HUD KeyPressObserver.swift
|
||||||
// Created by GitHub Copilot on 06/09/25.
|
// Created by GitHub Copilot on 06/09/25.
|
||||||
//
|
//
|
||||||
|
// ⚠️ DEPRECATED: This class is non-functional as it extends NSApplication
|
||||||
|
// but is never used as the main app class. Event monitoring has been moved
|
||||||
|
// to SystemChangesObserver using NSEvent.addGlobalMonitorForEvents.
|
||||||
|
//
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,12 @@ class SystemOSDManager {
|
||||||
|
|
||||||
/// Re-enables the system HUD by restarting OSDUIHelper
|
/// Re-enables the system HUD by restarting OSDUIHelper
|
||||||
public static func enableSystemHUD() {
|
public static func enableSystemHUD() {
|
||||||
|
Task.detached(priority: .background) {
|
||||||
|
await enableSystemHUDAsync()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func enableSystemHUDAsync() async {
|
||||||
do {
|
do {
|
||||||
// First, stop any existing OSDUIHelper process
|
// First, stop any existing OSDUIHelper process
|
||||||
let stopTask = Process()
|
let stopTask = Process()
|
||||||
|
|
@ -22,7 +28,7 @@ class SystemOSDManager {
|
||||||
stopTask.waitUntilExit()
|
stopTask.waitUntilExit()
|
||||||
|
|
||||||
// Small delay to ensure process is fully stopped
|
// Small delay to ensure process is fully stopped
|
||||||
usleep(200000) // 200ms
|
try await Task.sleep(nanoseconds: 200_000_000) // 200ms
|
||||||
|
|
||||||
// Then kickstart it again to ensure it's running properly
|
// Then kickstart it again to ensure it's running properly
|
||||||
let kickstart = Process()
|
let kickstart = Process()
|
||||||
|
|
@ -32,11 +38,15 @@ class SystemOSDManager {
|
||||||
kickstart.waitUntilExit()
|
kickstart.waitUntilExit()
|
||||||
|
|
||||||
// Additional delay to ensure service is fully started
|
// Additional delay to ensure service is fully started
|
||||||
usleep(300000) // 300ms
|
try await Task.sleep(nanoseconds: 300_000_000) // 300ms
|
||||||
|
|
||||||
print("✅ System HUD re-enabled")
|
await MainActor.run {
|
||||||
|
print("✅ System HUD re-enabled")
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
NSLog("❌ Error while trying to re-enable OSDUIHelper: \(error)")
|
await MainActor.run {
|
||||||
|
NSLog("❌ Error while trying to re-enable OSDUIHelper: \(error)")
|
||||||
|
}
|
||||||
|
|
||||||
// Fallback: Try to restart the service using launchctl load
|
// Fallback: Try to restart the service using launchctl load
|
||||||
do {
|
do {
|
||||||
|
|
@ -45,15 +55,26 @@ class SystemOSDManager {
|
||||||
fallbackTask.arguments = ["load", "-w", "/System/Library/LaunchAgents/com.apple.OSDUIHelper.plist"]
|
fallbackTask.arguments = ["load", "-w", "/System/Library/LaunchAgents/com.apple.OSDUIHelper.plist"]
|
||||||
try fallbackTask.run()
|
try fallbackTask.run()
|
||||||
fallbackTask.waitUntilExit()
|
fallbackTask.waitUntilExit()
|
||||||
print("✅ System HUD re-enabled via fallback method")
|
|
||||||
|
await MainActor.run {
|
||||||
|
print("✅ System HUD re-enabled via fallback method")
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
NSLog("❌ Fallback method also failed: \(error)")
|
await MainActor.run {
|
||||||
|
NSLog("❌ Fallback method also failed: \(error)")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Disables the system HUD by stopping OSDUIHelper
|
/// Disables the system HUD by stopping OSDUIHelper
|
||||||
public static func disableSystemHUD() {
|
public static func disableSystemHUD() {
|
||||||
|
Task.detached(priority: .background) {
|
||||||
|
await disableSystemHUDAsync()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func disableSystemHUDAsync() async {
|
||||||
do {
|
do {
|
||||||
let kickstart = Process()
|
let kickstart = Process()
|
||||||
kickstart.executableURL = URL(fileURLWithPath: "/bin/launchctl")
|
kickstart.executableURL = URL(fileURLWithPath: "/bin/launchctl")
|
||||||
|
|
@ -61,7 +82,8 @@ class SystemOSDManager {
|
||||||
kickstart.arguments = ["kickstart", "gui/\(getuid())/com.apple.OSDUIHelper"]
|
kickstart.arguments = ["kickstart", "gui/\(getuid())/com.apple.OSDUIHelper"]
|
||||||
try kickstart.run()
|
try kickstart.run()
|
||||||
kickstart.waitUntilExit()
|
kickstart.waitUntilExit()
|
||||||
usleep(500000) // Make sure it started
|
|
||||||
|
try await Task.sleep(nanoseconds: 500_000_000) // 500ms - async wait instead of usleep
|
||||||
|
|
||||||
let task = Process()
|
let task = Process()
|
||||||
task.executableURL = URL(fileURLWithPath: "/usr/bin/killall")
|
task.executableURL = URL(fileURLWithPath: "/usr/bin/killall")
|
||||||
|
|
@ -69,9 +91,13 @@ class SystemOSDManager {
|
||||||
try task.run()
|
try task.run()
|
||||||
task.waitUntilExit()
|
task.waitUntilExit()
|
||||||
|
|
||||||
print("✅ System HUD disabled")
|
await MainActor.run {
|
||||||
|
print("✅ System HUD disabled")
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
NSLog("❌ Error while trying to hide OSDUIHelper: \(error)")
|
await MainActor.run {
|
||||||
|
NSLog("❌ Error while trying to hide OSDUIHelper: \(error)")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -96,4 +122,14 @@ class SystemOSDManager {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Async version of status checking to avoid main thread blocking
|
||||||
|
public static func isOSDUIHelperRunningAsync() async -> Bool {
|
||||||
|
return await withCheckedContinuation { continuation in
|
||||||
|
Task.detached(priority: .background) {
|
||||||
|
let result = isOSDUIHelperRunning()
|
||||||
|
continuation.resume(returning: result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue