From aab85cfdfdec3e1c18ffc997aebb73b92651497c Mon Sep 17 00:00:00 2001 From: Serhiy Mytrovtsiy Date: Tue, 30 Jun 2020 23:11:40 +0200 Subject: [PATCH] - added RAM pressure to BarChart widget - added RAM pressure to LineChart widget --- ModuleKit/Widgets/BarChart.swift | 64 +++++++++++++++++++++++++++++-- ModuleKit/Widgets/LineChart.swift | 44 ++++++++++++++++++++- ModuleKit/module.swift | 2 +- ModuleKit/settings.swift | 2 +- ModuleKit/widget.swift | 16 ++++---- Modules/Memory/main.swift | 4 ++ Modules/Memory/readers.swift | 20 ++++++---- StatsKit/Charts.swift | 8 ++-- StatsKit/extensions.swift | 29 ++++++++++++++ 9 files changed, 165 insertions(+), 24 deletions(-) diff --git a/ModuleKit/Widgets/BarChart.swift b/ModuleKit/Widgets/BarChart.swift index 852038bc..46194a68 100644 --- a/ModuleKit/Widgets/BarChart.swift +++ b/ModuleKit/Widgets/BarChart.swift @@ -16,9 +16,14 @@ public class BarChart: Widget { private var labelState: Bool = true private var boxState: Bool = true private var colorState: Bool = false + private var pressureState: Bool = true private let store: UnsafePointer? private var value: [Double] = [] + private var pressureLevel: Int = 0 + + private var pressureLevelSettingsView: NSView? = nil + private var colorizeSettingsView: NSView? = nil public init(preview: Bool, title: String, config: NSDictionary?, store: UnsafePointer?) { var widgetTitle: String = title @@ -58,6 +63,7 @@ public class BarChart: Widget { self.boxState = store!.pointee.bool(key: "\(self.title)_\(self.type.rawValue)_box", defaultValue: self.boxState) self.labelState = store!.pointee.bool(key: "\(self.title)_\(self.type.rawValue)_label", defaultValue: self.labelState) self.colorState = store!.pointee.bool(key: "\(self.title)_\(self.type.rawValue)_color", defaultValue: self.colorState) + self.pressureState = store!.pointee.bool(key: "\(self.title)_\(self.type.rawValue)_pressure", defaultValue: self.pressureState) } if preview { @@ -66,6 +72,7 @@ public class BarChart: Widget { } self.setFrameSize(NSSize(width: 36, height: self.frame.size.height)) self.invalidateIntrinsicContentSize() + self.pressureState = false } } @@ -155,6 +162,8 @@ public class BarChart: Widget { if self.colorState { partitionValue.usageColor().setFill() + } else if self.pressureState { + self.pressureLevel.pressureColor().setFill() } else { NSColor.controlAccentColor.set() } @@ -175,13 +184,35 @@ public class BarChart: Widget { }) } + public func setPressure(_ level: Int) { + guard self.pressureLevel != level else { + return + } + + self.pressureLevel = level + DispatchQueue.main.async(execute: { + self.display() + }) + } + public override func settings(superview: NSView) { let rowHeight: CGFloat = 30 - let height: CGFloat = ((rowHeight + Constants.Settings.margin) * 3) + Constants.Settings.margin + let settingsNumber: CGFloat = self.title == "RAM" ? 4 : 3 + let height: CGFloat = ((rowHeight + Constants.Settings.margin) * settingsNumber) + Constants.Settings.margin superview.setFrameSize(NSSize(width: superview.frame.width, height: height)) let view: NSView = NSView(frame: NSRect(x: Constants.Settings.margin, y: Constants.Settings.margin, width: superview.frame.width - (Constants.Settings.margin*2), height: superview.frame.height - (Constants.Settings.margin*2))) + if self.title == "RAM" { + self.pressureLevelSettingsView = ToggleTitleRow( + frame: NSRect(x: 0, y: (rowHeight + Constants.Settings.margin) * 3, width: view.frame.width, height: rowHeight), + title: "Pressure level", + action: #selector(togglePressure), + state: self.pressureState + ) + view.addSubview(self.pressureLevelSettingsView!) + } + view.addSubview(ToggleTitleRow( frame: NSRect(x: 0, y: (rowHeight + Constants.Settings.margin) * 2, width: view.frame.width, height: rowHeight), title: "Label", @@ -196,12 +227,13 @@ public class BarChart: Widget { state: self.boxState )) - view.addSubview(ToggleTitleRow( + self.colorizeSettingsView = ToggleTitleRow( frame: NSRect(x: 0, y: (rowHeight + Constants.Settings.margin) * 0, width: view.frame.width, height: rowHeight), title: "Colorize", action: #selector(toggleColor), state: self.colorState - )) + ) + view.addSubview(self.colorizeSettingsView!) superview.addSubview(view) } @@ -239,6 +271,32 @@ public class BarChart: Widget { } self.colorState = state! == .on ? true : false self.store?.pointee.set(key: "\(self.title)_\(self.type.rawValue)_color", value: self.colorState) + + if self.colorState { + self.pressureState = false + self.store?.pointee.set(key: "\(self.title)_\(self.type.rawValue)_pressure", value: self.pressureState) + ToggleNSControlState(self.pressureLevelSettingsView, state: .off) + } + + self.display() + } + + @objc private func togglePressure(_ sender: NSControl) { + var state: NSControl.StateValue? = nil + if #available(OSX 10.15, *) { + state = sender is NSSwitch ? (sender as! NSSwitch).state: nil + } else { + state = sender is NSButton ? (sender as! NSButton).state: nil + } + self.pressureState = state! == .on ? true : false + self.store?.pointee.set(key: "\(self.title)_\(self.type.rawValue)_pressure", value: self.pressureState) + + if self.pressureState { + self.colorState = false + self.store?.pointee.set(key: "\(self.title)_\(self.type.rawValue)_color", value: self.colorState) + ToggleNSControlState(self.colorizeSettingsView, state: .off) + } + self.display() } } diff --git a/ModuleKit/Widgets/LineChart.swift b/ModuleKit/Widgets/LineChart.swift index 07238f72..b10cf7fc 100644 --- a/ModuleKit/Widgets/LineChart.swift +++ b/ModuleKit/Widgets/LineChart.swift @@ -17,10 +17,12 @@ public class LineChart: Widget { private var boxState: Bool = true private var valueState: Bool = false private var colorState: Bool = false + private var pressureState: Bool = true private let store: UnsafePointer? private var chart: LineChartView private var value: Double = 0 + private var pressureLevel: Int = 0 public init(preview: Bool, title: String, config: NSDictionary?, store: UnsafePointer?) { var widgetTitle: String = title @@ -54,6 +56,7 @@ public class LineChart: Widget { self.valueState = store!.pointee.bool(key: "\(self.title)_\(self.type.rawValue)_value", defaultValue: self.valueState) self.labelState = store!.pointee.bool(key: "\(self.title)_\(self.type.rawValue)_label", defaultValue: self.labelState) self.colorState = store!.pointee.bool(key: "\(self.title)_\(self.type.rawValue)_color", defaultValue: self.colorState) + self.pressureState = store!.pointee.bool(key: "\(self.title)_\(self.type.rawValue)_pressure", defaultValue: self.pressureState) } if self.labelState { @@ -67,6 +70,7 @@ public class LineChart: Widget { } self.chart.points = list self.value = 0.38 + self.pressureState = false } } @@ -142,6 +146,11 @@ public class LineChart: Widget { } chart.setFrameSize(NSSize(width: box.bounds.width - chartPadding, height: box.bounds.height - (chartPadding*2))) + if self.pressureState { + self.chart.color = self.pressureLevel.pressureColor() + } else { + self.chart.color = NSColor.controlAccentColor + } chart.draw(NSRect(x: box.bounds.origin.x + 1, y: chartPadding, width: chart.frame.width, height: chart.frame.height)) ctx.restoreGState() @@ -154,11 +163,21 @@ public class LineChart: Widget { public override func settings(superview: NSView) { let rowHeight: CGFloat = 30 - let height: CGFloat = ((rowHeight + Constants.Settings.margin) * 4) + Constants.Settings.margin + let settingsNumber: CGFloat = self.title == "RAM" ? 5 : 4 + let height: CGFloat = ((rowHeight + Constants.Settings.margin) * settingsNumber) + Constants.Settings.margin superview.setFrameSize(NSSize(width: superview.frame.width, height: height)) let view: NSView = NSView(frame: NSRect(x: Constants.Settings.margin, y: Constants.Settings.margin, width: superview.frame.width - (Constants.Settings.margin*2), height: superview.frame.height - (Constants.Settings.margin*2))) + if self.title == "RAM" { + view.addSubview(ToggleTitleRow( + frame: NSRect(x: 0, y: (rowHeight + Constants.Settings.margin) * 4, width: view.frame.width, height: rowHeight), + title: "Pressure level", + action: #selector(togglePressure), + state: self.pressureState + )) + } + view.addSubview(ToggleTitleRow( frame: NSRect(x: 0, y: (rowHeight + Constants.Settings.margin) * 3, width: view.frame.width, height: rowHeight), title: "Label", @@ -205,6 +224,17 @@ public class LineChart: Widget { }) } + public func setPressure(_ level: Int) { + guard self.pressureLevel != level else { + return + } + + self.pressureLevel = level + DispatchQueue.main.async(execute: { + self.display() + }) + } + @objc private func toggleLabel(_ sender: NSControl) { var state: NSControl.StateValue? = nil if #available(OSX 10.15, *) { @@ -252,4 +282,16 @@ public class LineChart: Widget { self.store?.pointee.set(key: "\(self.title)_\(self.type.rawValue)_color", value: self.colorState) self.display() } + + @objc private func togglePressure(_ sender: NSControl) { + var state: NSControl.StateValue? = nil + if #available(OSX 10.15, *) { + state = sender is NSSwitch ? (sender as! NSSwitch).state: nil + } else { + state = sender is NSButton ? (sender as! NSButton).state: nil + } + self.pressureState = state! == .on ? true : false + self.store?.pointee.set(key: "\(self.title)_\(self.type.rawValue)_pressure", value: self.pressureState) + self.display() + } } diff --git a/ModuleKit/module.swift b/ModuleKit/module.swift index ae2a1f2f..c81a8a8d 100644 --- a/ModuleKit/module.swift +++ b/ModuleKit/module.swift @@ -215,7 +215,7 @@ open class Module: Module_p { // load and setup widget private func setWidget() { - self.widget = LoadWidget(self.activeWidget, preview: false, title: self.config.name, config: self.config.widgetsConfig, store: self.store) + self.widget = LoadWidget(self.activeWidget, preview: false, name: self.config.name, config: self.config.widgetsConfig, store: self.store) if self.widget == nil { self.enabled = false os_log(.error, log: log, "widget with type %s not found", "\(self.activeWidget)") diff --git a/ModuleKit/settings.swift b/ModuleKit/settings.swift index 06c19f6d..d36844f8 100644 --- a/ModuleKit/settings.swift +++ b/ModuleKit/settings.swift @@ -120,7 +120,7 @@ open class Settings: NSView, Settings_p { var x: CGFloat = Constants.Settings.margin for i in 0...self.config.pointee.availableWidgets.count - 1 { let widgetType = self.config.pointee.availableWidgets[i] - if let widget = LoadWidget(widgetType, preview: true, title: self.config.pointee.name, config: self.config.pointee.widgetsConfig, store: nil) { + if let widget = LoadWidget(widgetType, preview: true, name: self.config.pointee.name, config: self.config.pointee.widgetsConfig, store: nil) { let preview = WidgetPreview( frame: NSRect(x: x, y: Constants.Settings.margin, width: widget.frame.width, height: self.widgetSelectorHeight - (Constants.Settings.margin*2)), title: self.config.pointee.name, diff --git a/ModuleKit/widget.swift b/ModuleKit/widget.swift index 337aaa4d..b2f14c22 100644 --- a/ModuleKit/widget.swift +++ b/ModuleKit/widget.swift @@ -72,31 +72,31 @@ open class Widget: NSView, Widget_p { open func setValues(_ values: [value_t]) {} } -func LoadWidget(_ type: widget_t, preview: Bool, title: String, config: NSDictionary?, store: UnsafePointer?) -> Widget_p? { +func LoadWidget(_ type: widget_t, preview: Bool, name: String, config: NSDictionary?, store: UnsafePointer?) -> Widget_p? { var widget: Widget_p? = nil let widgetConfig: NSDictionary? = config?[type.rawValue] as? NSDictionary switch type { case .mini: - widget = Mini(preview: preview, title: title, config: widgetConfig, store: store) + widget = Mini(preview: preview, title: name, config: widgetConfig, store: store) break case .lineChart: - widget = LineChart(preview: preview, title: title, config: widgetConfig, store: store) + widget = LineChart(preview: preview, title: name, config: widgetConfig, store: store) break case .barChart: - widget = BarChart(preview: preview, title: title, config: widgetConfig, store: store) + widget = BarChart(preview: preview, title: name, config: widgetConfig, store: store) break case .network: - widget = NetworkWidget(preview: preview, title: title, config: widgetConfig, store: store) + widget = NetworkWidget(preview: preview, title: name, config: widgetConfig, store: store) break case .battery: - widget = BatterykWidget(preview: preview, title: title, config: widgetConfig, store: store) + widget = BatterykWidget(preview: preview, title: name, config: widgetConfig, store: store) break case .sensors: - widget = SensorsWidget(preview: preview, title: title, config: widgetConfig, store: store) + widget = SensorsWidget(preview: preview, title: name, config: widgetConfig, store: store) break case .disk: - widget = DiskWidget(preview: preview, title: title, config: widgetConfig, store: store) + widget = DiskWidget(preview: preview, title: name, config: widgetConfig, store: store) break default: break } diff --git a/Modules/Memory/main.swift b/Modules/Memory/main.swift index 395bbd9a..4d4e8c96 100644 --- a/Modules/Memory/main.swift +++ b/Modules/Memory/main.swift @@ -24,6 +24,8 @@ public struct RAM_Usage: value_t { var used: Double? var free: Double? + var pressureLevel: Int + public var widget_value: Double { get { return self.usage ?? 0 @@ -63,9 +65,11 @@ public class Memory: Module { } if let widget = self.widget as? LineChart { widget.setValue(value!.usage ?? 0) + widget.setPressure(value?.pressureLevel ?? 0) } if let widget = self.widget as? BarChart { widget.setValue([value!.usage ?? 0]) + widget.setPressure(value?.pressureLevel ?? 0) } } } diff --git a/Modules/Memory/readers.swift b/Modules/Memory/readers.swift index aacbbd26..1d5f0602 100644 --- a/Modules/Memory/readers.swift +++ b/Modules/Memory/readers.swift @@ -29,7 +29,7 @@ internal class UsageReader: Reader { self.totalSize = Double(stats.max_mem) return } - + self.totalSize = 0 print("Error with host_info(): " + (String(cString: mach_error_string(kerr), encoding: String.Encoding.ascii) ?? "unknown error")) } @@ -37,22 +37,26 @@ internal class UsageReader: Reader { public override func read() { var stats = vm_statistics64() var count = UInt32(MemoryLayout.size / MemoryLayout.size) - + let result: kern_return_t = withUnsafeMutablePointer(to: &stats) { $0.withMemoryRebound(to: integer_t.self, capacity: 1) { host_statistics64(mach_host_self(), HOST_VM_INFO64, $0, &count) } } - + if result == KERN_SUCCESS { let active = Double(stats.active_count) * Double(PAGE_SIZE) let inactive = Double(stats.inactive_count) * Double(PAGE_SIZE) let wired = Double(stats.wire_count) * Double(PAGE_SIZE) let compressed = Double(stats.compressor_page_count) * Double(PAGE_SIZE) - + let used = active + wired + compressed let free = self.totalSize - used + var size: size_t = MemoryLayout.size + var pressureLevel: Int = 0 + sysctlbyname("kern.memorystatus_vm_pressure_level", &pressureLevel, &size, nil, 0) + self.callback(RAM_Usage( active: active, inactive: inactive, @@ -62,11 +66,13 @@ internal class UsageReader: Reader { usage: Double((self.totalSize - free) / self.totalSize), total: Double(self.totalSize), used: Double(used), - free: Double(free)) - ) + free: Double(free), + + pressureLevel: pressureLevel + )) return } - + print("Error with host_statistics64(): " + (String(cString: mach_error_string(result), encoding: String.Encoding.ascii) ?? "unknown error")) } } diff --git a/StatsKit/Charts.swift b/StatsKit/Charts.swift index d4e4dd0b..d824cc8f 100644 --- a/StatsKit/Charts.swift +++ b/StatsKit/Charts.swift @@ -24,6 +24,8 @@ public class LineChartView: NSView { public var points: [Double]? = nil public var transparent: Bool = true + public var color: NSColor = NSColor.controlAccentColor + public init(frame: NSRect, num: Int) { self.points = Array(repeating: 0, count: num) super.init(frame: frame) @@ -40,10 +42,10 @@ public class LineChartView: NSView { return } - let lineColor: NSColor = NSColor.controlAccentColor - var gradientColor: NSColor = NSColor.controlAccentColor.withAlphaComponent(0.5) + let lineColor: NSColor = self.color + var gradientColor: NSColor = self.color.withAlphaComponent(0.5) if !self.transparent { - gradientColor = NSColor.controlAccentColor.withAlphaComponent(0.8) + gradientColor = self.color.withAlphaComponent(0.8) } let context = NSGraphicsContext.current!.cgContext diff --git a/StatsKit/extensions.swift b/StatsKit/extensions.swift index d2930c31..ae3433cf 100644 --- a/StatsKit/extensions.swift +++ b/StatsKit/extensions.swift @@ -154,6 +154,21 @@ extension String: LocalizedError { } } +public extension Int { + func pressureColor() -> NSColor { + switch self { + case 1: + return NSColor.systemGreen + case 2: + return NSColor.systemYellow + case 3: + return NSColor.systemRed + default: + return NSColor.controlAccentColor + } + } +} + extension Float { init?(_ bytes: [UInt8]) { self = bytes.withUnsafeBytes { @@ -559,3 +574,17 @@ public extension Array where Element : Equatable { return true } } + +public func ToggleNSControlState(_ view: NSView?, state: NSControl.StateValue) { + if let control = view?.subviews.first(where: { $0 is NSControl }) { + if #available(OSX 10.15, *) { + if let checkbox = control as? NSSwitch { + checkbox.state = state + } + } else { + if let checkbox = control as? NSButton { + checkbox.state = state + } + } + } +}