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
|
|
@ -7,6 +7,9 @@
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
7E25ECA12E6C6C3800700E72 /* CoreBrightness.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7E25ECA02E6C6C3800700E72 /* CoreBrightness.framework */; };
|
||||||
|
7E25ECA32E6C6E2600700E72 /* OSD.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7E25ECA22E6C6E2600700E72 /* OSD.framework */; };
|
||||||
|
7E25ECA52E6C6E7800700E72 /* DisplayServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7E25ECA42E6C6E7800700E72 /* DisplayServices.framework */; };
|
||||||
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 */; };
|
||||||
|
|
@ -39,6 +42,9 @@
|
||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
7E25ECA02E6C6C3800700E72 /* CoreBrightness.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreBrightness.framework; path = /System/Library/PrivateFrameworks/CoreBrightness.framework; sourceTree = "<absolute>"; };
|
||||||
|
7E25ECA22E6C6E2600700E72 /* OSD.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OSD.framework; path = /System/Library/PrivateFrameworks/OSD.framework; sourceTree = "<absolute>"; };
|
||||||
|
7E25ECA42E6C6E7800700E72 /* DisplayServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DisplayServices.framework; path = /System/Library/PrivateFrameworks/DisplayServices.framework; sourceTree = "<absolute>"; };
|
||||||
7E81303D2E3FB40500E08F3F /* mediaremote-adapter.pl */ = {isa = PBXFileReference; lastKnownFileType = text.script.perl; path = "mediaremote-adapter.pl"; sourceTree = "<group>"; };
|
7E81303D2E3FB40500E08F3F /* mediaremote-adapter.pl */ = {isa = PBXFileReference; lastKnownFileType = text.script.perl; path = "mediaremote-adapter.pl"; sourceTree = "<group>"; };
|
||||||
7E81303E2E3FB40500E08F3F /* NowPlayingTestClient */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = NowPlayingTestClient; sourceTree = "<group>"; };
|
7E81303E2E3FB40500E08F3F /* NowPlayingTestClient */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = NowPlayingTestClient; sourceTree = "<group>"; };
|
||||||
7EE1D73C2E6B1E0B00997219 /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = System/Library/Frameworks/ApplicationServices.framework; sourceTree = SDKROOT; };
|
7EE1D73C2E6B1E0B00997219 /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = System/Library/Frameworks/ApplicationServices.framework; sourceTree = SDKROOT; };
|
||||||
|
|
@ -60,12 +66,15 @@
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
7E25ECA12E6C6C3800700E72 /* CoreBrightness.framework in Frameworks */,
|
||||||
7EE1D73F2E6B1E1200997219 /* IOKit.framework in Frameworks */,
|
7EE1D73F2E6B1E1200997219 /* IOKit.framework in Frameworks */,
|
||||||
7EE1D73D2E6B1E0B00997219 /* ApplicationServices.framework in Frameworks */,
|
7EE1D73D2E6B1E0B00997219 /* ApplicationServices.framework in Frameworks */,
|
||||||
BA90505C953246D0AB206451 /* MediaRemoteAdapter.framework in Frameworks */,
|
BA90505C953246D0AB206451 /* MediaRemoteAdapter.framework in Frameworks */,
|
||||||
7E61181C2E24071E0050006C /* MacroVisionKit in Frameworks */,
|
7E61181C2E24071E0050006C /* MacroVisionKit in Frameworks */,
|
||||||
7E61181B2E24071E0050006C /* LottieUI in Frameworks */,
|
7E61181B2E24071E0050006C /* LottieUI in Frameworks */,
|
||||||
7E61181A2E24071E0050006C /* SwiftUIIntrospect in Frameworks */,
|
7E61181A2E24071E0050006C /* SwiftUIIntrospect in Frameworks */,
|
||||||
|
7E25ECA32E6C6E2600700E72 /* OSD.framework in Frameworks */,
|
||||||
|
7E25ECA52E6C6E7800700E72 /* DisplayServices.framework 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 */,
|
||||||
|
|
@ -91,6 +100,9 @@
|
||||||
7E81304F2E3FB42700E08F3F /* Frameworks */ = {
|
7E81304F2E3FB42700E08F3F /* Frameworks */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
7E25ECA02E6C6C3800700E72 /* CoreBrightness.framework */,
|
||||||
|
7E25ECA42E6C6E7800700E72 /* DisplayServices.framework */,
|
||||||
|
7E25ECA22E6C6E2600700E72 /* OSD.framework */,
|
||||||
7EE1D73E2E6B1E1200997219 /* IOKit.framework */,
|
7EE1D73E2E6B1E1200997219 /* IOKit.framework */,
|
||||||
7EE1D73C2E6B1E0B00997219 /* ApplicationServices.framework */,
|
7EE1D73C2E6B1E0B00997219 /* ApplicationServices.framework */,
|
||||||
);
|
);
|
||||||
|
|
@ -371,6 +383,10 @@
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
|
SYSTEM_FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
|
||||||
|
);
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
};
|
};
|
||||||
|
|
@ -408,6 +424,10 @@
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
|
SYSTEM_FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
|
||||||
|
);
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,20 +2,8 @@
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>com.apple.security.app-sandbox</key>
|
|
||||||
<true/>
|
|
||||||
<key>com.apple.security.automation.apple-events</key>
|
<key>com.apple.security.automation.apple-events</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.device.camera</key>
|
|
||||||
<true/>
|
|
||||||
<key>com.apple.security.files.user-selected.read-write</key>
|
|
||||||
<true/>
|
|
||||||
<key>com.apple.security.network.client</key>
|
|
||||||
<true/>
|
|
||||||
<key>com.apple.security.network.server</key>
|
|
||||||
<true/>
|
|
||||||
<key>com.apple.security.personal-information.calendars</key>
|
|
||||||
<true/>
|
|
||||||
<key>com.apple.security.temporary-exception.apple-events</key>
|
<key>com.apple.security.temporary-exception.apple-events</key>
|
||||||
<array>
|
<array>
|
||||||
<string>com.spotify.client</string>
|
<string>com.spotify.client</string>
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,10 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
func applicationWillTerminate(_ notification: Notification) {
|
func applicationWillTerminate(_ notification: Notification) {
|
||||||
// Cancel any pending window size updates
|
// Cancel any pending window size updates
|
||||||
windowSizeUpdateWorkItem?.cancel()
|
windowSizeUpdateWorkItem?.cancel()
|
||||||
|
|
||||||
|
// Re-enable system HUD on app termination
|
||||||
|
SystemOSDManager.enableSystemHUD()
|
||||||
|
|
||||||
NotificationCenter.default.removeObserver(self)
|
NotificationCenter.default.removeObserver(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -290,6 +294,9 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
|
|
||||||
coordinator.setupWorkersNotificationObservers()
|
coordinator.setupWorkersNotificationObservers()
|
||||||
|
|
||||||
|
// Setup SystemHUD Manager
|
||||||
|
SystemHUDManager.shared.setup(coordinator: coordinator)
|
||||||
|
|
||||||
// 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()
|
||||||
|
|
|
||||||
|
|
@ -202,6 +202,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Brightness HUD" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Built-in System Monitoring" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Built-in system monitoring detects volume, brightness, and keyboard backlight changes directly without requiring external apps." : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Buy us a coffee" : {
|
"Buy us a coffee" : {
|
||||||
|
|
||||||
|
|
@ -467,6 +476,12 @@
|
||||||
},
|
},
|
||||||
"Email address" : {
|
"Email address" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Enable Built-in System HUD" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Enable built-in system monitoring to replace macOS HUD notifications with Dynamic Island displays." : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Enable colored spectrograms" : {
|
"Enable colored spectrograms" : {
|
||||||
"extractionState" : "stale",
|
"extractionState" : "stale",
|
||||||
|
|
@ -638,6 +653,9 @@
|
||||||
},
|
},
|
||||||
"Installed extensions" : {
|
"Installed extensions" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Keyboard Backlight HUD" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Keyboard shortcuts are disabled" : {
|
"Keyboard shortcuts are disabled" : {
|
||||||
|
|
||||||
|
|
@ -1027,6 +1045,9 @@
|
||||||
},
|
},
|
||||||
"Remember last tab" : {
|
"Remember last tab" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Replaces system HUD notifications with Dynamic Island displays." : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Reset to Default" : {
|
"Reset to Default" : {
|
||||||
|
|
||||||
|
|
@ -1080,6 +1101,9 @@
|
||||||
},
|
},
|
||||||
"Selected animation" : {
|
"Selected animation" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Sensitivity" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Set a custom duration for the timer. This will be used when you press the 'Custom' button in the timer interface." : {
|
"Set a custom duration for the timer. This will be used when you press the 'Custom' button in the timer interface." : {
|
||||||
|
|
||||||
|
|
@ -1337,6 +1361,9 @@
|
||||||
},
|
},
|
||||||
"View on GitHub: th-ch/youtube-music" : {
|
"View on GitHub: th-ch/youtube-music" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Volume HUD" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Welcome" : {
|
"Welcome" : {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,11 +41,8 @@ struct SettingsView: View {
|
||||||
NavigationLink(value: "Calendar") {
|
NavigationLink(value: "Calendar") {
|
||||||
Label("Calendar", systemImage: "calendar")
|
Label("Calendar", systemImage: "calendar")
|
||||||
}
|
}
|
||||||
if extensionManager.installedExtensions
|
NavigationLink(value: "HUD") {
|
||||||
.contains(where: { $0.bundleIdentifier == hudExtension }) {
|
Label("HUDs", systemImage: "dial.medium.fill")
|
||||||
NavigationLink(value: "HUD") {
|
|
||||||
Label("HUDs", systemImage: "dial.medium.fill")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
NavigationLink(value: "Battery") {
|
NavigationLink(value: "Battery") {
|
||||||
Label("Battery", systemImage: "battery.100.bolt")
|
Label("Battery", systemImage: "battery.100.bolt")
|
||||||
|
|
@ -423,14 +420,66 @@ struct HUD: View {
|
||||||
@EnvironmentObject var vm: DynamicIslandViewModel
|
@EnvironmentObject var vm: DynamicIslandViewModel
|
||||||
@Default(.inlineHUD) var inlineHUD
|
@Default(.inlineHUD) var inlineHUD
|
||||||
@Default(.enableGradient) var enableGradient
|
@Default(.enableGradient) var enableGradient
|
||||||
|
@Default(.enableSystemHUD) var enableSystemHUD
|
||||||
|
@Default(.enableVolumeHUD) var enableVolumeHUD
|
||||||
|
@Default(.enableBrightnessHUD) var enableBrightnessHUD
|
||||||
|
@Default(.enableKeyboardBacklightHUD) var enableKeyboardBacklightHUD
|
||||||
|
@Default(.systemHUDSensitivity) var systemHUDSensitivity
|
||||||
@ObservedObject var coordinator = DynamicIslandViewCoordinator.shared
|
@ObservedObject var coordinator = DynamicIslandViewCoordinator.shared
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
Form {
|
||||||
Section {
|
Section {
|
||||||
Toggle("Enable HUD replacement", isOn: $coordinator.hudReplacement)
|
Toggle("Enable HUD replacement", isOn: $coordinator.hudReplacement)
|
||||||
} header: {
|
} header: {
|
||||||
Text("General")
|
Text("General")
|
||||||
|
} footer: {
|
||||||
|
Text("Replaces system HUD notifications with Dynamic Island displays.")
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
.font(.caption)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Section {
|
||||||
|
Toggle("Enable Built-in System HUD", isOn: $enableSystemHUD)
|
||||||
|
.onChange(of: enableSystemHUD) { _ in
|
||||||
|
// SystemHUDManager will automatically handle start/stop via its @Default observer
|
||||||
|
}
|
||||||
|
|
||||||
|
if enableSystemHUD {
|
||||||
|
Toggle("Volume HUD", isOn: $enableVolumeHUD)
|
||||||
|
Toggle("Brightness HUD", isOn: $enableBrightnessHUD)
|
||||||
|
Toggle("Keyboard Backlight HUD", isOn: $enableKeyboardBacklightHUD)
|
||||||
|
|
||||||
|
HStack {
|
||||||
|
Text("Sensitivity")
|
||||||
|
Spacer()
|
||||||
|
Slider(value: Binding(
|
||||||
|
get: { Double(systemHUDSensitivity) },
|
||||||
|
set: { systemHUDSensitivity = Int($0) }
|
||||||
|
), in: 1...10, step: 1) {
|
||||||
|
Text("Sensitivity")
|
||||||
|
}
|
||||||
|
.frame(width: 120)
|
||||||
|
Text("\(systemHUDSensitivity)")
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
.font(.caption)
|
||||||
|
.frame(width: 20)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} header: {
|
||||||
|
Text("Built-in System Monitoring")
|
||||||
|
} footer: {
|
||||||
|
if enableSystemHUD {
|
||||||
|
Text("Built-in system monitoring detects volume, brightness, and keyboard backlight changes directly without requiring external apps.")
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
.font(.caption)
|
||||||
|
} else {
|
||||||
|
Text("Enable built-in system monitoring to replace macOS HUD notifications with Dynamic Island displays.")
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
.font(.caption)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
Picker("HUD style", selection: $inlineHUD) {
|
Picker("HUD style", selection: $inlineHUD) {
|
||||||
Text("Default")
|
Text("Default")
|
||||||
|
|
|
||||||
15
DynamicIsland/helpers/AppleScriptError.swift
Normal file
15
DynamicIsland/helpers/AppleScriptError.swift
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
//
|
||||||
|
// AppleScriptError.swift
|
||||||
|
// DynamicIsland
|
||||||
|
//
|
||||||
|
// Adapted from TheBoringWorker-HUD
|
||||||
|
// Created by GitHub Copilot on 06/09/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum AppleScriptError: Error {
|
||||||
|
case initScriptFailed
|
||||||
|
case runtimeError
|
||||||
|
case emptyOutput
|
||||||
|
}
|
||||||
28
DynamicIsland/helpers/AppleScriptRunner.swift
Normal file
28
DynamicIsland/helpers/AppleScriptRunner.swift
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
//
|
||||||
|
// AppleScriptRunner.swift
|
||||||
|
// DynamicIsland
|
||||||
|
//
|
||||||
|
// Adapted from TheBoringWorker-HUD
|
||||||
|
// Created by GitHub Copilot on 06/09/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class AppleScriptRunner {
|
||||||
|
private init() {}
|
||||||
|
|
||||||
|
static func run(script: String) throws -> String {
|
||||||
|
var error: NSDictionary?
|
||||||
|
if let scriptObject = NSAppleScript(source: script) {
|
||||||
|
let output: NSAppleEventDescriptor = scriptObject.executeAndReturnError(&error)
|
||||||
|
guard error == nil else {
|
||||||
|
throw AppleScriptError.runtimeError
|
||||||
|
}
|
||||||
|
if let outputString = output.stringValue {
|
||||||
|
return outputString
|
||||||
|
}
|
||||||
|
throw AppleScriptError.emptyOutput
|
||||||
|
}
|
||||||
|
throw AppleScriptError.initScriptFailed
|
||||||
|
}
|
||||||
|
}
|
||||||
22
DynamicIsland/helpers/SensorError.swift
Normal file
22
DynamicIsland/helpers/SensorError.swift
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
//
|
||||||
|
// SensorError.swift
|
||||||
|
// DynamicIsland
|
||||||
|
//
|
||||||
|
// Adapted from TheBoringWorker-HUD
|
||||||
|
// Created by GitHub Copilot on 06/09/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum SensorError: Error {
|
||||||
|
enum Display: Error {
|
||||||
|
case notFound
|
||||||
|
case notSilicon
|
||||||
|
case notStandard
|
||||||
|
}
|
||||||
|
enum Keyboard: Error {
|
||||||
|
case notFound
|
||||||
|
case notSilicon
|
||||||
|
case notStandard
|
||||||
|
}
|
||||||
|
}
|
||||||
15
DynamicIsland/helpers/SensorMethod.swift
Normal file
15
DynamicIsland/helpers/SensorMethod.swift
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
//
|
||||||
|
// SensorMethod.swift
|
||||||
|
// DynamicIsland
|
||||||
|
//
|
||||||
|
// Adapted from TheBoringWorker-HUD
|
||||||
|
// Created by GitHub Copilot on 06/09/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum SensorMethod {
|
||||||
|
case standard
|
||||||
|
case m1
|
||||||
|
case allFailed
|
||||||
|
}
|
||||||
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -221,6 +221,13 @@ extension Defaults.Keys {
|
||||||
// MARK: Keyboard Shortcuts
|
// MARK: Keyboard Shortcuts
|
||||||
static let enableShortcuts = Key<Bool>("enableShortcuts", default: true)
|
static let enableShortcuts = Key<Bool>("enableShortcuts", default: true)
|
||||||
|
|
||||||
|
// MARK: System HUD Feature
|
||||||
|
static let enableSystemHUD = Key<Bool>("enableSystemHUD", default: false)
|
||||||
|
static let enableVolumeHUD = Key<Bool>("enableVolumeHUD", default: true)
|
||||||
|
static let enableBrightnessHUD = Key<Bool>("enableBrightnessHUD", default: true)
|
||||||
|
static let enableKeyboardBacklightHUD = Key<Bool>("enableKeyboardBacklightHUD", default: true)
|
||||||
|
static let systemHUDSensitivity = Key<Int>("systemHUDSensitivity", default: 5)
|
||||||
|
|
||||||
// Helper to determine the default media controller based on macOS version
|
// Helper to determine the default media controller based on macOS version
|
||||||
static var defaultMediaController: MediaControllerType {
|
static var defaultMediaController: MediaControllerType {
|
||||||
if #available(macOS 15.4, *) {
|
if #available(macOS 15.4, *) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue