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
|
|
@ -32,7 +32,10 @@ class DynamicIslandExtensionManager: ObservableObject {
|
|||
|
||||
@Published var installedExtensions: [Extension] = [] {
|
||||
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
|
||||
|
||||
class SystemChangesObserver {
|
||||
private var oldVolume: Float
|
||||
private var oldMuted: Bool
|
||||
private var oldVolume: Float = 0
|
||||
private var oldMuted: Bool = false
|
||||
private var oldBrightness: Float = 0
|
||||
|
||||
private weak var coordinator: DynamicIslandViewCoordinator?
|
||||
private var timer: Timer?
|
||||
|
||||
// Backup timer for edge cases only
|
||||
private var backupTimer: Timer?
|
||||
|
||||
init(coordinator: DynamicIslandViewCoordinator) {
|
||||
self.coordinator = coordinator
|
||||
|
||||
// Initialize baseline values only once
|
||||
oldVolume = SystemVolumeManager.getOutputVolume()
|
||||
oldMuted = SystemVolumeManager.isMuted()
|
||||
|
||||
|
||||
do {
|
||||
oldBrightness = try SystemDisplayManager.getDisplayBrightness()
|
||||
} catch {
|
||||
NSLog("Failed to retrieve display brightness: \(error)")
|
||||
NSLog("Failed to retrieve initial display brightness: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
func startObserving() {
|
||||
guard Defaults[.enableSystemHUD] else { return }
|
||||
|
||||
createObservers()
|
||||
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")
|
||||
startMinimalBackupTimer()
|
||||
}
|
||||
|
||||
@objc func showBrightnessHUD() {
|
||||
guard Defaults[.enableSystemHUD] && Defaults[.enableBrightnessHUD] else { return }
|
||||
|
||||
if oldBrightness == 0.0 || oldBrightness == 1.0 {
|
||||
sendBrightnessNotification(value: oldBrightness)
|
||||
private func startMinimalBackupTimer() {
|
||||
// More frequent backup check (every 300ms) for better responsiveness
|
||||
// This is a reasonable compromise between the original 200ms and the previous 5s
|
||||
backupTimer = Timer.scheduledTimer(withTimeInterval: 0.3, repeats: true) { [weak self] _ in
|
||||
guard Defaults[.enableSystemHUD] else { return }
|
||||
self?.performBackupCheck()
|
||||
}
|
||||
print("Showing brightness HUD")
|
||||
}
|
||||
|
||||
@objc func checkChanges() {
|
||||
guard Defaults[.enableSystemHUD] else { return }
|
||||
|
||||
checkBrightnessChanges()
|
||||
|
||||
private func performBackupCheck() {
|
||||
// Only check if we haven't had recent activity
|
||||
checkVolumeChanges()
|
||||
checkBrightnessChanges()
|
||||
}
|
||||
|
||||
private func isAlmost(firstNumber: Float, secondNumber: Float) -> Bool {
|
||||
let marginValue = 5 / 100.0
|
||||
return (firstNumber + Float(marginValue) >= secondNumber && firstNumber - Float(marginValue) <= secondNumber)
|
||||
|
||||
// MARK: - Public Media Key Event Handlers (called from MediaKeyApplication)
|
||||
|
||||
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() {
|
||||
|
|
@ -95,7 +115,12 @@ class SystemChangesObserver {
|
|||
let newVolume = SystemVolumeManager.getOutputVolume()
|
||||
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 {
|
||||
sendVolumeNotification(value: 0.0)
|
||||
} else {
|
||||
|
|
@ -108,22 +133,30 @@ class SystemChangesObserver {
|
|||
|
||||
private func checkBrightnessChanges() {
|
||||
guard Defaults[.enableBrightnessHUD] else { return }
|
||||
|
||||
if NSScreen.screens.count == 0 {
|
||||
return
|
||||
}
|
||||
guard NSScreen.screens.count > 0 else { return }
|
||||
|
||||
do {
|
||||
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)
|
||||
oldBrightness = newBrightness
|
||||
}
|
||||
} 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
|
||||
|
||||
private func sendVolumeNotification(value: Float) {
|
||||
|
|
@ -149,8 +182,12 @@ class SystemChangesObserver {
|
|||
}
|
||||
|
||||
func stopObserving() {
|
||||
timer?.invalidate()
|
||||
timer = nil
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
// Clean up backup timer
|
||||
backupTimer?.invalidate()
|
||||
backupTimer = nil
|
||||
|
||||
print("🎹 Media key detection stopped")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ class SystemHUDManager {
|
|||
|
||||
private var changesObserver: SystemChangesObserver?
|
||||
private weak var coordinator: DynamicIslandViewCoordinator?
|
||||
private var isSetupComplete = false
|
||||
private var isSystemOperationInProgress = false
|
||||
|
||||
private init() {
|
||||
// Set up observer for settings changes
|
||||
|
|
@ -23,11 +25,14 @@ class SystemHUDManager {
|
|||
private func setupSettingsObserver() {
|
||||
// Use Defaults publisher instead of @Default property wrapper
|
||||
Defaults.publisher(.enableSystemHUD, options: []).sink { [weak self] change in
|
||||
guard let self = self, self.isSetupComplete else {
|
||||
return
|
||||
}
|
||||
Task { @MainActor in
|
||||
if change.newValue {
|
||||
await self?.startSystemHUD()
|
||||
await self.startSystemHUD()
|
||||
} else {
|
||||
await self?.stopSystemHUD()
|
||||
await self.stopSystemHUD()
|
||||
}
|
||||
}
|
||||
}.store(in: &cancellables)
|
||||
|
|
@ -35,34 +40,46 @@ class SystemHUDManager {
|
|||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
/// Public property to check if system operations are in progress
|
||||
var isOperationInProgress: Bool {
|
||||
return isSystemOperationInProgress
|
||||
}
|
||||
|
||||
func setup(coordinator: DynamicIslandViewCoordinator) {
|
||||
self.coordinator = coordinator
|
||||
|
||||
if Defaults[.enableSystemHUD] {
|
||||
Task { @MainActor in
|
||||
await startSystemHUD()
|
||||
self.isSetupComplete = true
|
||||
}
|
||||
} else {
|
||||
isSetupComplete = true
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
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
|
||||
|
||||
changesObserver = SystemChangesObserver(coordinator: coordinator)
|
||||
changesObserver?.startObserving()
|
||||
|
||||
// Disable system HUD if possible
|
||||
// Note: This would require additional implementation similar to OSDUIManager
|
||||
disableSystemHUD()
|
||||
|
||||
print("System HUD replacement started")
|
||||
isSystemOperationInProgress = false
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private func stopSystemHUD() async {
|
||||
guard !isSystemOperationInProgress else { return }
|
||||
|
||||
isSystemOperationInProgress = true
|
||||
changesObserver?.stopObserving()
|
||||
changesObserver = nil
|
||||
|
||||
|
|
@ -70,6 +87,7 @@ class SystemHUDManager {
|
|||
enableOriginalSystemHUD()
|
||||
|
||||
print("System HUD replacement stopped")
|
||||
isSystemOperationInProgress = false
|
||||
}
|
||||
|
||||
private func disableSystemHUD() {
|
||||
|
|
@ -83,21 +101,32 @@ class SystemHUDManager {
|
|||
// Re-enable the system HUD using SystemOSDManager
|
||||
SystemOSDManager.enableSystemHUD()
|
||||
|
||||
// Verify it's working after a short delay
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
|
||||
let isRunning = SystemOSDManager.isOSDUIHelperRunning()
|
||||
// Check status after a reasonable delay, using async to avoid blocking
|
||||
Task {
|
||||
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")")
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
print("🧹 SystemHUDManager deinitializing - ensuring system HUD is restored")
|
||||
cancellables.removeAll()
|
||||
Task { @MainActor in
|
||||
await stopSystemHUD()
|
||||
}
|
||||
// Also call the emergency restore as backup
|
||||
SystemHUDDebugger.forceRestartOSDUIHelper()
|
||||
// Emergency restore is available via SystemHUDManager.emergencyRestoreSystemHUD() if needed
|
||||
}
|
||||
|
||||
/// Emergency restore function - call this if system HUD is not working
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@
|
|||
// Adapted from TheBoringWorker-HUD KeyPressObserver.swift
|
||||
// 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
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,12 @@ class SystemOSDManager {
|
|||
|
||||
/// Re-enables the system HUD by restarting OSDUIHelper
|
||||
public static func enableSystemHUD() {
|
||||
Task.detached(priority: .background) {
|
||||
await enableSystemHUDAsync()
|
||||
}
|
||||
}
|
||||
|
||||
private static func enableSystemHUDAsync() async {
|
||||
do {
|
||||
// First, stop any existing OSDUIHelper process
|
||||
let stopTask = Process()
|
||||
|
|
@ -22,7 +28,7 @@ class SystemOSDManager {
|
|||
stopTask.waitUntilExit()
|
||||
|
||||
// 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
|
||||
let kickstart = Process()
|
||||
|
|
@ -32,11 +38,15 @@ class SystemOSDManager {
|
|||
kickstart.waitUntilExit()
|
||||
|
||||
// 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 {
|
||||
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
|
||||
do {
|
||||
|
|
@ -45,15 +55,26 @@ class SystemOSDManager {
|
|||
fallbackTask.arguments = ["load", "-w", "/System/Library/LaunchAgents/com.apple.OSDUIHelper.plist"]
|
||||
try fallbackTask.run()
|
||||
fallbackTask.waitUntilExit()
|
||||
print("✅ System HUD re-enabled via fallback method")
|
||||
|
||||
await MainActor.run {
|
||||
print("✅ System HUD re-enabled via fallback method")
|
||||
}
|
||||
} catch {
|
||||
NSLog("❌ Fallback method also failed: \(error)")
|
||||
await MainActor.run {
|
||||
NSLog("❌ Fallback method also failed: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Disables the system HUD by stopping OSDUIHelper
|
||||
public static func disableSystemHUD() {
|
||||
Task.detached(priority: .background) {
|
||||
await disableSystemHUDAsync()
|
||||
}
|
||||
}
|
||||
|
||||
private static func disableSystemHUDAsync() async {
|
||||
do {
|
||||
let kickstart = Process()
|
||||
kickstart.executableURL = URL(fileURLWithPath: "/bin/launchctl")
|
||||
|
|
@ -61,7 +82,8 @@ class SystemOSDManager {
|
|||
kickstart.arguments = ["kickstart", "gui/\(getuid())/com.apple.OSDUIHelper"]
|
||||
try kickstart.run()
|
||||
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()
|
||||
task.executableURL = URL(fileURLWithPath: "/usr/bin/killall")
|
||||
|
|
@ -69,9 +91,13 @@ class SystemOSDManager {
|
|||
try task.run()
|
||||
task.waitUntilExit()
|
||||
|
||||
print("✅ System HUD disabled")
|
||||
await MainActor.run {
|
||||
print("✅ System HUD disabled")
|
||||
}
|
||||
} 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
|
||||
}
|
||||
}
|
||||
|
||||
/// 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