From ca00510030362440f7904833e46739bb427a6f71 Mon Sep 17 00:00:00 2001 From: Serhiy Mytrovtsiy Date: Sat, 22 Jan 2022 16:37:46 +0100 Subject: [PATCH] feat: rewritten Battery top processes reader (#781) --- Modules/Battery/readers.swift | 135 ++++++++++++++-------------------- 1 file changed, 55 insertions(+), 80 deletions(-) diff --git a/Modules/Battery/readers.swift b/Modules/Battery/readers.swift index 57c58659..115592b8 100644 --- a/Modules/Battery/readers.swift +++ b/Modules/Battery/readers.swift @@ -142,99 +142,74 @@ internal class UsageReader: Reader { } public class ProcessReader: Reader<[TopProcess]> { - private let title: String = "Battery" - - private var task: Process = Process() - private var initialized: Bool = false - private var paused: Bool = false - private var initRead: Bool = false - private var numberOfProcesses: Int { get { - return Store.shared.int(key: "\(self.title)_processes", defaultValue: 8) + return Store.shared.int(key: "Battery_processes", defaultValue: 8) } } public override func setup() { self.popup = true - - let pipe = Pipe() - - self.task.standardOutput = pipe - self.task.launchPath = "/usr/bin/top" - self.task.arguments = ["-o", "power", "-n", "\(self.numberOfProcesses)", "-stats", "pid,command,power"] - - pipe.fileHandleForReading.readabilityHandler = { (fileHandle) -> Void in - let output = String(decoding: fileHandle.availableData, as: UTF8.self) - var processes: [TopProcess] = [] - - output.enumerateLines { (line, _) -> Void in - if line.matches("^\\d* +.+ \\d*.?\\d*$") { - var str = line.trimmingCharacters(in: .whitespaces) - - if self.paused { - return - } - - let pidString = str.findAndCrop(pattern: "^\\d+") - let usageString = str.findAndCrop(pattern: " +[0-9]+.*[0-9]*$") - let command = str.trimmingCharacters(in: .whitespaces) - - let pid = Int(pidString) ?? 0 - guard let usage = Double(usageString.filter("01234567890.".contains)) else { - return - } - - var name: String? = nil - var icon: NSImage? = nil - if let app = NSRunningApplication(processIdentifier: pid_t(pid) ) { - name = app.localizedName ?? nil - icon = app.icon - } - - processes.append(TopProcess(pid: pid, command: command, name: name, usage: usage, icon: icon)) - } - } - - if !processes.isEmpty { - self.callback(processes.prefix(self.numberOfProcesses).reversed().reversed()) - } - - if !self.initRead { - self.pause() - self.initRead = true - } - } } - public override func start() { - if !self.initialized { - self.task.launch() - self.initialized = true + public override func read() { + if self.numberOfProcesses == 0 { return } - self.initRead = true - if !self.task.isRunning { - do { - try self.task.run() - } catch let error { - debug("run Battery process reader \(error)", log: self.log) + let task = Process() + task.launchPath = "/bin/ps" + task.launchPath = "/usr/bin/top" + task.arguments = ["-o", "power", "-l", "2", "-n", "\(self.numberOfProcesses)", "-stats", "pid,command,power"] + + let outputPipe = Pipe() + defer { + outputPipe.fileHandleForReading.closeFile() + } + task.standardOutput = outputPipe + + do { + try task.run() + } catch let err { + error("error read ps: \(err.localizedDescription)", log: self.log) + return + } + + let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile() + if outputData.isEmpty { + return + } + + let output = String(decoding: outputData.advanced(by: outputData.count/2), as: UTF8.self) + if output.isEmpty { + return + } + + var processes: [TopProcess] = [] + output.enumerateLines { (line, _) -> Void in + if line.matches("^\\d+ *[^(\\d)]*\\d+\\.*\\d* *$") { + var str = line.trimmingCharacters(in: .whitespaces) + + let pidString = str.findAndCrop(pattern: "^\\d+") + let usageString = str.findAndCrop(pattern: " +[0-9]+.*[0-9]*$") + let command = str.trimmingCharacters(in: .whitespaces) + + let pid = Int(pidString) ?? 0 + guard let usage = Double(usageString.filter("01234567890.".contains)) else { + return + } + + var name: String? = nil + var icon: NSImage? = nil + if let app = NSRunningApplication(processIdentifier: pid_t(pid) ) { + name = app.localizedName ?? nil + icon = app.icon + } + + processes.append(TopProcess(pid: pid, command: command, name: name, usage: usage, icon: icon)) } - } else if self.paused { - self.paused = !self.task.resume() - } - } - - public override func pause() { - if self.task.isRunning && !self.paused { - self.paused = self.task.suspend() - } - } - - public override func stop() { - if self.task.isRunning && !self.paused { - self.paused = self.task.suspend() } + + self.callback(processes.suffix(self.numberOfProcesses).sorted(by: { $0.usage > $1.usage })) } }