mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 15:14:33 +00:00
Working Indicators
This commit is contained in:
parent
79eb1311bf
commit
1a45d4e7f4
16 changed files with 629 additions and 17 deletions
153
DynamicIsland/managers/SystemChangesObserver.swift
Normal file
153
DynamicIsland/managers/SystemChangesObserver.swift
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
//
|
||||
// SystemChangesObserver.swift
|
||||
// DynamicIsland
|
||||
//
|
||||
// Adapted from TheBoringWorker-HUD ChangesObserver.swift
|
||||
// Created by GitHub Copilot on 06/09/25.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import Foundation
|
||||
import Defaults
|
||||
|
||||
class SystemChangesObserver: ObservableObject {
|
||||
private var oldVolume: Float
|
||||
private var oldMuted: Bool
|
||||
private var oldBrightness: Float = 0
|
||||
|
||||
private weak var coordinator: DynamicIslandViewCoordinator?
|
||||
|
||||
@Default(.enableSystemHUD) var enableSystemHUD
|
||||
@Default(.enableVolumeHUD) var enableVolumeHUD
|
||||
@Default(.enableBrightnessHUD) var enableBrightnessHUD
|
||||
|
||||
init(coordinator: DynamicIslandViewCoordinator) {
|
||||
self.coordinator = coordinator
|
||||
oldVolume = SystemVolumeManager.getOutputVolume()
|
||||
oldMuted = SystemVolumeManager.isMuted()
|
||||
|
||||
do {
|
||||
oldBrightness = try SystemDisplayManager.getDisplayBrightness()
|
||||
} catch {
|
||||
NSLog("Failed to retrieve display brightness: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
func startObserving() {
|
||||
guard enableSystemHUD else { return }
|
||||
|
||||
createObservers()
|
||||
createTimerForContinuousChangesCheck(with: 0.2)
|
||||
}
|
||||
|
||||
private func createTimerForContinuousChangesCheck(with seconds: TimeInterval) {
|
||||
let 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 enableSystemHUD && enableVolumeHUD else { return }
|
||||
|
||||
if oldVolume == 0.0 || oldVolume == 1.0 {
|
||||
sendVolumeNotification(value: oldVolume)
|
||||
}
|
||||
print("Showing volume HUD")
|
||||
}
|
||||
|
||||
@objc func showBrightnessHUD() {
|
||||
guard enableSystemHUD && enableBrightnessHUD else { return }
|
||||
|
||||
if oldBrightness == 0.0 || oldBrightness == 1.0 {
|
||||
sendBrightnessNotification(value: oldBrightness)
|
||||
}
|
||||
print("Showing brightness HUD")
|
||||
}
|
||||
|
||||
@objc func checkChanges() {
|
||||
guard enableSystemHUD else { return }
|
||||
|
||||
checkBrightnessChanges()
|
||||
checkVolumeChanges()
|
||||
}
|
||||
|
||||
private func isAlmost(firstNumber: Float, secondNumber: Float) -> Bool {
|
||||
let marginValue = 5 / 100.0
|
||||
return (firstNumber + Float(marginValue) >= secondNumber && firstNumber - Float(marginValue) <= secondNumber)
|
||||
}
|
||||
|
||||
private func checkVolumeChanges() {
|
||||
guard enableVolumeHUD else { return }
|
||||
|
||||
let newVolume = SystemVolumeManager.getOutputVolume()
|
||||
let newMuted = SystemVolumeManager.isMuted()
|
||||
|
||||
if !isAlmost(firstNumber: oldVolume, secondNumber: newVolume) || newMuted != oldMuted {
|
||||
if newMuted {
|
||||
sendVolumeNotification(value: 0.0)
|
||||
} else {
|
||||
sendVolumeNotification(value: newVolume)
|
||||
}
|
||||
oldVolume = newVolume
|
||||
oldMuted = newMuted
|
||||
}
|
||||
}
|
||||
|
||||
private func checkBrightnessChanges() {
|
||||
guard enableBrightnessHUD else { return }
|
||||
|
||||
if NSScreen.screens.count == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
let newBrightness = try SystemDisplayManager.getDisplayBrightness()
|
||||
if !isAlmost(firstNumber: oldBrightness, secondNumber: newBrightness) {
|
||||
sendBrightnessNotification(value: newBrightness)
|
||||
oldBrightness = newBrightness
|
||||
}
|
||||
} catch {
|
||||
NSLog("Failed to retrieve display brightness: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Internal Notification Methods
|
||||
|
||||
private func sendVolumeNotification(value: Float) {
|
||||
coordinator?.toggleSneakPeek(
|
||||
status: true,
|
||||
type: .volume,
|
||||
value: CGFloat(value),
|
||||
icon: ""
|
||||
)
|
||||
}
|
||||
|
||||
private func sendBrightnessNotification(value: Float) {
|
||||
coordinator?.toggleSneakPeek(
|
||||
status: true,
|
||||
type: .brightness,
|
||||
value: CGFloat(value),
|
||||
icon: ""
|
||||
)
|
||||
}
|
||||
|
||||
deinit {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
}
|
||||
73
DynamicIsland/managers/SystemDisplayManager.swift
Normal file
73
DynamicIsland/managers/SystemDisplayManager.swift
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
//
|
||||
// SystemDisplayManager.swift
|
||||
// DynamicIsland
|
||||
//
|
||||
// Adapted from TheBoringWorker-HUD DisplayManager.swift
|
||||
// Created by GitHub Copilot on 06/09/25.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Cocoa
|
||||
|
||||
class SystemDisplayManager {
|
||||
private init() {}
|
||||
|
||||
private static var method = SensorMethod.standard
|
||||
|
||||
static func getDisplayBrightness() throws -> Float {
|
||||
switch SystemDisplayManager.method {
|
||||
case .standard:
|
||||
do {
|
||||
return try getStandardDisplayBrightness()
|
||||
} catch {
|
||||
method = .m1
|
||||
}
|
||||
case .m1:
|
||||
do {
|
||||
return try getM1DisplayBrightness()
|
||||
} catch {
|
||||
method = .allFailed
|
||||
}
|
||||
case .allFailed:
|
||||
throw SensorError.Display.notFound
|
||||
}
|
||||
return try getDisplayBrightness()
|
||||
}
|
||||
|
||||
private static func getStandardDisplayBrightness() throws -> Float {
|
||||
var brightness: float_t = 1
|
||||
let service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODisplayConnect"))
|
||||
defer {
|
||||
IOObjectRelease(service)
|
||||
}
|
||||
|
||||
let result = IODisplayGetFloatParameter(service, 0, kIODisplayBrightnessKey as CFString, &brightness)
|
||||
if result != kIOReturnSuccess {
|
||||
throw SensorError.Display.notStandard
|
||||
}
|
||||
return brightness
|
||||
}
|
||||
|
||||
private static func getM1DisplayBrightness() throws -> Float {
|
||||
let task = Process()
|
||||
task.launchPath = "/usr/libexec/corebrightnessdiag"
|
||||
task.arguments = ["status-info"]
|
||||
let pipe = Pipe()
|
||||
task.standardOutput = pipe
|
||||
try task.run()
|
||||
let data = pipe.fileHandleForReading.readDataToEndOfFile()
|
||||
task.waitUntilExit()
|
||||
|
||||
if let plist = try? PropertyListSerialization.propertyList(from: data, options: [], format: nil) as? NSDictionary,
|
||||
let displays = plist["CBDisplays"] as? [String: [String: Any]] {
|
||||
for display in displays.values {
|
||||
if let displayInfo = display["Display"] as? [String: Any],
|
||||
displayInfo["DisplayServicesIsBuiltInDisplay"] as? Bool == true,
|
||||
let brightness = displayInfo["DisplayServicesBrightness"] as? Float {
|
||||
return brightness
|
||||
}
|
||||
}
|
||||
}
|
||||
throw SensorError.Display.notSilicon
|
||||
}
|
||||
}
|
||||
82
DynamicIsland/managers/SystemHUDManager.swift
Normal file
82
DynamicIsland/managers/SystemHUDManager.swift
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
//
|
||||
// SystemHUDManager.swift
|
||||
// DynamicIsland
|
||||
//
|
||||
// Created by GitHub Copilot on 06/09/25.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Defaults
|
||||
|
||||
class SystemHUDManager: ObservableObject {
|
||||
static let shared = SystemHUDManager()
|
||||
|
||||
private var changesObserver: SystemChangesObserver?
|
||||
private weak var coordinator: DynamicIslandViewCoordinator?
|
||||
|
||||
@Default(.enableSystemHUD) var enableSystemHUD {
|
||||
didSet {
|
||||
Task { @MainActor in
|
||||
if enableSystemHUD {
|
||||
await startSystemHUD()
|
||||
} else {
|
||||
await stopSystemHUD()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private init() {}
|
||||
|
||||
func setup(coordinator: DynamicIslandViewCoordinator) {
|
||||
self.coordinator = coordinator
|
||||
|
||||
if enableSystemHUD {
|
||||
Task { @MainActor in
|
||||
await startSystemHUD()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private func startSystemHUD() async {
|
||||
guard let coordinator = coordinator else { return }
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private func stopSystemHUD() async {
|
||||
changesObserver = nil
|
||||
|
||||
// Re-enable system HUD
|
||||
enableOriginalSystemHUD()
|
||||
|
||||
print("System HUD replacement stopped")
|
||||
}
|
||||
|
||||
private func disableSystemHUD() {
|
||||
// Disable the system HUD using SystemOSDManager
|
||||
SystemOSDManager.disableSystemHUD()
|
||||
}
|
||||
|
||||
private func enableOriginalSystemHUD() {
|
||||
// Re-enable the system HUD using SystemOSDManager
|
||||
SystemOSDManager.enableSystemHUD()
|
||||
}
|
||||
|
||||
deinit {
|
||||
Task { @MainActor in
|
||||
await stopSystemHUD()
|
||||
}
|
||||
}
|
||||
}
|
||||
44
DynamicIsland/managers/SystemKeyObserver.swift
Normal file
44
DynamicIsland/managers/SystemKeyObserver.swift
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
//
|
||||
// SystemKeyObserver.swift
|
||||
// DynamicIsland
|
||||
//
|
||||
// Adapted from TheBoringWorker-HUD KeyPressObserver.swift
|
||||
// Created by GitHub Copilot on 06/09/25.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
class SystemKeyObserver: NSApplication {
|
||||
static let volumeChanged = Notification.Name("DynamicIsland.volumeChanged")
|
||||
static let brightnessChanged = Notification.Name("DynamicIsland.brightnessChanged")
|
||||
static let keyboardIlluminationChanged = Notification.Name("DynamicIsland.keyboardIlluminationChanged")
|
||||
|
||||
override func sendEvent(_ event: NSEvent) {
|
||||
if event.type == .systemDefined && event.subtype.rawValue == 8 {
|
||||
let keyCode = ((event.data1 & 0xFFFF0000) >> 16)
|
||||
let keyFlags = (event.data1 & 0x0000FFFF)
|
||||
// Get the key state. 0xA is KeyDown, OxB is KeyUp
|
||||
let keyState = ((keyFlags & 0xFF00) >> 8) == 0xA
|
||||
let keyRepeat = keyFlags & 0x1
|
||||
mediaKeyEvent(key: Int32(keyCode), state: keyState, keyRepeat: Bool(truncating: keyRepeat as NSNumber))
|
||||
}
|
||||
|
||||
super.sendEvent(event)
|
||||
}
|
||||
|
||||
func mediaKeyEvent(key: Int32, state: Bool, keyRepeat: Bool) {
|
||||
// Only send events on KeyDown. Without this check, these events will happen twice
|
||||
if state {
|
||||
switch key {
|
||||
case NX_KEYTYPE_SOUND_DOWN, NX_KEYTYPE_SOUND_UP, NX_KEYTYPE_MUTE:
|
||||
NotificationCenter.default.post(name: SystemKeyObserver.volumeChanged, object: self)
|
||||
case NX_KEYTYPE_BRIGHTNESS_UP, NX_KEYTYPE_BRIGHTNESS_DOWN:
|
||||
NotificationCenter.default.post(name: SystemKeyObserver.brightnessChanged, object: self)
|
||||
case NX_KEYTYPE_ILLUMINATION_DOWN, NX_KEYTYPE_ILLUMINATION_UP:
|
||||
NotificationCenter.default.post(name: SystemKeyObserver.keyboardIlluminationChanged, object: self)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
47
DynamicIsland/managers/SystemOSDManager.swift
Normal file
47
DynamicIsland/managers/SystemOSDManager.swift
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
//
|
||||
// SystemOSDManager.swift
|
||||
// DynamicIsland
|
||||
//
|
||||
// Adapted from TheBoringWorker-HUD OSDUIManager.swift
|
||||
// Created by GitHub Copilot on 06/09/25.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class SystemOSDManager {
|
||||
private init() {}
|
||||
|
||||
/// Re-enables the system HUD by restarting OSDUIHelper
|
||||
public static func enableSystemHUD() {
|
||||
do {
|
||||
let task = Process()
|
||||
task.executableURL = URL(fileURLWithPath: "/usr/bin/killall")
|
||||
task.arguments = ["-9", "OSDUIHelper"]
|
||||
try task.run()
|
||||
print("System HUD re-enabled")
|
||||
} catch {
|
||||
NSLog("Error while trying to re-enable OSDUIHelper. \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
/// Disables the system HUD by stopping OSDUIHelper
|
||||
public static func disableSystemHUD() {
|
||||
do {
|
||||
let kickstart = Process()
|
||||
kickstart.executableURL = URL(fileURLWithPath: "/bin/launchctl")
|
||||
// When macOS boots, OSDUIHelper does not start until a volume button is pressed. We can workaround this by kickstarting it.
|
||||
kickstart.arguments = ["kickstart", "gui/\(getuid())/com.apple.OSDUIHelper"]
|
||||
try kickstart.run()
|
||||
kickstart.waitUntilExit()
|
||||
usleep(500000) // Make sure it started
|
||||
|
||||
let task = Process()
|
||||
task.executableURL = URL(fileURLWithPath: "/usr/bin/killall")
|
||||
task.arguments = ["-STOP", "OSDUIHelper"]
|
||||
try task.run()
|
||||
print("System HUD disabled")
|
||||
} catch {
|
||||
NSLog("Error while trying to hide OSDUIHelper. Please create an issue on GitHub. Error: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
35
DynamicIsland/managers/SystemVolumeManager.swift
Normal file
35
DynamicIsland/managers/SystemVolumeManager.swift
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// SystemVolumeManager.swift
|
||||
// DynamicIsland
|
||||
//
|
||||
// Adapted from TheBoringWorker-HUD VolumeManager.swift
|
||||
// Created by GitHub Copilot on 06/09/25.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class SystemVolumeManager {
|
||||
private init() {}
|
||||
|
||||
static func isMuted() -> Bool {
|
||||
do {
|
||||
return try AppleScriptRunner.run(script: "return output muted of (get volume settings)") == "true"
|
||||
} catch {
|
||||
NSLog("Error while trying to retrieve muted properties of device: \(error). Returning default value false.")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
static func getOutputVolume() -> Float {
|
||||
do {
|
||||
if let volumeStr = Float(try AppleScriptRunner.run(script: "return output volume of (get volume settings)")) {
|
||||
return volumeStr / 100
|
||||
} else {
|
||||
NSLog("Error while trying to parse volume string value. Returning default volume value 1.")
|
||||
}
|
||||
} catch {
|
||||
NSLog("Error while trying to retrieve volume properties of device: \(error). Returning default volume value 1.")
|
||||
}
|
||||
return 0.01
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue