- fix unnecessary popup interface update (when popup closed)

GPU module MVP
This commit is contained in:
Serhiy Mytrovtsiy
2020-08-17 22:51:21 +02:00
parent 98fb84163e
commit 95f941149f
9 changed files with 261 additions and 124 deletions

View File

@@ -189,13 +189,15 @@ internal class Popup: NSView {
public func processCallback(_ list: [TopProcess]) {
DispatchQueue.main.async(execute: {
for i in 0..<list.count {
let process = list[i]
let index = list.count-i-1
if self.processes.indices.contains(index) {
self.processes[index].label = process.name != nil ? process.name! : process.command
self.processes[index].value = "\(process.usage)%"
self.processes[index].icon = process.icon
if (self.window?.isVisible ?? false) {
for i in 0..<list.count {
let process = list[i]
let index = list.count-i-1
if self.processes.indices.contains(index) {
self.processes[index].label = process.name != nil ? process.name! : process.command
self.processes[index].value = "\(process.usage)%"
self.processes[index].icon = process.icon
}
}
}
})

View File

@@ -159,20 +159,20 @@ internal class Popup: NSView {
let v = Int(value.totalUsage.rounded(toPlaces: 2) * 100)
self.loadField?.stringValue = "\(v) %"
self.ready = true
}
self.circle?.setValue(value.totalUsage)
self.circle?.setSegments([
circle_segment(value: value.systemLoad, color: NSColor.systemRed),
circle_segment(value: value.userLoad, color: NSColor.systemBlue),
])
if tempValue != nil {
self.temperatureCircle?.setValue(tempValue!)
let formatter = MeasurementFormatter()
formatter.numberFormatter.maximumFractionDigits = 0
let measurement = Measurement(value: tempValue!, unit: UnitTemperature.celsius)
self.temperatureCircle?.setText(formatter.string(from: measurement))
self.circle?.setValue(value.totalUsage)
self.circle?.setSegments([
circle_segment(value: value.systemLoad, color: NSColor.systemRed),
circle_segment(value: value.userLoad, color: NSColor.systemBlue),
])
if tempValue != nil {
self.temperatureCircle?.setValue(tempValue!)
let formatter = MeasurementFormatter()
formatter.numberFormatter.maximumFractionDigits = 0
let measurement = Measurement(value: tempValue!, unit: UnitTemperature.celsius)
self.temperatureCircle?.setText(formatter.string(from: measurement))
}
}
self.chart?.addValue(value.totalUsage)
})
@@ -180,13 +180,15 @@ internal class Popup: NSView {
public func processCallback(_ list: [TopProcess]) {
DispatchQueue.main.async(execute: {
for i in 0..<list.count {
let process = list[i]
let index = list.count-i-1
if self.processes.indices.contains(index) {
self.processes[index].label = process.name != nil ? process.name! : process.command
self.processes[index].value = "\(process.usage)%"
self.processes[index].icon = process.icon
if (self.window?.isVisible ?? false) {
for i in 0..<list.count {
let process = list[i]
let index = list.count-i-1
if self.processes.indices.contains(index) {
self.processes[index].label = process.name != nil ? process.name! : process.command
self.processes[index].value = "\(process.usage)%"
self.processes[index].icon = process.icon
}
}
}
})

View File

@@ -64,6 +64,7 @@ internal class DiskView: NSView {
public let name: String
public let size: Int64
private let uri: URL?
private var ready: Bool = false
private let nameHeight: CGFloat = 20
private let legendHeight: CGFloat = 12
@@ -196,22 +197,26 @@ internal class DiskView: NSView {
public func update(free: Int64, read: Int64?, write: Int64?) {
DispatchQueue.main.async(execute: {
if self.legendField != nil {
self.legendField?.stringValue = "Used \(Units(bytes: (self.size - free)).getReadableMemory()) from \(Units(bytes: self.size).getReadableMemory())"
self.percentageField?.stringValue = "\(Int8((Double(self.size - free) / Double(self.size)) * 100))%"
}
if self.usedBarSpace != nil {
let percentage = CGFloat(self.size - free) / CGFloat(self.size)
let width: CGFloat = ((self.mainView.frame.width - 2) * percentage) / 1
self.usedBarSpace?.setFrameSize(NSSize(width: width, height: self.usedBarSpace!.frame.height))
}
if read != nil {
self.setReadState(read != 0)
}
if write != nil {
self.setWriteState(write != 0)
if (self.window?.isVisible ?? false) || !self.ready {
if self.legendField != nil {
self.legendField?.stringValue = "Used \(Units(bytes: (self.size - free)).getReadableMemory()) from \(Units(bytes: self.size).getReadableMemory())"
self.percentageField?.stringValue = "\(Int8((Double(self.size - free) / Double(self.size)) * 100))%"
}
if self.usedBarSpace != nil {
let percentage = CGFloat(self.size - free) / CGFloat(self.size)
let width: CGFloat = ((self.mainView.frame.width - 2) * percentage) / 1
self.usedBarSpace?.setFrameSize(NSSize(width: width, height: self.usedBarSpace!.frame.height))
}
if read != nil {
self.setReadState(read != 0)
}
if write != nil {
self.setWriteState(write != 0)
}
self.ready = true
}
})
}

View File

@@ -19,11 +19,6 @@ public struct GPU_Info {
public var state: Bool = false
public var utilization: Double = 0
public var totalVram: Int = 0
public var freeVram: Int = 0
public var coreClock: Int = 0
public var power: Int = 0
public var temperature: Int = 0
}

View File

@@ -14,6 +14,9 @@ import StatsKit
import ModuleKit
internal class Popup: NSView {
private var list: [String: GPUView] = [:]
private let gpuViewHeight: CGFloat = 162
public init() {
super.init(frame: NSRect(x: 0, y: 0, width: Constants.Popup.width, height: 0))
}
@@ -23,10 +26,152 @@ internal class Popup: NSView {
}
internal func infoCallback(_ value: GPUs) {
print(value)
if self.list.count != value.list.count {
DispatchQueue.main.async(execute: {
self.subviews.forEach{ $0.removeFromSuperview() }
})
self.list = [:]
}
value.list.forEach { (gpu: GPU_Info) in
if self.list[gpu.name] == nil {
DispatchQueue.main.async(execute: {
self.list[gpu.name] = GPUView(
NSRect(x: 0, y: (self.gpuViewHeight + Constants.Popup.margins) * CGFloat(self.list.count), width: self.frame.width, height: self.gpuViewHeight),
gpu: gpu
)
self.addSubview(self.list[gpu.name]!)
})
} else {
self.list[gpu.name]?.update(gpu)
}
}
DispatchQueue.main.async(execute: {
let h: CGFloat = ((self.gpuViewHeight + Constants.Popup.margins) * CGFloat(self.list.count)) - Constants.Popup.margins
if self.frame.size.height != h {
self.setFrameSize(NSSize(width: self.frame.width, height: h))
NotificationCenter.default.post(name: .updatePopupSize, object: nil, userInfo: ["module": "GPU"])
}
})
}
}
private class GPUView: NSView {
private let height: CGFloat = 60
private let margin: CGFloat = 4
private var name: String
private var state: Bool
private var chart: LineChartView? = nil
private var utilization: HalfCircleGraphView? = nil
private var temperature: HalfCircleGraphView? = nil
private var stateView: NSView? = nil
public init(_ frame: NSRect, gpu: GPU_Info) {
self.name = gpu.name
self.state = gpu.state
super.init(frame: frame)
self.wantsLayer = true
self.layer?.cornerRadius = 2
self.initName()
self.initCircles()
self.initChart()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func initName() {
let y: CGFloat = self.frame.height - Constants.Popup.separatorHeight
let width: CGFloat = self.name.widthOfString(usingFont: NSFont.systemFont(ofSize: 12, weight: .medium)) + 16
let view: NSView = NSView(frame: NSRect(x: (self.frame.width - width)/2, y: y, width: width, height: 30))
let labelView: NSTextField = TextView(frame: NSRect(x: 0, y: (view.frame.height-15)/2, width: width - 8, height: 15))
labelView.alignment = .center
labelView.textColor = .secondaryLabelColor
labelView.font = NSFont.systemFont(ofSize: 12, weight: .medium)
labelView.stringValue = self.name
let stateView: NSView = NSView(frame: NSRect(x: width - 8, y: (view.frame.height-7)/2, width: 6, height: 6))
stateView.wantsLayer = true
stateView.layer?.backgroundColor = (self.state ? NSColor.systemGreen : NSColor.systemRed).cgColor
stateView.toolTip = "GPU \(self.state ? "enabled" : "disabled")"
stateView.layer?.cornerRadius = 4
view.addSubview(labelView)
view.addSubview(stateView)
self.addSubview(view)
self.stateView = stateView
}
private func initCircles() {
let view: NSView = NSView(frame: NSRect(
x: self.margin,
y: self.height + (self.margin*2),
width: self.frame.width - (self.margin*2),
height: self.height
))
let circleSize: CGFloat = 50
self.temperature = HalfCircleGraphView(frame: NSRect(
x: ((view.frame.width/2) - circleSize)/2 + 10,
y: 5,
width: circleSize,
height: circleSize
))
self.temperature!.toolTip = "GPU temperature"
self.utilization = HalfCircleGraphView(frame: NSRect(
x: (view.frame.width/2) + (((view.frame.width/2) - circleSize)/2) - 10,
y: 5,
width: circleSize,
height: circleSize
))
self.utilization!.toolTip = "GPU utilization"
view.addSubview(self.temperature!)
view.addSubview(self.utilization!)
self.addSubview(view)
}
private func initChart() {
let view: NSView = NSView(frame: NSRect(x: self.margin, y: self.margin, width: self.frame.width - (self.margin*2), height: self.height))
view.wantsLayer = true
view.layer?.backgroundColor = NSColor.lightGray.withAlphaComponent(0.1).cgColor
view.layer?.cornerRadius = 3
self.chart = LineChartView(frame: NSRect(x: 1, y: 0, width: view.frame.width, height: view.frame.height), num: 120)
view.addSubview(self.chart!)
self.addSubview(view)
}
public func update(_ gpu: GPU_Info) {
DispatchQueue.main.async(execute: {
if (self.window?.isVisible ?? false) {
self.stateView?.layer?.backgroundColor = (gpu.state ? NSColor.systemGreen : NSColor.systemRed).cgColor
self.stateView?.toolTip = "GPU \(gpu.state ? "enabled" : "disabled")"
self.utilization?.setValue(gpu.utilization)
self.utilization?.setText("\(Int(gpu.utilization*100))%")
self.temperature?.setValue(Double(gpu.temperature))
let formatter = MeasurementFormatter()
formatter.numberFormatter.maximumFractionDigits = 0
let measurement = Measurement(value: Double(gpu.temperature), unit: UnitTemperature.celsius)
self.temperature?.setText(formatter.string(from: measurement))
self.chart?.addValue(gpu.utilization)
}
})
}
}

View File

@@ -66,10 +66,9 @@ internal class InfoReader: Reader<GPUs> {
}
let utilization = stats["Device Utilization %"] as? Int ?? 0
let totalVram = accelerator["VRAM,totalMB"] as? Int ?? matchedGPU["VRAM,totalMB"] as? Int ?? 0
let freeVram = stats["vramFreeBytes"] as? Int ?? 0
let coreClock = stats["Core Clock(MHz)"] as? Int ?? 0
var power = stats["Total Power(W)"] as? Int ?? 0
// let totalVram = (accelerator["VRAM,totalMB"] as? Int ?? matchedGPU["VRAM,totalMB"] as? Int ?? 0) * 1000000
// let freeVram = stats["vramFreeBytes"] as? Int ?? 0
// let coreClock = stats["Core Clock(MHz)"] as? Int ?? 0
var temperature = stats["Temperature(C)"] as? Int ?? 0
if IOClass == "IntelAccelerator" {
@@ -80,25 +79,11 @@ internal class InfoReader: Reader<GPUs> {
temperature = Int(tmp)
}
}
if power == 0 {
if let pwr = self.smc?.pointee.getValue("PCPG") {
power = Int(pwr)
} else if let pwr = self.smc?.pointee.getValue("PCGC") {
power = Int(pwr)
} else if let pwr = self.smc?.pointee.getValue("PCGM") {
power = Int(pwr)
}
}
}
self.gpus.list[idx].state = agcInfo["poweredOffByAGC"] == 0
self.gpus.list[idx].utilization = utilization == 0 ? 0 : Double(utilization)/100
self.gpus.list[idx].totalVram = totalVram
self.gpus.list[idx].freeVram = freeVram
self.gpus.list[idx].coreClock = coreClock
self.gpus.list[idx].power = power
self.gpus.list[idx].temperature = temperature
}

View File

@@ -161,29 +161,32 @@ internal class Popup: NSView {
self.totalField?.stringValue = Units(bytes: Int64(value.total)).getReadableMemory()
self.usedField?.stringValue = Units(bytes: Int64(value.used)).getReadableMemory()
self.freeField?.stringValue = Units(bytes: Int64(value.free)).getReadableMemory()
self.circle?.setValue(value.usage)
self.circle?.setSegments([
circle_segment(value: value.active/value.total, color: NSColor.systemBlue),
circle_segment(value: value.wired/value.total, color: NSColor.systemOrange),
circle_segment(value: value.compressed/value.total, color: NSColor.systemPink)
])
self.level?.setLevel(value.pressureLevel)
self.initialized = true
}
self.circle?.setValue(value.usage)
self.circle?.setSegments([
circle_segment(value: value.active/value.total, color: NSColor.systemBlue),
circle_segment(value: value.wired/value.total, color: NSColor.systemOrange),
circle_segment(value: value.compressed/value.total, color: NSColor.systemPink)
])
self.chart?.addValue(value.usage)
self.level?.setLevel(value.pressureLevel)
})
}
public func processCallback(_ list: [TopProcess]) {
DispatchQueue.main.async(execute: {
for i in 0..<list.count {
let process = list[i]
let index = list.count-i-1
if self.processes.indices.contains(index) {
self.processes[index].label = process.name != nil ? process.name! : process.command
self.processes[index].value = Units(bytes: Int64(process.usage)).getReadableMemory()
self.processes[index].icon = process.icon
if (self.window?.isVisible ?? false) {
for i in 0..<list.count {
let process = list[i]
let index = list.count-i-1
if self.processes.indices.contains(index) {
self.processes[index].label = process.name != nil ? process.name! : process.command
self.processes[index].value = Units(bytes: Int64(process.usage)).getReadableMemory()
self.processes[index].icon = process.icon
}
}
}
})

View File

@@ -222,44 +222,42 @@ internal class Popup: NSView {
public func usageCallback(_ value: Network_Usage) {
DispatchQueue.main.async(execute: {
if !(self.window?.isVisible ?? false) && self.initialized {
return
}
self.uploadValue = value.upload
self.downloadValue = value.download
self.setUploadDownloadFields()
if let interface = value.interface {
self.interfaceField?.stringValue = "\(interface.displayName) (\(interface.BSDName))"
self.macAdressField?.stringValue = interface.address
} else {
self.interfaceField?.stringValue = "Unknown"
self.macAdressField?.stringValue = "Unknown"
}
if value.connectionType == .wifi {
self.ssidField?.stringValue = value.ssid ?? "Unknown"
} else {
self.ssidField?.stringValue = "Unavailable"
}
if self.publicIPField?.stringValue != value.raddr {
if value.raddr == nil {
self.publicIPField?.stringValue = "Unknown"
if (self.window?.isVisible ?? false) || !self.initialized {
self.uploadValue = value.upload
self.downloadValue = value.download
self.setUploadDownloadFields()
if let interface = value.interface {
self.interfaceField?.stringValue = "\(interface.displayName) (\(interface.BSDName))"
self.macAdressField?.stringValue = interface.address
} else {
if value.countryCode == nil {
self.publicIPField?.stringValue = value.raddr!
self.interfaceField?.stringValue = "Unknown"
self.macAdressField?.stringValue = "Unknown"
}
if value.connectionType == .wifi {
self.ssidField?.stringValue = value.ssid ?? "Unknown"
} else {
self.ssidField?.stringValue = "Unavailable"
}
if self.publicIPField?.stringValue != value.raddr {
if value.raddr == nil {
self.publicIPField?.stringValue = "Unknown"
} else {
self.publicIPField?.stringValue = "\(value.raddr!) (\(value.countryCode!))"
if value.countryCode == nil {
self.publicIPField?.stringValue = value.raddr!
} else {
self.publicIPField?.stringValue = "\(value.raddr!) (\(value.countryCode!))"
}
}
}
if self.localIPField?.stringValue != value.laddr {
self.localIPField?.stringValue = value.laddr ?? "Unknown"
}
self.initialized = true
}
if self.localIPField?.stringValue != value.laddr {
self.localIPField?.stringValue = value.laddr ?? "Unknown"
}
self.initialized = true
self.chart?.addValues([Double(value.upload), Double(value.download)])
})
@@ -267,14 +265,16 @@ internal class Popup: NSView {
public func processCallback(_ list: [Network_Process]) {
DispatchQueue.main.async(execute: {
for i in 0..<list.count {
let process = list[i]
let index = list.count-i-1
if self.processes.indices.contains(index) {
self.processes[index].label = process.name
self.processes[index].upload = Units(bytes: Int64(process.upload)).getReadableSpeed()
self.processes[index].download = Units(bytes: Int64(process.download)).getReadableSpeed()
self.processes[index].icon = process.icon
if (self.window?.isVisible ?? false) {
for i in 0..<list.count {
let process = list[i]
let index = list.count-i-1
if self.processes.indices.contains(index) {
self.processes[index].label = process.name
self.processes[index].upload = Units(bytes: Int64(process.upload)).getReadableSpeed()
self.processes[index].download = Units(bytes: Int64(process.download)).getReadableSpeed()
self.processes[index].icon = process.icon
}
}
}
})

View File

@@ -781,8 +781,8 @@
children = (
9A90E19524EAD35F00471E9A /* main.swift */,
9A90E1A224EAD66600471E9A /* reader.swift */,
9A53EBF824EAFA5200648841 /* settings.swift */,
9A53EBFA24EB041E00648841 /* popup.swift */,
9A53EBF824EAFA5200648841 /* settings.swift */,
9A90E18C24EAD2BB00471E9A /* Info.plist */,
9A90E19724EAD3B000471E9A /* config.plist */,
);