From b4f9e51c1f960e94f6e952107495fef66f867187 Mon Sep 17 00:00:00 2001 From: Hariharan Mudaliar Date: Sat, 18 Oct 2025 13:38:37 +0530 Subject: [PATCH 1/6] Working volume --- .gitignore | 1 + DynamicIsland/DynamicIslandApp.swift | 82 ---- .../managers/MediaKeyInterceptor.swift | 204 ++++++++ .../managers/SystemChangesObserver.swift | 458 ++++-------------- DynamicIsland/managers/SystemHUDManager.swift | 133 +---- .../managers/SystemMediaControllers.swift | 348 +++++++++++++ 6 files changed, 645 insertions(+), 581 deletions(-) create mode 100644 DynamicIsland/managers/MediaKeyInterceptor.swift create mode 100644 DynamicIsland/managers/SystemMediaControllers.swift diff --git a/.gitignore b/.gitignore index 8d394d2..8a5e361 100644 --- a/.gitignore +++ b/.gitignore @@ -88,6 +88,7 @@ ONBOARDING_ENHANCEMENT.md SHEET_PRESENTATION_FIX.md lock_mode_reference.md liquid_glass.md +huds.md MoveToSky.xcodeproj /MoveToSky/ \ No newline at end of file diff --git a/DynamicIsland/DynamicIslandApp.swift b/DynamicIsland/DynamicIslandApp.swift index 418f97e..a3adef5 100644 --- a/DynamicIsland/DynamicIslandApp.swift +++ b/DynamicIsland/DynamicIslandApp.swift @@ -93,10 +93,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { private var cancellables = Set() private var windowsHiddenForLock = false - // Media key monitoring - private var globalEventMonitor: Any? - private var localEventMonitor: Any? - // Debouncing mechanism for window size updates private var windowSizeUpdateWorkItem: DispatchWorkItem? // let calendarManager = CalendarManager.shared @@ -127,84 +123,9 @@ class AppDelegate: NSObject, NSApplicationDelegate { 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) { // Cancel any pending window size updates windowSizeUpdateWorkItem?.cancel() - - // Stop media key monitoring - stopMediaKeyMonitoring() - - print("πŸšͺ Application terminating - ensuring system HUD is restored...") - - // Re-enable system HUD on app termination - SystemOSDManager.enableSystemHUD() - - // Give it time to complete - usleep(500000) // 500ms - NotificationCenter.default.removeObserver(self) } @@ -454,9 +375,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { // Setup Privacy Indicator Manager (camera and microphone monitoring) PrivacyIndicatorManager.shared.startMonitoring() - // Setup media key monitoring - setupMediaKeyMonitoring() - // Observe tab changes - use debounced updates coordinator.$currentView.sink { [weak self] newView in self?.debouncedUpdateWindowSize() diff --git a/DynamicIsland/managers/MediaKeyInterceptor.swift b/DynamicIsland/managers/MediaKeyInterceptor.swift new file mode 100644 index 0000000..2821769 --- /dev/null +++ b/DynamicIsland/managers/MediaKeyInterceptor.swift @@ -0,0 +1,204 @@ +import Foundation +import AppKit +import CoreGraphics +#if canImport(ApplicationServices) +import ApplicationServices +#endif + +private let NX_SYSDEFINED_EVENT_TYPE: UInt32 = 14 +private let NX_KEYTYPE_SOUND_UP: Int32 = 0 +private let NX_KEYTYPE_SOUND_DOWN: Int32 = 1 +private let NX_KEYTYPE_BRIGHTNESS_UP: Int32 = 2 +private let NX_KEYTYPE_BRIGHTNESS_DOWN: Int32 = 3 +private let NX_KEYTYPE_MUTE: Int32 = 7 + +enum MediaKeyDirection { + case up + case down +} + +struct MediaKeyConfiguration { + var interceptVolume: Bool + var interceptBrightness: Bool + + static let disabled = MediaKeyConfiguration(interceptVolume: false, interceptBrightness: false) +} + +protocol MediaKeyInterceptorDelegate: AnyObject { + func mediaKeyInterceptor(_ interceptor: MediaKeyInterceptor, didReceiveVolumeCommand direction: MediaKeyDirection, isRepeat: Bool) + func mediaKeyInterceptor(_ interceptor: MediaKeyInterceptor, didReceiveBrightnessCommand direction: MediaKeyDirection, isRepeat: Bool) + func mediaKeyInterceptorDidToggleMute(_ interceptor: MediaKeyInterceptor) +} + +final class MediaKeyInterceptor { + static let shared = MediaKeyInterceptor() + + weak var delegate: MediaKeyInterceptorDelegate? + var configuration: MediaKeyConfiguration = .disabled { + didSet { + updateTapState() + } + } + + private var eventTap: CFMachPort? + private var runLoopSource: CFRunLoopSource? + private var isTapEnabled = false +#if canImport(ApplicationServices) + private var didRequestAccessibilityPrompt = false +#endif + private let systemDefinedEventType = CGEventType(rawValue: NX_SYSDEFINED_EVENT_TYPE) + private let eventTapLocations: [CGEventTapLocation] = [.cghidEventTap, .cgSessionEventTap] + + private init() {} + + @discardableResult + func start() -> Bool { + guard eventTap == nil else { + updateTapState() + return true + } + +#if canImport(ApplicationServices) + requestAccessibilityPermissionIfNeeded() +#endif + + guard let systemDefinedType = systemDefinedEventType else { + NSLog("❌ Unable to resolve system-defined event type") + return false + } + let mask = CGEventMask(1) << systemDefinedType.rawValue + let callback: CGEventTapCallBack = { _, type, cgEvent, userInfo in + guard let userInfo else { return Unmanaged.passUnretained(cgEvent) } + let interceptor = Unmanaged.fromOpaque(userInfo).takeUnretainedValue() + return interceptor.handleEvent(cgEvent: cgEvent, type: type) + } + + var createdTap: CFMachPort? + for location in eventTapLocations { + if let tap = CGEvent.tapCreate( + tap: location, + place: .headInsertEventTap, + options: .defaultTap, + eventsOfInterest: mask, + callback: callback, + userInfo: UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()) + ) { + createdTap = tap + break + } + } + + guard let tap = createdTap else { +#if canImport(ApplicationServices) + if !AXIsProcessTrusted() { + NSLog("⚠️ Accessibility permission missing; grant access in System Settings β€Ί Privacy & Security β€Ί Accessibility") + } +#endif + NSLog("❌ Failed to create media key event tap") + return false + } + + eventTap = tap + runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, tap, 0) + if let source = runLoopSource { + CFRunLoopAddSource(CFRunLoopGetMain(), source, .commonModes) + } + CGEvent.tapEnable(tap: tap, enable: true) + isTapEnabled = true + NSLog("βœ… Media key event tap installed (HID)") + return true + } + + func stop() { + if let tap = eventTap { + CGEvent.tapEnable(tap: tap, enable: false) + CFMachPortInvalidate(tap) + } + if let source = runLoopSource { + CFRunLoopRemoveSource(CFRunLoopGetMain(), source, .commonModes) + } + eventTap = nil + runLoopSource = nil + isTapEnabled = false + } + + private func updateTapState() { + guard let tap = eventTap else { return } + let shouldEnable = configuration.interceptVolume || configuration.interceptBrightness + if shouldEnable != isTapEnabled { + CGEvent.tapEnable(tap: tap, enable: shouldEnable) + isTapEnabled = shouldEnable + } + } + + private func handleEvent(cgEvent: CGEvent, type: CGEventType) -> Unmanaged? { + guard let systemDefinedType = systemDefinedEventType, + type == systemDefinedType, + let nsEvent = NSEvent(cgEvent: cgEvent), + nsEvent.subtype.rawValue == 8 else { + return Unmanaged.passUnretained(cgEvent) + } + + let data1 = nsEvent.data1 + let keyCode = (data1 & 0xFFFF0000) >> 16 + let keyFlags = data1 & 0x0000FFFF + let keyState = ((keyFlags & 0xFF00) >> 8) == 0xA // 0xA = keyDown, 0xB = keyUp + let isRepeat = (keyFlags & 0x0001) == 1 + + guard keyState else { + // Swallow key-up events only when intercepting, otherwise let them pass through + if shouldHandle(keyCode: Int32(keyCode)) { + return nil + } + return Unmanaged.passUnretained(cgEvent) + } + + switch Int32(keyCode) { + case NX_KEYTYPE_SOUND_UP: + guard configuration.interceptVolume else { return Unmanaged.passUnretained(cgEvent) } + delegate?.mediaKeyInterceptor(self, didReceiveVolumeCommand: .up, isRepeat: isRepeat) + return nil + case NX_KEYTYPE_SOUND_DOWN: + guard configuration.interceptVolume else { return Unmanaged.passUnretained(cgEvent) } + delegate?.mediaKeyInterceptor(self, didReceiveVolumeCommand: .down, isRepeat: isRepeat) + return nil + case NX_KEYTYPE_MUTE: + guard configuration.interceptVolume else { return Unmanaged.passUnretained(cgEvent) } + delegate?.mediaKeyInterceptorDidToggleMute(self) + return nil + case NX_KEYTYPE_BRIGHTNESS_UP: + guard configuration.interceptBrightness else { return Unmanaged.passUnretained(cgEvent) } + delegate?.mediaKeyInterceptor(self, didReceiveBrightnessCommand: .up, isRepeat: isRepeat) + return nil + case NX_KEYTYPE_BRIGHTNESS_DOWN: + guard configuration.interceptBrightness else { return Unmanaged.passUnretained(cgEvent) } + delegate?.mediaKeyInterceptor(self, didReceiveBrightnessCommand: .down, isRepeat: isRepeat) + return nil + default: + return Unmanaged.passUnretained(cgEvent) + } + } + + private func shouldHandle(keyCode: Int32) -> Bool { + switch keyCode { + case NX_KEYTYPE_SOUND_UP, NX_KEYTYPE_SOUND_DOWN, NX_KEYTYPE_MUTE: + return configuration.interceptVolume + case NX_KEYTYPE_BRIGHTNESS_UP, NX_KEYTYPE_BRIGHTNESS_DOWN: + return configuration.interceptBrightness + default: + return false + } + } +} + +#if canImport(ApplicationServices) +extension MediaKeyInterceptor { + private func requestAccessibilityPermissionIfNeeded() { + guard !AXIsProcessTrusted(), !didRequestAccessibilityPrompt else { return } + let promptKey = kAXTrustedCheckOptionPrompt.takeUnretainedValue() as String + let options: CFDictionary = [promptKey: true] as CFDictionary + _ = AXIsProcessTrustedWithOptions(options) + didRequestAccessibilityPrompt = true + } +} +#endif diff --git a/DynamicIsland/managers/SystemChangesObserver.swift b/DynamicIsland/managers/SystemChangesObserver.swift index a609214..8e1ca19 100644 --- a/DynamicIsland/managers/SystemChangesObserver.swift +++ b/DynamicIsland/managers/SystemChangesObserver.swift @@ -1,347 +1,97 @@ -// -// SystemChangesObserver.swift -// DynamicIsland -// -// Adapted from TheBoringWorker-HUD ChangesObserver.swift -// Created by GitHub Copilot on 06/09/25. -// - -import Cocoa import Foundation +import CoreGraphics import Defaults -import CoreAudio -class SystemChangesObserver { - private var oldVolume: Float = 0 - private var oldMuted: Bool = false - private var oldBrightness: Float = 0 - +final class SystemChangesObserver: MediaKeyInterceptorDelegate { private weak var coordinator: DynamicIslandViewCoordinator? - - // Adaptive polling timer - private var pollingTimer: Timer? - private var currentPollingInterval: TimeInterval = 2.0 - - // Polling intervals - private let rapidPollingInterval: TimeInterval = 0.05 // Burst mode for active changes - private let normalPollingInterval: TimeInterval = 2.0 // Normal mode when idle - - // Activity tracking - private var lastBrightnessChangeTime: Date = Date.distantPast - private var lastVolumeChangeTime: Date = Date.distantPast - private var lastVolumeCheckTime: Date = Date.distantPast - private var lastExternalChangeTime: Date = Date.distantPast // Track external volume changes - - // Headphone detection (for info only, doesn't affect polling) - private var hasHeadphonesConnected: Bool = false + private let volumeController = SystemVolumeController.shared + private let brightnessController = SystemBrightnessController.shared + private let mediaKeyInterceptor = MediaKeyInterceptor.shared + + private let volumeStep: Float = 1.0 / 16.0 + private let brightnessStep: Float = 1.0 / 16.0 + + private var volumeEnabled = false + private var brightnessEnabled = false 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 initial display brightness: \(error)") - } - - // Detect headphones on init - updateHeadphoneStatus() - setupAudioRouteChangeListener() } - func startObserving() { - guard Defaults[.enableSystemHUD] else { return } - - // Start with appropriate polling rate - startPollingTimer(interval: determinePollingInterval()) - } - - private func determinePollingInterval() -> TimeInterval { - let now = Date() - - // Burst mode: Rapid polling within 5 seconds of any activity - // Activity = key press OR external change detected - let timeSinceKeyPress = min( - now.timeIntervalSince(lastVolumeChangeTime), - now.timeIntervalSince(lastBrightnessChangeTime) + func startObserving(volumeEnabled: Bool, brightnessEnabled: Bool) { + self.volumeEnabled = volumeEnabled + self.brightnessEnabled = brightnessEnabled + + volumeController.onVolumeChange = { [weak self] volume, muted in + guard let self, self.volumeEnabled else { return } + let value = muted ? 0 : volume + self.sendVolumeNotification(value: value) + } + volumeController.onRouteChange = { [weak self] in + guard let self, self.volumeEnabled else { return } + self.sendVolumeNotification(value: self.volumeController.isMuted ? 0 : self.volumeController.currentVolume) + } + volumeController.start() + + brightnessController.onBrightnessChange = { [weak self] brightness in + guard let self, self.brightnessEnabled else { return } + self.sendBrightnessNotification(value: brightness) + } + brightnessController.start() + + mediaKeyInterceptor.delegate = self + let tapStarted = mediaKeyInterceptor.start() + if !tapStarted { + NSLog("⚠️ Media key interception unavailable; system HUD will remain visible") + } + mediaKeyInterceptor.configuration = MediaKeyConfiguration( + interceptVolume: volumeEnabled, + interceptBrightness: brightnessEnabled ) - let timeSinceExternalChange = now.timeIntervalSince(lastExternalChangeTime) - - // Use burst mode if there's been recent activity (within 5 seconds) - if timeSinceKeyPress < 5.0 || timeSinceExternalChange < 5.0 { - return rapidPollingInterval - } - - // Normal mode: Idle state (regardless of headphones) - return normalPollingInterval - } - - private func startPollingTimer(interval: TimeInterval) { - // Don't restart if same interval - if pollingTimer != nil && currentPollingInterval == interval { - return - } - - currentPollingInterval = interval - pollingTimer?.invalidate() - - pollingTimer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { [weak self] _ in - guard let self = self, Defaults[.enableSystemHUD] else { return } - - // Check if we need to adjust polling rate - let newInterval = self.determinePollingInterval() - if newInterval != self.currentPollingInterval { - self.startPollingTimer(interval: newInterval) - return - } - - // Poll volume - self.checkVolumeChanges() - - // Poll brightness only within 5 seconds of key press - if Date().timeIntervalSince(self.lastBrightnessChangeTime) < 5.0 { - self.checkBrightnessChanges() - } - } - - let mode = interval == rapidPollingInterval ? "BURST (50ms)" : "NORMAL (2s)" - print("βœ… Polling started: \(mode) mode\(hasHeadphonesConnected ? " [🎧 Headphones connected]" : "")") - } - - // MARK: - Headphone Detection - - private func setupAudioRouteChangeListener() { - // Listen for default audio device changes - var propertyAddress = AudioObjectPropertyAddress( - mSelector: kAudioHardwarePropertyDefaultOutputDevice, - mScope: kAudioObjectPropertyScopeGlobal, - mElement: kAudioObjectPropertyElementMain - ) - - AudioObjectAddPropertyListener( - AudioObjectID(kAudioObjectSystemObject), - &propertyAddress, - audioDeviceChangeListener, - Unmanaged.passUnretained(self).toOpaque() - ) - } - - @objc fileprivate func audioRouteChanged() { - updateHeadphoneStatus() - - // Adjust polling rate immediately - startPollingTimer(interval: determinePollingInterval()) - } - - private func updateHeadphoneStatus() { - // Check for headphones/external audio devices - var propertyAddress = AudioObjectPropertyAddress( - mSelector: kAudioHardwarePropertyDefaultOutputDevice, - mScope: kAudioObjectPropertyScopeGlobal, - mElement: kAudioObjectPropertyElementMain - ) - - var deviceID: AudioDeviceID = 0 - var propertySize = UInt32(MemoryLayout.size) - - let status = AudioObjectGetPropertyData( - AudioObjectID(kAudioObjectSystemObject), - &propertyAddress, - 0, - nil, - &propertySize, - &deviceID - ) - - guard status == noErr else { - hasHeadphonesConnected = false - return - } - - // Check if device is not built-in speakers - var transportType: UInt32 = 0 - var transportSize = UInt32(MemoryLayout.size) - var transportAddress = AudioObjectPropertyAddress( - mSelector: kAudioDevicePropertyTransportType, - mScope: kAudioObjectPropertyScopeGlobal, - mElement: kAudioObjectPropertyElementMain - ) - - let transportStatus = AudioObjectGetPropertyData( - deviceID, - &transportAddress, - 0, - nil, - &transportSize, - &transportType - ) - - // Consider headphones if: Bluetooth, USB, or any non-built-in device - let wasConnected = hasHeadphonesConnected - hasHeadphonesConnected = transportStatus == noErr && - transportType != kAudioDeviceTransportTypeBuiltIn - - if wasConnected != hasHeadphonesConnected { - print(hasHeadphonesConnected ? "🎧 Headphones connected (will use burst mode on change detection)" : - "πŸ”Š Headphones disconnected") - } - } - - // MARK: - Public Media Key Event Handlers (called from MediaKeyApplication) - - func handleVolumeKeyEvent() { - // Mark as user-initiated and trigger burst mode - lastVolumeChangeTime = Date() - - // Switch to burst mode immediately - startPollingTimer(interval: rapidPollingInterval) - - DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) { - self.handleVolumeKeyPress() - } - } - - func handleBrightnessKeyEvent() { - // Mark as user-initiated and trigger burst mode - lastBrightnessChangeTime = Date() - - // Switch to burst mode immediately - startPollingTimer(interval: rapidPollingInterval) - - DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) { - self.handleBrightnessKeyPress() - } - } - - // MARK: - Key Press Event Handlers - - private func handleVolumeKeyPress() { - guard Defaults[.enableVolumeHUD] else { return } - - // Mark as user-initiated (don't debounce key presses) - lastVolumeChangeTime = Date() - - 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) [BURST MODE]") - sendBrightnessNotification(value: newBrightness) - - // Update stored value - oldBrightness = newBrightness - } catch { - // Silently ignore brightness errors to reduce log spam - } - } - - // MARK: - Polling Check Methods - - private func checkVolumeChanges() { - guard Defaults[.enableVolumeHUD] else { return } - - // Throttle AppleScript checks based on current mode - let now = Date() - let minInterval: TimeInterval = currentPollingInterval == rapidPollingInterval ? 0.05 : 0.2 - - if now.timeIntervalSince(lastVolumeCheckTime) < minInterval { - return - } - lastVolumeCheckTime = now - - let newVolume = SystemVolumeManager.getOutputVolume() - let newMuted = SystemVolumeManager.isMuted() - - let volumeChanged = !isAlmost(firstNumber: oldVolume, secondNumber: newVolume) - let muteChanged = newMuted != oldMuted - - if volumeChanged || muteChanged { - // Check if this is an external change (not from key press) - let debounceWindow = currentPollingInterval == rapidPollingInterval ? 0.1 : 0.5 - let timeSinceKeyPress = now.timeIntervalSince(lastVolumeChangeTime) - - // External change detected! - if timeSinceKeyPress > debounceWindow { - // Mark it and trigger burst mode - lastExternalChangeTime = now - - let mode = currentPollingInterval == rapidPollingInterval ? "[BURST]" : "[EXTERNALβ†’BURST]" - print("πŸ”Š Volume change detected \(mode): \(oldVolume) β†’ \(newVolume), muted: \(oldMuted) β†’ \(newMuted)") - - // Show HUD immediately - if newMuted { - sendVolumeNotification(value: 0.0) - } else { - sendVolumeNotification(value: newVolume) - } - - // Switch to burst mode immediately if we're in normal mode - if currentPollingInterval == normalPollingInterval { - print("⚑ Switching from NORMAL to BURST mode due to external change") - startPollingTimer(interval: rapidPollingInterval) - } - } else { - // Within debounce window of key press - still update values but don't show HUD - print("πŸ”‡ Volume change within debounce window (\(timeSinceKeyPress)s) - skipping HUD") - } - - oldVolume = newVolume - oldMuted = newMuted - } } - private func checkBrightnessChanges() { - guard Defaults[.enableBrightnessHUD] else { return } - guard NSScreen.screens.count > 0 else { return } - - do { - let newBrightness = try SystemDisplayManager.getDisplayBrightness() - let brightnessChanged = !isAlmost(firstNumber: oldBrightness, secondNumber: newBrightness) - - if brightnessChanged { - print("β˜€οΈ Brightness change detected: \(oldBrightness) β†’ \(newBrightness)") - sendBrightnessNotification(value: newBrightness) - oldBrightness = newBrightness - } - } catch { - // Silently ignore brightness errors to reduce log spam - } + func update(volumeEnabled: Bool, brightnessEnabled: Bool) { + self.volumeEnabled = volumeEnabled + self.brightnessEnabled = brightnessEnabled + mediaKeyInterceptor.configuration = MediaKeyConfiguration( + interceptVolume: volumeEnabled, + interceptBrightness: brightnessEnabled + ) } - - private func isAlmost(firstNumber: Float, secondNumber: Float) -> Bool { - // Use a fixed sensitive threshold for volume changes - // 0.015 = 1.5% change required (sensitive enough for headphone increments) - let threshold: Float = 0.015 - return abs(firstNumber - secondNumber) <= threshold + + func stopObserving() { + mediaKeyInterceptor.stop() + mediaKeyInterceptor.delegate = nil + + volumeController.stop() + volumeController.onVolumeChange = nil + volumeController.onRouteChange = nil + + brightnessController.stop() + brightnessController.onBrightnessChange = nil } - - // MARK: - Internal Notification Methods - + + // MARK: - MediaKeyInterceptorDelegate + + func mediaKeyInterceptor(_ interceptor: MediaKeyInterceptor, didReceiveVolumeCommand direction: MediaKeyDirection, isRepeat: Bool) { + guard volumeEnabled else { return } + let delta = (direction == .up ? volumeStep : -volumeStep) + volumeController.adjust(by: delta) + } + + func mediaKeyInterceptorDidToggleMute(_ interceptor: MediaKeyInterceptor) { + guard volumeEnabled else { return } + volumeController.toggleMute() + } + + func mediaKeyInterceptor(_ interceptor: MediaKeyInterceptor, didReceiveBrightnessCommand direction: MediaKeyDirection, isRepeat: Bool) { + guard brightnessEnabled else { return } + let delta = (direction == .up ? brightnessStep : -brightnessStep) + brightnessController.adjust(by: delta) + } + + // MARK: - HUD Dispatch + private func sendVolumeNotification(value: Float) { coordinator?.toggleSneakPeek( status: true, @@ -350,7 +100,7 @@ class SystemChangesObserver { icon: "" ) } - + private func sendBrightnessNotification(value: Float) { coordinator?.toggleSneakPeek( status: true, @@ -359,52 +109,6 @@ class SystemChangesObserver { icon: "" ) } - - deinit { - // Remove audio device change listener - var propertyAddress = AudioObjectPropertyAddress( - mSelector: kAudioHardwarePropertyDefaultOutputDevice, - mScope: kAudioObjectPropertyScopeGlobal, - mElement: kAudioObjectPropertyElementMain - ) - - AudioObjectRemovePropertyListener( - AudioObjectID(kAudioObjectSystemObject), - &propertyAddress, - audioDeviceChangeListener, - Unmanaged.passUnretained(self).toOpaque() - ) - - stopObserving() - } - - func stopObserving() { - // Clean up polling timer - pollingTimer?.invalidate() - pollingTimer = nil - - print("🎹 Media key detection stopped") - } -} - -// MARK: - CoreAudio Callback for Device Changes - -private func audioDeviceChangeListener( - _ inObjectID: AudioObjectID, - _ inNumberAddresses: UInt32, - _ inAddresses: UnsafePointer, - _ inClientData: UnsafeMutableRawPointer? -) -> OSStatus { - guard let inClientData = inClientData else { return noErr } - - let observer = Unmanaged.fromOpaque(inClientData).takeUnretainedValue() - - // Handle on main thread - DispatchQueue.main.async { - observer.audioRouteChanged() - } - - return noErr } diff --git a/DynamicIsland/managers/SystemHUDManager.swift b/DynamicIsland/managers/SystemHUDManager.swift index cbd9c4f..dc64d38 100644 --- a/DynamicIsland/managers/SystemHUDManager.swift +++ b/DynamicIsland/managers/SystemHUDManager.swift @@ -8,7 +8,6 @@ import Foundation import Defaults import Combine -import AppKit class SystemHUDManager { static let shared = SystemHUDManager() @@ -43,20 +42,20 @@ class SystemHUDManager { guard let self = self, self.isSetupComplete, Defaults[.enableSystemHUD] else { return } - // When volume HUD is enabled, ensure OSD is disabled - if change.newValue { - self.disableSystemHUD() - } + self.changesObserver?.update( + volumeEnabled: change.newValue, + brightnessEnabled: Defaults[.enableBrightnessHUD] + ) }.store(in: &cancellables) Defaults.publisher(.enableBrightnessHUD, options: []).sink { [weak self] change in guard let self = self, self.isSetupComplete, Defaults[.enableSystemHUD] else { return } - // When brightness HUD is enabled, ensure OSD is disabled - if change.newValue { - self.disableSystemHUD() - } + self.changesObserver?.update( + volumeEnabled: Defaults[.enableVolumeHUD], + brightnessEnabled: change.newValue + ) }.store(in: &cancellables) } @@ -88,13 +87,9 @@ class SystemHUDManager { await stopSystemHUD() // Stop any existing observer changesObserver = SystemChangesObserver(coordinator: coordinator) - changesObserver?.startObserving() - - // Disable system HUD if possible - disableSystemHUD() - - // Start event-driven OSD monitoring - startOSDMonitoring() + let volumeEnabled = Defaults[.enableVolumeHUD] + let brightnessEnabled = Defaults[.enableBrightnessHUD] + changesObserver?.startObserving(volumeEnabled: volumeEnabled, brightnessEnabled: brightnessEnabled) print("System HUD replacement started") isSystemOperationInProgress = false @@ -108,120 +103,14 @@ class SystemHUDManager { changesObserver?.stopObserving() changesObserver = nil - // Stop OSD monitoring - stopOSDMonitoring() - - // Re-enable system HUD - enableOriginalSystemHUD() - print("System HUD replacement stopped") isSystemOperationInProgress = false } - private func disableSystemHUD() { - print("πŸ”‡ Disabling system HUD (OSDUIHelper running: \(SystemOSDManager.isOSDUIHelperRunning()))") - // Disable the system HUD using SystemOSDManager - SystemOSDManager.disableSystemHUD() - } - - private func enableOriginalSystemHUD() { - print("πŸ”Š Re-enabling system HUD (OSDUIHelper running: \(SystemOSDManager.isOSDUIHelperRunning()))") - // Re-enable the system HUD using SystemOSDManager - SystemOSDManager.enableSystemHUD() - - // 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() - } - - // MARK: - OSD Monitoring - - /// Start event-driven monitoring for OSDUIHelper process launches - @MainActor - private func startOSDMonitoring() { - stopOSDMonitoring() // Stop any existing observer - - // Listen for process launch notifications - NSWorkspace.shared.notificationCenter.publisher(for: NSWorkspace.didLaunchApplicationNotification) - .compactMap { notification -> NSRunningApplication? in - notification.userInfo?[NSWorkspace.applicationUserInfoKey] as? NSRunningApplication - } - .filter { app in - // Check if OSDUIHelper was launched - app.bundleIdentifier == "com.apple.OSDUIHelper" || - app.localizedName?.contains("OSDUIHelper") == true - } - .sink { [weak self] _ in - guard let self = self else { return } - - // Check if any HUD is enabled - let anyHUDEnabled = Defaults[.enableVolumeHUD] || Defaults[.enableBrightnessHUD] - guard anyHUDEnabled else { return } - - Task { @MainActor in - print("⚠️ OSDUIHelper launched - re-disabling") - self.disableSystemHUD() - } - } - .store(in: &cancellables) - - // Also listen for screen wake events (OSDUIHelper often restarts after wake) - NSWorkspace.shared.notificationCenter.publisher(for: NSWorkspace.screensDidWakeNotification) - .sink { [weak self] _ in - guard let self = self else { return } - - let anyHUDEnabled = Defaults[.enableVolumeHUD] || Defaults[.enableBrightnessHUD] - guard anyHUDEnabled else { return } - - // Check after a short delay to ensure system is ready - Task { @MainActor in - try? await Task.sleep(nanoseconds: 500_000_000) // 0.5 seconds - - let isRunning = await SystemOSDManager.isOSDUIHelperRunningAsync() - if isRunning { - print("⚠️ OSDUIHelper detected running after wake - re-disabling") - self.disableSystemHUD() - } - } - } - .store(in: &cancellables) - - print("βœ… OSD monitoring started (event-driven)") - } - - /// Stop OSD monitoring - @MainActor - private func stopOSDMonitoring() { - // Cancellables are automatically cleaned up - print("πŸ›‘ OSD monitoring stopped") - } - deinit { cancellables.removeAll() Task { @MainActor in await stopSystemHUD() } - // Emergency restore is available via SystemHUDManager.emergencyRestoreSystemHUD() if needed - } - - /// Emergency restore function - call this if system HUD is not working - public static func emergencyRestoreSystemHUD() { - print("🚨 Emergency system HUD restore initiated") - SystemHUDDebugger.forceRestartOSDUIHelper() } } \ No newline at end of file diff --git a/DynamicIsland/managers/SystemMediaControllers.swift b/DynamicIsland/managers/SystemMediaControllers.swift new file mode 100644 index 0000000..1026db7 --- /dev/null +++ b/DynamicIsland/managers/SystemMediaControllers.swift @@ -0,0 +1,348 @@ +import Foundation +import CoreAudio +import IOKit + +extension Notification.Name { + static let systemVolumeDidChange = Notification.Name("DynamicIsland.systemVolumeDidChange") + static let systemBrightnessDidChange = Notification.Name("DynamicIsland.systemBrightnessDidChange") + static let systemAudioRouteDidChange = Notification.Name("DynamicIsland.systemAudioRouteDidChange") +} + +final class SystemVolumeController { + static let shared = SystemVolumeController() + + var onVolumeChange: ((Float, Bool) -> Void)? + var onRouteChange: (() -> Void)? + + private let callbackQueue = DispatchQueue(label: "com.dynamicisland.volume-listener") + private var currentDeviceID: AudioDeviceID = 0 + private var listenersInstalled = false + private var volumeElement: AudioObjectPropertyElement? + private var muteElement: AudioObjectPropertyElement? + + private let candidateElements: [AudioObjectPropertyElement] = [ + kAudioObjectPropertyElementMain, + AudioObjectPropertyElement(1), + AudioObjectPropertyElement(2) + ] + + private init() { + currentDeviceID = resolveDefaultDevice() + refreshPropertyElements() + installDefaultDeviceListener() + installVolumeListeners(for: currentDeviceID) + notifyCurrentState() + } + + func start() { + // Listeners are installed during init, nothing else required. + } + + func stop() { + // We keep listeners alive for the app lifetime; clearing closures prevents UI updates. + onVolumeChange = nil + onRouteChange = nil + } + + func adjust(by delta: Float) { + var newValue = currentVolume + delta + newValue = max(0, min(1, newValue)) + setVolume(newValue) + } + + func toggleMute() { + setMuted(!isMuted) + } + + var currentVolume: Float { + getVolume() + } + + var isMuted: Bool { + getMuteState() + } + + func setVolume(_ value: Float) { + var volume = max(0, min(1, value)) + let status = setData(selector: kAudioDevicePropertyVolumeScalar, data: &volume) + if status != noErr { + NSLog("⚠️ Failed to set volume: \(status)") + } + } + + func setMuted(_ muted: Bool) { + var muteFlag: UInt32 = muted ? 1 : 0 + let status = setData(selector: kAudioDevicePropertyMute, data: &muteFlag) + if status != noErr { + NSLog("⚠️ Failed to set mute state: \(status)") + } + } + + // MARK: - Private + + private func resolveDefaultDevice() -> AudioDeviceID { + var deviceID = AudioDeviceID() + var size = UInt32(MemoryLayout.size(ofValue: deviceID)) + var address = AudioObjectPropertyAddress( + mSelector: kAudioHardwarePropertyDefaultOutputDevice, + mScope: kAudioObjectPropertyScopeGlobal, + mElement: kAudioObjectPropertyElementMain + ) + let status = AudioObjectGetPropertyData(AudioObjectID(kAudioObjectSystemObject), &address, 0, nil, &size, &deviceID) + if status != noErr { + NSLog("⚠️ Unable to fetch default audio device: \(status)") + } + return deviceID + } + + private func installDefaultDeviceListener() { + guard !listenersInstalled else { return } + var address = AudioObjectPropertyAddress( + mSelector: kAudioHardwarePropertyDefaultOutputDevice, + mScope: kAudioObjectPropertyScopeGlobal, + mElement: kAudioObjectPropertyElementMain + ) + let status = AudioObjectAddPropertyListenerBlock( + AudioObjectID(kAudioObjectSystemObject), + &address, + callbackQueue + ) { [weak self] _, _ in + guard let self else { return } + self.handleDefaultDeviceChanged() + } + if status != noErr { + NSLog("⚠️ Failed to install default device listener: \(status)") + } + listenersInstalled = true + } + + private func installVolumeListeners(for deviceID: AudioDeviceID) { + if let element = resolveElement(selector: kAudioDevicePropertyVolumeScalar, deviceID: deviceID) { + volumeElement = element + var address = makeAddress(selector: kAudioDevicePropertyVolumeScalar, element: element) + AudioObjectAddPropertyListenerBlock(deviceID, &address, callbackQueue) { [weak self] _, _ in + self?.notifyCurrentState() + } + } + + if let element = resolveElement(selector: kAudioDevicePropertyMute, deviceID: deviceID) { + muteElement = element + var address = makeAddress(selector: kAudioDevicePropertyMute, element: element) + AudioObjectAddPropertyListenerBlock(deviceID, &address, callbackQueue) { [weak self] _, _ in + self?.notifyCurrentState() + } + } + } + + private func handleDefaultDeviceChanged() { + callbackQueue.async { [weak self] in + guard let self else { return } + self.currentDeviceID = self.resolveDefaultDevice() + self.refreshPropertyElements() + self.installVolumeListeners(for: self.currentDeviceID) + self.notifyCurrentState() + DispatchQueue.main.async { + self.onRouteChange?() + NotificationCenter.default.post(name: .systemAudioRouteDidChange, object: nil) + } + } + } + + private func notifyCurrentState() { + let volume = getVolume() + let muted = getMuteState() + DispatchQueue.main.async { + self.onVolumeChange?(volume, muted) + NotificationCenter.default.post(name: .systemVolumeDidChange, object: nil, userInfo: ["value": volume, "muted": muted]) + } + } + + private func getVolume() -> Float { + var volume = Float32(0) + let status = getData(selector: kAudioDevicePropertyVolumeScalar, data: &volume) + if status != noErr { + NSLog("⚠️ Unable to fetch volume: \(status)") + } + return volume + } + + private func getMuteState() -> Bool { + var mute: UInt32 = 0 + let status = getData(selector: kAudioDevicePropertyMute, data: &mute) + if status != noErr { + // Some devices do not support mute; treat as not muted + return false + } + return mute != 0 + } + + private func refreshPropertyElements() { + volumeElement = resolveElement(selector: kAudioDevicePropertyVolumeScalar, deviceID: currentDeviceID) + muteElement = resolveElement(selector: kAudioDevicePropertyMute, deviceID: currentDeviceID) + } + + private func resolveElement(selector: AudioObjectPropertySelector, deviceID: AudioDeviceID) -> AudioObjectPropertyElement? { + for element in candidateElements { + var address = makeAddress(selector: selector, element: element) + if propertyExists(deviceID: deviceID, address: &address) { + return element + } + } + return nil + } + + private func preferredElements(for selector: AudioObjectPropertySelector) -> [AudioObjectPropertyElement] { + if let cached = cachedElement(for: selector) { + return [cached] + candidateElements.filter { $0 != cached } + } + return candidateElements + } + + private func cachedElement(for selector: AudioObjectPropertySelector) -> AudioObjectPropertyElement? { + switch selector { + case kAudioDevicePropertyVolumeScalar: + return volumeElement + case kAudioDevicePropertyMute: + return muteElement + default: + return nil + } + } + + private func cache(element: AudioObjectPropertyElement, for selector: AudioObjectPropertySelector) { + switch selector { + case kAudioDevicePropertyVolumeScalar: + volumeElement = element + case kAudioDevicePropertyMute: + muteElement = element + default: + break + } + } + + private func makeAddress(selector: AudioObjectPropertySelector, element: AudioObjectPropertyElement) -> AudioObjectPropertyAddress { + AudioObjectPropertyAddress( + mSelector: selector, + mScope: kAudioDevicePropertyScopeOutput, + mElement: element + ) + } + + private func propertyExists(deviceID: AudioDeviceID, address: inout AudioObjectPropertyAddress) -> Bool { + withUnsafePointer(to: &address) { pointer in + AudioObjectHasProperty(deviceID, pointer) + } + } + + private func getData(selector: AudioObjectPropertySelector, data: inout T) -> OSStatus { + var lastStatus: OSStatus = kAudioHardwareUnspecifiedError + for element in preferredElements(for: selector) { + var address = makeAddress(selector: selector, element: element) + guard propertyExists(deviceID: currentDeviceID, address: &address) else { continue } + var size = UInt32(MemoryLayout.size) + lastStatus = AudioObjectGetPropertyData(currentDeviceID, &address, 0, nil, &size, &data) + if lastStatus == noErr { + cache(element: element, for: selector) + return lastStatus + } + } + return lastStatus + } + + private func setData(selector: AudioObjectPropertySelector, data: inout T) -> OSStatus { + var lastStatus: OSStatus = kAudioHardwareUnspecifiedError + for element in preferredElements(for: selector) { + var address = makeAddress(selector: selector, element: element) + guard propertyExists(deviceID: currentDeviceID, address: &address) else { continue } + let size = UInt32(MemoryLayout.size) + lastStatus = AudioObjectSetPropertyData(currentDeviceID, &address, 0, nil, size, &data) + if lastStatus == noErr { + cache(element: element, for: selector) + return lastStatus + } + } + return lastStatus + } +} + +final class SystemBrightnessController { + static let shared = SystemBrightnessController() + + var onBrightnessChange: ((Float) -> Void)? + + private let notificationCenter = NotificationCenter.default + private var observers: [NSObjectProtocol] = [] + private var notificationsInstalled = false + + private init() { + registerExternalNotifications() + } + + func start() { + notifyCurrentBrightness() + } + + func stop() { + onBrightnessChange = nil + } + + func adjust(by delta: Float) { + setBrightness(currentBrightness + delta) + } + + func setBrightness(_ value: Float) { + let clamped = max(0, min(1, value)) + guard let service = displayService() else { return } + IODisplaySetFloatParameter(service, 0, kIODisplayBrightnessKey as CFString, clamped) + IOObjectRelease(service) + notifyCurrentBrightness() + } + + var currentBrightness: Float { + guard let service = displayService() else { return 0.5 } + var brightness: Float = 0 + let result = IODisplayGetFloatParameter(service, 0, kIODisplayBrightnessKey as CFString, &brightness) + IOObjectRelease(service) + if result != kIOReturnSuccess { + return 0.5 + } + return brightness + } + + private func notifyCurrentBrightness() { + let brightness = currentBrightness + DispatchQueue.main.async { + self.onBrightnessChange?(brightness) + self.notificationCenter.post(name: .systemBrightnessDidChange, object: nil, userInfo: ["value": brightness]) + } + } + + private func displayService() -> io_service_t? { + var iterator = io_iterator_t() + guard IOServiceGetMatchingServices(kIOMainPortDefault, IOServiceMatching("IODisplayConnect"), &iterator) == KERN_SUCCESS else { + return nil + } + let service = IOIteratorNext(iterator) + IOObjectRelease(iterator) + return service + } + + private func registerExternalNotifications() { + guard !notificationsInstalled else { return } + let names = [ + Notification.Name("com.apple.BezelEngine.BrightnessChanged"), + Notification.Name("com.apple.BezelServices.BrightnessChanged"), + Notification.Name("com.apple.controlcenter.display.brightness") + ] + observers = names.map { name in + DistributedNotificationCenter.default().addObserver(forName: name, object: nil, queue: .main) { [weak self] _ in + self?.notifyCurrentBrightness() + } + } + notificationsInstalled = true + } + + deinit { + observers.forEach { DistributedNotificationCenter.default().removeObserver($0) } + } +} From e4d9653f81c74be6824dc477749ec8a8b2b080cc Mon Sep 17 00:00:00 2001 From: Hariharan Mudaliar Date: Sat, 18 Oct 2025 13:41:07 +0530 Subject: [PATCH 2/6] Working brightness huds --- .../managers/SystemMediaControllers.swift | 53 ++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/DynamicIsland/managers/SystemMediaControllers.swift b/DynamicIsland/managers/SystemMediaControllers.swift index 1026db7..a5add30 100644 --- a/DynamicIsland/managers/SystemMediaControllers.swift +++ b/DynamicIsland/managers/SystemMediaControllers.swift @@ -1,5 +1,6 @@ import Foundation import CoreAudio +import CoreGraphics import IOKit extension Notification.Name { @@ -273,6 +274,7 @@ final class SystemBrightnessController { private let notificationCenter = NotificationCenter.default private var observers: [NSObjectProtocol] = [] private var notificationsInstalled = false + private var displayID: CGDirectDisplayID = CGMainDisplayID() private init() { registerExternalNotifications() @@ -292,13 +294,24 @@ final class SystemBrightnessController { func setBrightness(_ value: Float) { let clamped = max(0, min(1, value)) + if setBrightnessViaDisplayServices(clamped) { + notifyCurrentBrightness() + return + } guard let service = displayService() else { return } - IODisplaySetFloatParameter(service, 0, kIODisplayBrightnessKey as CFString, clamped) + let status = IODisplaySetFloatParameter(service, 0, kIODisplayBrightnessKey as CFString, clamped) IOObjectRelease(service) - notifyCurrentBrightness() + if status == kIOReturnSuccess { + notifyCurrentBrightness() + } else { + NSLog("⚠️ Failed to set brightness via IODisplay: \(status)") + } } var currentBrightness: Float { + if let level = getBrightnessViaDisplayServices() { + return level + } guard let service = displayService() else { return 0.5 } var brightness: Float = 0 let result = IODisplayGetFloatParameter(service, 0, kIODisplayBrightnessKey as CFString, &brightness) @@ -327,6 +340,36 @@ final class SystemBrightnessController { return service } + private func setBrightnessViaDisplayServices(_ value: Float) -> Bool { + let status = DisplayServicesSetBrightness(displayID, value) + if status == kIOReturnSuccess { + return true + } + // Attempt to refresh display ID in case the main display changed + displayID = CGMainDisplayID() + let retry = DisplayServicesSetBrightness(displayID, value) + if retry != kIOReturnSuccess { + NSLog("⚠️ DisplayServicesSetBrightness failed: \(retry)") + return false + } + return true + } + + private func getBrightnessViaDisplayServices() -> Float? { + var brightness: Float = 0 + let status = DisplayServicesGetBrightness(displayID, &brightness) + if status == kIOReturnSuccess { + return brightness + } + displayID = CGMainDisplayID() + let retry = DisplayServicesGetBrightness(displayID, &brightness) + if retry == kIOReturnSuccess { + return brightness + } + NSLog("⚠️ DisplayServicesGetBrightness failed: \(retry)") + return nil + } + private func registerExternalNotifications() { guard !notificationsInstalled else { return } let names = [ @@ -346,3 +389,9 @@ final class SystemBrightnessController { observers.forEach { DistributedNotificationCenter.default().removeObserver($0) } } } + +@_silgen_name("DisplayServicesGetBrightness") +private func DisplayServicesGetBrightness(_ display: UInt32, _ brightness: UnsafeMutablePointer) -> Int32 + +@_silgen_name("DisplayServicesSetBrightness") +private func DisplayServicesSetBrightness(_ display: UInt32, _ brightness: Float) -> Int32 From e55f6a9a524011cef20573a8919ea619c3dbe820 Mon Sep 17 00:00:00 2001 From: Hariharan Mudaliar Date: Sat, 18 Oct 2025 14:29:46 +0530 Subject: [PATCH 3/6] Working both - without disabling OSDUIHelper --- DynamicIsland.xcodeproj/project.pbxproj | 8 +- .../xcschemes/DynamicIsland.xcscheme | 82 +++++++++++++++++++ 2 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 DynamicIsland.xcodeproj/xcshareddata/xcschemes/DynamicIsland.xcscheme diff --git a/DynamicIsland.xcodeproj/project.pbxproj b/DynamicIsland.xcodeproj/project.pbxproj index 2a8b3eb..a587c08 100644 --- a/DynamicIsland.xcodeproj/project.pbxproj +++ b/DynamicIsland.xcodeproj/project.pbxproj @@ -379,12 +379,13 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = DynamicIsland/DynamicIsland.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"DynamicIsland/Preview Content\""; - DEVELOPMENT_TEAM = 9Y64TRM77N; + DEVELOPMENT_TEAM = ""; ENABLE_APP_SANDBOX = NO; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; @@ -410,6 +411,7 @@ MARKETING_VERSION = "1.0.0-beta"; PRODUCT_BUNDLE_IDENTIFIER = com.karthikinformationtechnology.DynamicIsland; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; SYSTEM_FRAMEWORK_SEARCH_PATHS = ( @@ -425,12 +427,13 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = DynamicIsland/DynamicIsland.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"DynamicIsland/Preview Content\""; - DEVELOPMENT_TEAM = 9Y64TRM77N; + DEVELOPMENT_TEAM = ""; ENABLE_APP_SANDBOX = NO; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; @@ -456,6 +459,7 @@ MARKETING_VERSION = "1.0.0-beta"; PRODUCT_BUNDLE_IDENTIFIER = com.karthikinformationtechnology.DynamicIsland; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; SYSTEM_FRAMEWORK_SEARCH_PATHS = ( diff --git a/DynamicIsland.xcodeproj/xcshareddata/xcschemes/DynamicIsland.xcscheme b/DynamicIsland.xcodeproj/xcshareddata/xcschemes/DynamicIsland.xcscheme new file mode 100644 index 0000000..9929882 --- /dev/null +++ b/DynamicIsland.xcodeproj/xcshareddata/xcschemes/DynamicIsland.xcscheme @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From d35dbe58db6cce3c217999d77aa8aaa96860f699 Mon Sep 17 00:00:00 2001 From: Hariharan Mudaliar Date: Sat, 18 Oct 2025 22:07:07 +0530 Subject: [PATCH 4/6] Modify gitignore --- .gitignore | 56 +++++++++++++ .../xcschemes/DynamicIsland.xcscheme | 82 ------------------- 2 files changed, 56 insertions(+), 82 deletions(-) delete mode 100644 DynamicIsland.xcodeproj/xcshareddata/xcschemes/DynamicIsland.xcscheme diff --git a/.gitignore b/.gitignore index 8a5e361..a3971b1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,60 @@ .DS_Store +# Xcode +*.xcuserstate +*.xcuserdata +*.xcscheme +*.xcuserdatad +*.pbxuser +*.xccheckout +*.xcscheme +*.xcplayground +*.xcuserdatad +*.xctest +*.xcuserdata +*.xcodeproj/xcshareddata/WorkspaceSettings.xcsettings + +# CocoaPods +Pods/ +podfile.lock + +# Carthage +Carthage/Build/ + +# Swift Package Manager +.swiftpm/ +.build/ + +# Derived data +DerivedData/ + +# Build output +build/ +*.app +*.dSYM + +# Temporary files +*.swp +*.swo +*.tmp +*.log + +# Other +*.DS_Store +*.vscode/ +*.idea/ + +# User-specific files +*.xcuserdatad/ +*.xcscheme +*.xcodeproj/xcuserdata +*.xcodeproj/project.xcuserdata + +# Build artifacts +*.ipa +*.xcarchive +*.dSYM + + # Xcode # build/ diff --git a/DynamicIsland.xcodeproj/xcshareddata/xcschemes/DynamicIsland.xcscheme b/DynamicIsland.xcodeproj/xcshareddata/xcschemes/DynamicIsland.xcscheme deleted file mode 100644 index 9929882..0000000 --- a/DynamicIsland.xcodeproj/xcshareddata/xcschemes/DynamicIsland.xcscheme +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From cf29960be9b8c246a343e2d894876f0b910b0213 Mon Sep 17 00:00:00 2001 From: Hariharan Mudaliar Date: Sat, 18 Oct 2025 22:10:02 +0530 Subject: [PATCH 5/6] Update xcschareddata --- .../xcschemes/DynamicIsland.xcscheme | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 DynamicIsland.xcodeproj/xcshareddata/xcschemes/DynamicIsland.xcscheme diff --git a/DynamicIsland.xcodeproj/xcshareddata/xcschemes/DynamicIsland.xcscheme b/DynamicIsland.xcodeproj/xcshareddata/xcschemes/DynamicIsland.xcscheme new file mode 100644 index 0000000..9b2fc4e --- /dev/null +++ b/DynamicIsland.xcodeproj/xcshareddata/xcschemes/DynamicIsland.xcscheme @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From e67c7eb7b21e52d3681ed3380f9f2e7a06d3e98e Mon Sep 17 00:00:00 2001 From: Hariharan Mudaliar Date: Sat, 18 Oct 2025 22:10:35 +0530 Subject: [PATCH 6/6] Modify gitignore --- .gitignore | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitignore b/.gitignore index a3971b1..96a6a4e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,11 +2,9 @@ # Xcode *.xcuserstate *.xcuserdata -*.xcscheme *.xcuserdatad *.pbxuser *.xccheckout -*.xcscheme *.xcplayground *.xcuserdatad *.xctest @@ -45,7 +43,6 @@ build/ # User-specific files *.xcuserdatad/ -*.xcscheme *.xcodeproj/xcuserdata *.xcodeproj/project.xcuserdata