Skip to content

Commit

Permalink
Add logic responsible for supporting machines on which high power mod…
Browse files Browse the repository at this point in the history
…e is not available
  • Loading branch information
rurza committed Nov 21, 2024
1 parent 047aa43 commit 4edd63a
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 58 deletions.
17 changes: 13 additions & 4 deletions BatFiKit/Sources/App/App.swift
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,8 @@ public final class BatFi: StatusItemManagerDelegate, HelperConnectionManagerDele
KeyboardShortcuts.onKeyUp(for: .toggleLowPowerMode) { [weak self] in
guard let self else { return }
Task {
guard let mode = try? await self.powerModeClient.getCurrentPowerMode() else { return }
if mode != .low {
guard let result = try? await self.powerModeClient.getCurrentPowerMode() else { return }
if result.0 != .low {
do {
try await self.powerModeClient.setPowerMode(.low)
try? await self.userNotificationsClient.showUserNotification(
Expand Down Expand Up @@ -243,8 +243,17 @@ public final class BatFi: StatusItemManagerDelegate, HelperConnectionManagerDele
KeyboardShortcuts.onKeyUp(for: .toggleHighPowerMode) { [weak self] in
guard let self else { return }
Task {
guard let mode = try? await self.powerModeClient.getCurrentPowerMode() else { return }
if mode != .high {
guard let result = try? await self.powerModeClient.getCurrentPowerMode(), result.1 else {
try? await self.userNotificationsClient.showUserNotification(
title: L10n.Notifications.Notification.Title.highPowerModeUnsupported,
body: "",
identifier: "high",
threadIdentifier: "powermode",
delay: nil
)
return
}
if result.0 != .high {
do {
try await self.powerModeClient.setPowerMode(.high)
try? await self.userNotificationsClient.showUserNotification(
Expand Down
32 changes: 21 additions & 11 deletions BatFiKit/Sources/AppCore/StatusItemManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public final class StatusItemManager {
private var powerModeTask: Task<Void, Never>?
@Published
private var lastPowerMode: PowerMode?
private var showHighPowerMode = false
private let menuDelegate = MenuObserver.shared
private let batteryInfoModel = BatteryInfoViewModel()

Expand Down Expand Up @@ -102,7 +103,10 @@ public final class StatusItemManager {
private func observeMenuState() {
menuStateTask = Task { [weak self] in
guard let self else { return }
self.lastPowerMode = try? await self.powerModeClient.getCurrentPowerMode()
if let result = try? await self.powerModeClient.getCurrentPowerMode() {
self.lastPowerMode = result.0
self.showHighPowerMode = result.1
}
for await ((state, showDebugMenu, showPowerModeOptions), (showChart, showPowerDiagram, showHighEnergyImpactProcesses), powerMode) in combineLatest(
combineLatest(
appChargingState.appChargingModeDidChage(),
Expand Down Expand Up @@ -137,9 +141,13 @@ public final class StatusItemManager {
powerModeTask = Task { [weak self] in
guard let self else { return }
while !Task.isCancelled {
let mode = try? await self.powerModeClient.getCurrentPowerMode()
if mode != self.lastPowerMode {
self.lastPowerMode = mode
if let result = try? await self.powerModeClient.getCurrentPowerMode() {
if result.0 != self.lastPowerMode {
self.lastPowerMode = result.0
}
if result.1 != self.showHighPowerMode {
self.showHighPowerMode = result.1
}
}
try? await self.clock.sleep(for: .seconds(1), tolerance: .milliseconds(50))
}
Expand Down Expand Up @@ -230,14 +238,16 @@ public final class StatusItemManager {
}
}
.state(dependencies.powerMode == .normal ? .on : .off)
MenuItem(L10n.Menu.Label.highPowerMode)
.onSelect { [weak self] in
Task {
self?.lastPowerMode = .high
try? await self?.powerModeClient.setPowerMode(.high)
if showHighPowerMode {
MenuItem(L10n.Menu.Label.highPowerMode)
.onSelect { [weak self] in
Task {
self?.lastPowerMode = .high
try? await self?.powerModeClient.setPowerMode(.high)
}
}
}
.state(dependencies.powerMode == .high ? .on : .off)
.state(dependencies.powerMode == .high ? .on : .off)
}
}

SeparatorItem()
Expand Down
4 changes: 2 additions & 2 deletions BatFiKit/Sources/Clients/PowerModeClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ public enum PowerModeClientError: Error {
}

public struct PowerModeClient: TestDependencyKey {
public var getCurrentPowerMode: () async throws -> PowerMode
public var getCurrentPowerMode: () async throws -> (PowerMode, Bool)
public var setPowerMode: (PowerMode) async throws -> Void

public init(
getCurrentPowerMode: @escaping () async throws -> PowerMode,
getCurrentPowerMode: @escaping () async throws -> (PowerMode, Bool),
setPowerMode: @escaping (PowerMode) async throws -> Void
) {
self.getCurrentPowerMode = getCurrentPowerMode
Expand Down
4 changes: 2 additions & 2 deletions BatFiKit/Sources/ClientsLive/PowerModeClient+Live.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ extension PowerModeClient: DependencyKey {
let xpcClient = XPCClient.shared
return Self(
getCurrentPowerMode: {
let uint = try await xpcClient.getPowerMode()
let (uint, highPowerModeIsAvailable) = try await xpcClient.getPowerMode()
if let mode = PowerMode(uint: uint) {
return mode
return (mode, highPowerModeIsAvailable)
} else {
throw PowerModeClientError.unsupportedMode
}
Expand Down
6 changes: 3 additions & 3 deletions BatFiKit/Sources/ClientsLive/XPCClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -139,13 +139,13 @@ actor XPCClient {
}
}

func getPowerMode() async throws -> UInt8 {
func getPowerMode() async throws -> (UInt8, Bool) {
logger.debug("Getting power mode")
let remote = newRemoteService()
return try await remote.withContinuation { service, continuation in
service.currentPowerMode { mode in
service.currentPowerMode { mode, highPowerModeIsAvailable in
if let uint = mode?.uint8Value {
continuation.resume(returning: uint)
continuation.resume(returning: (uint, highPowerModeIsAvailable))
} else {
continuation.resume(throwing: XPCClientError.canNotGetPowerMode)
}
Expand Down
11 changes: 11 additions & 0 deletions BatFiKit/Sources/L10n/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -6802,6 +6802,17 @@
}
}
},
"notifications.notification.title.high_power_mode_unsupported" : {
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "High power mode is not supported"
}
}
}
},
"notifications.notification.title.low_battery" : {
"extractionState" : "extracted_with_value",
"localizations" : {
Expand Down
1 change: 1 addition & 0 deletions BatFiKit/Sources/L10n/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ public enum L10n {
public static let lowPowerModeOn = String(localized: "notifications.notification.title.low_power_mode_on", defaultValue: "Low power mode is on", bundle: Bundle.module)
public static let highPowerModeOn = String(localized: "notifications.notification.title.high_power_mode_on", defaultValue: "High power mode is on", bundle: Bundle.module)
public static let automaticPowerModeOn = String(localized: "notifications.notification.title.automatic_power_mode_on", defaultValue: "Automatic power mode is on", bundle: Bundle.module)
public static let highPowerModeUnsupported = String(localized: "notifications.notification.title.high_power_mode_unsupported", defaultValue: "High power mode is not supported", bundle: Bundle.module)
}

public enum Body {
Expand Down
88 changes: 53 additions & 35 deletions BatFiKit/Sources/Server/Listener.swift
Original file line number Diff line number Diff line change
Expand Up @@ -126,49 +126,67 @@ final class XPCServiceHandler: XPCService {
}
}

func currentPowerMode(_ handler: @escaping (NSNumber?) -> Void) {
func currentPowerMode(_ handler: @escaping (NSNumber?, Bool) -> Void) {
Task {
let pmsetProcess = Process()
let grepProcess = Process()
let pipe1 = Pipe()
let pipe2 = Pipe()

// Configure the `pmset` process
pmsetProcess.launchPath = "/usr/bin/pmset"
pmsetProcess.arguments = ["-g"]
pmsetProcess.standardOutput = pipe1

// Configure the `grep` process
grepProcess.launchPath = "/usr/bin/grep"
grepProcess.arguments = ["powermode"]
grepProcess.standardInput = pipe1
grepProcess.standardOutput = pipe2

// Launch the processes
pmsetProcess.launch()
grepProcess.launch()

// Wait for the processes to complete
pmsetProcess.waitUntilExit()
grepProcess.waitUntilExit()

// Read the output from the `grep` process
let outputData = pipe2.fileHandleForReading.readDataToEndOfFile()
guard let output = String(data: outputData, encoding: .utf8) else {
handler(nil)
return
}

func parsePowerMode(output: String) -> UInt8? {
// Extract the value by trimming spaces and suffixing the last character
let trimmedOutput = output.trimmingCharacters(in: .whitespacesAndNewlines)
if let lastSpaceIndex = trimmedOutput.lastIndex(of: " ") {
let valueStartIndex = trimmedOutput.index(after: lastSpaceIndex)
if let uint = UInt8(trimmedOutput[valueStartIndex...]) {
handler(NSNumber(value: uint))
return
return uint
}
}
handler(nil)
return nil
}
let pmsetProcess = Process()
let grepProcess = Process()
let pipe1 = Pipe()
let pipe2 = Pipe()

// Configure the `pmset` process
pmsetProcess.launchPath = "/usr/bin/pmset"
pmsetProcess.arguments = ["-g"]
pmsetProcess.standardOutput = pipe1

// Configure the `grep` process
grepProcess.launchPath = "/usr/bin/grep"
grepProcess.arguments = ["powermode"]
grepProcess.standardInput = pipe1
grepProcess.standardOutput = pipe2

// Launch the processes
pmsetProcess.launch()
grepProcess.launch()

// Wait for the processes to complete
pmsetProcess.waitUntilExit()
grepProcess.waitUntilExit()

// Read the output from the `grep` process
let outputData = pipe2.fileHandleForReading.readDataToEndOfFile()
guard let output = String(data: outputData, encoding: .utf8) else {
let anotherGrepProcess = Process()
let pipe3 = Pipe()
// Configure the `grep` process
anotherGrepProcess.launchPath = "/usr/bin/grep"
anotherGrepProcess.arguments = ["lowpowermode"]
anotherGrepProcess.standardInput = pipe1
anotherGrepProcess.standardOutput = pipe3
let outputData = pipe3.fileHandleForReading.readDataToEndOfFile()
guard let output = String(data: outputData, encoding: .utf8),
let result = parsePowerMode(output: output) else {
handler(nil, false)
return
}
handler(NSNumber(value: result), false)
return
}
guard let result = parsePowerMode(output: output) else {
handler(nil, false)
return
}
handler(NSNumber(value: result), true)
}
}

Expand Down
3 changes: 2 additions & 1 deletion BatFiKit/Sources/Shared/XPCService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ public protocol XPCService {
func ping(_ handler: @escaping (Bool, Error?) -> Void)
func quit(_ handler: @escaping (Bool, Error?) -> Void)
func turnPowerMode(_ mode: UInt8, _ handler: @escaping (Error?) -> Void)
func currentPowerMode(_ handler: @escaping (NSNumber?) -> Void)
/// Boolean is telling us if the high power mode is available
func currentPowerMode(_ handler: @escaping (NSNumber?, Bool) -> Void)
}

0 comments on commit 4edd63a

Please sign in to comment.