Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 43 additions & 80 deletions boringNotch/XPCHelperClient/XPCHelperClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@ import Foundation
import Cocoa
import AsyncXPCConnection

@MainActor
final class XPCHelperClient: NSObject {
nonisolated static let shared = XPCHelperClient()

nonisolated private override init() {
super.init()
}

private let serviceName = "theboringteam.boringnotch.BoringNotchXPCHelper"

private var remoteService: RemoteXPCService<BoringNotchXPCHelperProtocol>?
Expand All @@ -14,14 +19,8 @@ final class XPCHelperClient: NSObject {
private var lunarListener: BoringNotchXPCHelperLunarListener?
private var hasLunarListener: Bool = false

deinit {
connection?.invalidate()
stopMonitoringAccessibilityAuthorization()
}

// MARK: - Connection Management (Main Actor Isolated)

@MainActor
private func ensureRemoteService(needsListener: Bool = false) -> RemoteXPCService<BoringNotchXPCHelperProtocol> {
if let existing = remoteService, (!needsListener || hasLunarListener) {
return existing
Expand Down Expand Up @@ -72,7 +71,6 @@ final class XPCHelperClient: NSObject {
return service
}

@MainActor
private func getRemoteService() -> RemoteXPCService<BoringNotchXPCHelperProtocol>? {
remoteService
}
Expand All @@ -88,7 +86,6 @@ final class XPCHelperClient: NSObject {
return interface
}

@MainActor
private func notifyAuthorizationChange(_ granted: Bool) {
guard lastKnownAuthorization != granted else { return }
lastKnownAuthorization = granted
Expand All @@ -100,10 +97,10 @@ final class XPCHelperClient: NSObject {
}

// MARK: - Monitoring
nonisolated func startMonitoringAccessibilityAuthorization(every interval: TimeInterval = 3.0) {
func startMonitoringAccessibilityAuthorization(every interval: TimeInterval = 3.0) {
// Ensure only one monitor exists
stopMonitoringAccessibilityAuthorization()
monitoringTask = Task.detached { [weak self] in
monitoringTask = Task { [weak self] in
guard let self = self else { return }
while !Task.isCancelled {
// Call the helper method periodically which will notify on change
Expand All @@ -115,7 +112,7 @@ final class XPCHelperClient: NSObject {
}
}

nonisolated func stopMonitoringAccessibilityAuthorization() {
func stopMonitoringAccessibilityAuthorization() {
monitoringTask?.cancel()
monitoringTask = nil
}
Expand All @@ -127,49 +124,41 @@ final class XPCHelperClient: NSObject {

// MARK: - Accessibility

// Fire-and-forget: callers invoke this from non-isolated contexts, and the work
// itself hops onto the main actor.
nonisolated func requestAccessibilityAuthorization() {
Task {
let service = await MainActor.run {
ensureRemoteService()
}
Task { @MainActor in
let service = ensureRemoteService()
try? await service.withService { service in
service.requestAccessibilityAuthorization()
}
}
}

nonisolated func isAccessibilityAuthorized() async -> Bool {
func isAccessibilityAuthorized() async -> Bool {
do {
let service = await MainActor.run {
ensureRemoteService()
}
let service = ensureRemoteService()
let result: Bool = try await service.withContinuation { service, continuation in
service.isAccessibilityAuthorized { authorized in
continuation.resume(returning: authorized)
}
}
await MainActor.run {
notifyAuthorizationChange(result)
}
notifyAuthorizationChange(result)
return result
} catch {
return false
}
}

nonisolated func ensureAccessibilityAuthorization(promptIfNeeded: Bool) async -> Bool {
func ensureAccessibilityAuthorization(promptIfNeeded: Bool) async -> Bool {
do {
let service = await MainActor.run {
ensureRemoteService()
}
let service = ensureRemoteService()
let result: Bool = try await service.withContinuation { service, continuation in
service.ensureAccessibilityAuthorization(promptIfNeeded) { authorized in
continuation.resume(returning: authorized)
}
}
await MainActor.run {
notifyAuthorizationChange(result)
}
notifyAuthorizationChange(result)
return result
} catch {
return false
Expand All @@ -178,11 +167,9 @@ final class XPCHelperClient: NSObject {

// MARK: - Keyboard Brightness

nonisolated func isKeyboardBrightnessAvailable() async -> Bool {
func isKeyboardBrightnessAvailable() async -> Bool {
do {
let service = await MainActor.run {
ensureRemoteService()
}
let service = ensureRemoteService()
return try await service.withContinuation { service, continuation in
service.isKeyboardBrightnessAvailable { available in
continuation.resume(returning: available)
Expand All @@ -193,11 +180,9 @@ final class XPCHelperClient: NSObject {
}
}

nonisolated func currentKeyboardBrightness() async -> Float? {
func currentKeyboardBrightness() async -> Float? {
do {
let service = await MainActor.run {
ensureRemoteService()
}
let service = ensureRemoteService()
let result: NSNumber? = try await service.withContinuation { service, continuation in
service.currentKeyboardBrightness { value in
continuation.resume(returning: value)
Expand All @@ -209,11 +194,9 @@ final class XPCHelperClient: NSObject {
}
}

nonisolated func setKeyboardBrightness(_ value: Float) async -> Bool {
func setKeyboardBrightness(_ value: Float) async -> Bool {
do {
let service = await MainActor.run {
ensureRemoteService()
}
let service = ensureRemoteService()
return try await service.withContinuation { service, continuation in
service.setKeyboardBrightness(value) { success in
continuation.resume(returning: success)
Expand All @@ -226,11 +209,9 @@ final class XPCHelperClient: NSObject {

// MARK: - Screen Brightness

nonisolated func isScreenBrightnessAvailable() async -> Bool {
func isScreenBrightnessAvailable() async -> Bool {
do {
let service = await MainActor.run {
ensureRemoteService()
}
let service = ensureRemoteService()
return try await service.withContinuation { service, continuation in
service.isScreenBrightnessAvailable { available in
continuation.resume(returning: available)
Expand All @@ -241,11 +222,9 @@ final class XPCHelperClient: NSObject {
}
}

nonisolated func currentScreenBrightness() async -> Float? {
func currentScreenBrightness() async -> Float? {
do {
let service = await MainActor.run {
ensureRemoteService()
}
let service = ensureRemoteService()
let result: NSNumber? = try await service.withContinuation { service, continuation in
service.currentScreenBrightness { value in
continuation.resume(returning: value)
Expand All @@ -257,11 +236,9 @@ final class XPCHelperClient: NSObject {
}
}

nonisolated func displayIDForBrightness() async -> CGDirectDisplayID? {
func displayIDForBrightness() async -> CGDirectDisplayID? {
do {
let service = await MainActor.run {
ensureRemoteService()
}
let service = ensureRemoteService()
let result: NSNumber? = try await service.withContinuation { service, continuation in
service.displayIDForBrightness(with: { value in
continuation.resume(returning: value)
Expand All @@ -274,11 +251,9 @@ final class XPCHelperClient: NSObject {
}
}

nonisolated func setScreenBrightness(_ value: Float) async -> Bool {
func setScreenBrightness(_ value: Float) async -> Bool {
do {
let service = await MainActor.run {
ensureRemoteService()
}
let service = ensureRemoteService()
return try await service.withContinuation { service, continuation in
service.setScreenBrightness(value) { success in
continuation.resume(returning: success)
Expand All @@ -288,11 +263,9 @@ final class XPCHelperClient: NSObject {
return false
}
}
nonisolated func adjustScreenBrightness(by value: Float) async -> Bool {
func adjustScreenBrightness(by value: Float) async -> Bool {
do {
let service = await MainActor.run {
ensureRemoteService()
}
let service = ensureRemoteService()
return try await service.withContinuation { service, continuation in
service.adjustScreenBrightness(by: value) { success in
continuation.resume(returning: success)
Expand All @@ -305,11 +278,9 @@ final class XPCHelperClient: NSObject {

// MARK: - Lunar Events

nonisolated func isLunarAvailable() async -> Bool {
func isLunarAvailable() async -> Bool {
do {
let service = await MainActor.run {
ensureRemoteService()
}
let service = ensureRemoteService()
return try await service.withContinuation { service, continuation in
service.isLunarAvailable { available in
continuation.resume(returning: available)
Expand All @@ -320,14 +291,10 @@ final class XPCHelperClient: NSObject {
}
}

nonisolated func startLunarEventStream(listener: BoringNotchXPCHelperLunarListener) async -> Bool {
await MainActor.run {
lunarListener = listener
}
func startLunarEventStream(listener: BoringNotchXPCHelperLunarListener) async -> Bool {
lunarListener = listener
do {
let service = await MainActor.run {
ensureRemoteService(needsListener: true)
}
let service = ensureRemoteService(needsListener: true)
return try await service.withContinuation { service, continuation in
service.startLunarEventStream { started in
continuation.resume(returning: started)
Expand All @@ -338,11 +305,9 @@ final class XPCHelperClient: NSObject {
}
}

nonisolated func stopLunarEventStream() async {
func stopLunarEventStream() async {
do {
let service = await MainActor.run {
ensureRemoteService(needsListener: true)
}
let service = ensureRemoteService(needsListener: true)
try await service.withService { service in
service.stopLunarEventStream()
}
Expand All @@ -351,11 +316,9 @@ final class XPCHelperClient: NSObject {
}
}

nonisolated func setLunarOSDHidden(_ hide: Bool) async -> Bool {
func setLunarOSDHidden(_ hide: Bool) async -> Bool {
do {
let service = await MainActor.run {
ensureRemoteService()
}
let service = ensureRemoteService()
return try await service.withContinuation { service, continuation in
service.setLunarOSDHidden(hide) { ok in
continuation.resume(returning: ok)
Expand Down
Loading