diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..8d5ea47c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,58 @@ +# Changelog +All notable changes to this project will be documented in this file. + +### [v1.2.5] + - added chart bar widget for CPU, Memory and Disk module + - label in Charts are enabled by default + - color and label option are visible only if available in selected widget + - fixed few bugs + +### [v1.2.4] + - fixed bug when widgets don't display properly (or don't shows at all) + - initialized bar chart widget + - fixed few bugs + +### [v1.2.3] + - new icon + - small code refactoring + - changed font style name of the indicator in the Chart/Chart with value + - added dock icon visibility to preferences + - moved color and label preference from global to local (now each module can be configurated separately) + - now check for updates on start can be disabled in preferences + - fixed few bugs + +### [v1.2.2] + - fully automated build and sign app process + - fixed update and about visibility window in dark mode + - added name of the indicators in the Chart/Chart with value + - added check for new version on start + - removed charts and charts with value to Disk module + - now module submenu is disabled if module is disabled + - fixed bug when network module stop working after turn on/of + - fixed few bugs + +### [v1.2.1] + - added charts and charts with value to Disk module + - fixed bug when Chart with value does not shows + +### [v1.2.0] + - added network module + - added Check for updates window + - fixed few bugs + +### [v1.1.0] + - added battery module + - added chart widget for CPU and Memory + - added About Stats window + +### [v1.0.0] + - first release + +[v1.2.5]: https://github.com/exelban/stats/releases/tag/v1.2.5 +[v1.2.4]: https://github.com/exelban/stats/releases/tag/v1.2.4 +[v1.2.3]: https://github.com/exelban/stats/releases/tag/v1.2.3 +[v1.2.2]: https://github.com/exelban/stats/releases/tag/v1.2.2 +[v1.2.1]: https://github.com/exelban/stats/releases/tag/v1.2.1 +[v1.2.0]: https://github.com/exelban/stats/releases/tag/v1.2.0 +[v1.1.0]: https://github.com/exelban/stats/releases/tag/v1.1.0 +[v1.0.0]: https://github.com/exelban/stats/releases/tag/v1.0.0 \ No newline at end of file diff --git a/README.md b/README.md index 0ae2815e..a9edb7f8 100644 --- a/README.md +++ b/README.md @@ -20,9 +20,9 @@ You can download latest version [here](https://github.com/exelban/stats/releases | Name | Available widgets | Description | | --- | --- | --- | -| **CPU** | Percentage / Chart / Chart with value | Shows CPU usage | -| **Memory** | Percentage / Chart / Chart with value | Shows RAM usage | -| **Disk** | Percentage | Shows disk utilization | +| **CPU** | Percentage / Chart / Chart with value / Chart Bar | Shows CPU usage | +| **Memory** | Percentage / Chart / Chart with value / Chart Bar | Shows RAM usage | +| **Disk** | Percentage / Chart Bar | Shows disk utilization | | **Battery** | Graphic / Percentage | Shows battery level and charging status | | **Newtork** | Dots / Upload/Download traffic | Shows network activity | @@ -32,63 +32,9 @@ You can download latest version [here](https://github.com/exelban/stats/releases | 10.13.6 *(High Sierra)* | **true** | | 10.14.1 *(Mojave)* | **true** | -## Todo - - [X] Battery percentage - - [X] Create new logo ([IconArchive](http://www.iconarchive.com/show/simple-icons-by-kxmylo/utilities-system-monitor-icon.html)) - - [ ] Window with preferences - - [ ] Save last modules values - - [X] Colors toggle for each module - - [ ] temperature module - - [X] battery module - - [X] move to module system (CPU, RAM, DISK) - - [X] network module - - [X] save settings - - [ ] OTA updates - - [X] charts - - [X] autostart on boot - ## What's new +[CHANGELOG](https://github.com/exelban/stats/blob/master/CHANGELOG.md) -### v1.2.4 - - fixed bug when widgets don't display properly (or don't shows at all) - - initialized bar chart widget - - fixed few bugs - -### v1.2.3 - - new icon - - small code refactoring - - changed font style name of the indicator in the Chart/Chart with value - - added dock icon visibility to preferences - - moved color and label preference from global to local (now each module can be configurated separately) - - now check for updates on start can be disabled in preferences - - fixed few bugs - -### v1.2.2 - - fully automated build and sign app process - - fixed update and about visibility window in dark mode - - added name of the indicators in the Chart/Chart with value - - added check for new version on start - - removed charts and charts with value to Disk module - - now module submenu is disabled if module is disabled - - fixed bug when network module stop working after turn on/of - - fixed few bugs - -### v1.2.1 - - added charts and charts with value to Disk module - - fixed bug when Chart with value does not shows - -### v1.2.0 - - added network module - - added Check for updates window - - fixed few bugs - -### v1.1.0 - - added battery module - - added chart widget for CPU and Memory - - added About Stats window - -### v1.0.0 - - first release ## License [MIT License](https://github.com/exelban/stats/blob/master/LICENSE) diff --git a/Stats/Modules/Battery/Battery.swift b/Stats/Modules/Battery/Battery.swift index 297f83c7..d40bf7f0 100644 --- a/Stats/Modules/Battery/Battery.swift +++ b/Stats/Modules/Battery/Battery.swift @@ -36,17 +36,17 @@ class Battery: Module { } func start() { - if !self.reader.value.value.isNaN { + if !self.reader.value.value.isEmpty { let value = self.reader.value!.value - (self.view as! BatteryView).setCharging(value: value > 0) - (self.view as! Widget).value(value: abs(value)) + (self.view as! BatteryView).setCharging(value: value.first! > 0) + (self.view as! Widget).setValue(data: [abs(value.first!)]) } self.reader.start() self.reader.value.subscribe(observer: self) { (value, _) in - if !value.isNaN { - (self.view as! BatteryView).setCharging(value: value > 0) - (self.view as! Widget).value(value: abs(value)) + if !value.isEmpty { + (self.view as! BatteryView).setCharging(value: value.first! > 0) + (self.view as! Widget).setValue(data: [abs(value.first!)]) } } } diff --git a/Stats/Modules/Battery/BatteryReader.swift b/Stats/Modules/Battery/BatteryReader.swift index 8a2c89ca..8b97b1a0 100644 --- a/Stats/Modules/Battery/BatteryReader.swift +++ b/Stats/Modules/Battery/BatteryReader.swift @@ -10,12 +10,12 @@ import Foundation import IOKit.ps class BatteryReader: Reader { - var value: Observable! + var value: Observable<[Double]>! var available: Bool = false var updateTimer: Timer! init() { - self.value = Observable(0) + self.value = Observable([]) read() } @@ -49,7 +49,7 @@ class BatteryReader: Reader { cap = 0 - cap } - self.value << Double(cap) + self.value << [Double(cap)] } } } diff --git a/Stats/Modules/CPU/CPU.swift b/Stats/Modules/CPU/CPU.swift index 26e680b1..cfb5e1af 100644 --- a/Stats/Modules/CPU/CPU.swift +++ b/Stats/Modules/CPU/CPU.swift @@ -28,7 +28,7 @@ class CPU: Module { self.active = Observable(defaults.object(forKey: name) != nil ? defaults.bool(forKey: name) : true) self.widgetType = defaults.object(forKey: "\(name)_widget") != nil ? defaults.float(forKey: "\(name)_widget") : Widgets.Mini self.color = Observable(defaults.object(forKey: "\(name)_color") != nil ? defaults.bool(forKey: "\(name)_color") : false) - self.label = Observable(defaults.object(forKey: "\(name)_label") != nil ? defaults.bool(forKey: "\(name)_label") : false) + self.label = Observable(defaults.object(forKey: "\(name)_label") != nil ? defaults.bool(forKey: "\(name)_label") : true) initMenu() initWidget() } @@ -61,22 +61,26 @@ class CPU: Module { barChart.target = self let color = NSMenuItem(title: "Color", action: #selector(toggleColor), keyEquivalent: "") - color.state = defaults.bool(forKey: "\(name)_color") ? NSControl.StateValue.on : NSControl.StateValue.off + color.state = self.color.value ? NSControl.StateValue.on : NSControl.StateValue.off color.target = self let label = NSMenuItem(title: "Label", action: #selector(toggleLabel), keyEquivalent: "") - label.state = defaults.bool(forKey: "\(name)_label") || defaults.object(forKey: "\(name)_label") == nil ? NSControl.StateValue.on : NSControl.StateValue.off + label.state = self.label.value ? NSControl.StateValue.on : NSControl.StateValue.off label.target = self submenu.addItem(mini) submenu.addItem(chart) submenu.addItem(chartWithValue) -// submenu.addItem(barChart) + submenu.addItem(barChart) submenu.addItem(NSMenuItem.separator()) - submenu.addItem(label) - submenu.addItem(color) + if self.widgetType == Widgets.BarChart || self.widgetType == Widgets.ChartWithValue || self.widgetType == Widgets.Chart { + submenu.addItem(label) + } + if self.widgetType == Widgets.Mini || self.widgetType == Widgets.ChartWithValue { + submenu.addItem(color) + } menu.submenu = submenu } @@ -126,7 +130,8 @@ class CPU: Module { self.defaults.set(widgetCode, forKey: "\(name)_widget") self.widgetType = widgetCode self.active << false - initWidget() + self.initWidget() + self.initMenu() self.active << true } diff --git a/Stats/Modules/CPU/CPUReader.swift b/Stats/Modules/CPU/CPUReader.swift index 1cee0c2e..d8d3f372 100644 --- a/Stats/Modules/CPU/CPUReader.swift +++ b/Stats/Modules/CPU/CPUReader.swift @@ -9,7 +9,7 @@ import Foundation class CPUReader: Reader { - var value: Observable! + var value: Observable<[Double]>! var available: Bool = true var cpuInfo: processor_info_array_t! var prevCpuInfo: processor_info_array_t? @@ -19,9 +19,11 @@ class CPUReader: Reader { var updateTimer: Timer! let CPUUsageLock: NSLock = NSLock() + var perCoreMode: Bool = true + init() { let mibKeys: [Int32] = [ CTL_HW, HW_NCPU ] - self.value = Observable(0) + self.value = Observable([]) mibKeys.withUnsafeBufferPointer() { mib in var sizeOfNumCPUs: size_t = MemoryLayout.size let status = sysctl(processor_info_array_t(mutating: mib.baseAddress), 2, &numCPUs, &sizeOfNumCPUs, nil, 0) @@ -55,6 +57,8 @@ class CPUReader: Reader { var inUseOnAllCores: Int32 = 0 var totalOnAllCores: Int32 = 0 + var usagePerCore: [Double] = [] + for i in 0 ..< Int32(numCPUs) { var inUse: Int32 var total: Int32 @@ -76,8 +80,14 @@ class CPUReader: Reader { inUseOnAllCores = inUseOnAllCores + inUse totalOnAllCores = totalOnAllCores + total + usagePerCore.insert((Double(inUse) / Double(total)), at: Int(i)) + } + + if perCoreMode { + self.value << usagePerCore + } else { + self.value << [(Double(inUseOnAllCores) / Double(totalOnAllCores))] } - self.value << (Double(inUseOnAllCores) / Double(totalOnAllCores)) CPUUsageLock.unlock() diff --git a/Stats/Modules/Disk/Disk.swift b/Stats/Modules/Disk/Disk.swift index c0b85ea3..4f9156c0 100644 --- a/Stats/Modules/Disk/Disk.swift +++ b/Stats/Modules/Disk/Disk.swift @@ -31,7 +31,7 @@ class Disk: Module { self.active = Observable(defaults.object(forKey: name) != nil ? defaults.bool(forKey: name) : true) self.widgetType = defaults.object(forKey: "\(name)_widget") != nil ? defaults.float(forKey: "\(name)_widget") : Widgets.Mini self.color = Observable(defaults.object(forKey: "\(name)_color") != nil ? defaults.bool(forKey: "\(name)_color") : false) - self.label = Observable(defaults.object(forKey: "\(name)_label") != nil ? defaults.bool(forKey: "\(name)_label") : false) + self.label = Observable(defaults.object(forKey: "\(name)_label") != nil ? defaults.bool(forKey: "\(name)_label") : true) self.initMenu() self.initWidget() @@ -48,11 +48,33 @@ class Disk: Module { } menu.target = self + let mini = NSMenuItem(title: "Mini", action: #selector(toggleWidget), keyEquivalent: "") + mini.state = self.widgetType == Widgets.Mini ? NSControl.StateValue.on : NSControl.StateValue.off + mini.target = self + + let barChart = NSMenuItem(title: "Bar chart", action: #selector(toggleWidget), keyEquivalent: "") + barChart.state = self.widgetType == Widgets.BarChart ? NSControl.StateValue.on : NSControl.StateValue.off + barChart.target = self + let color = NSMenuItem(title: "Color", action: #selector(toggleColor), keyEquivalent: "") - color.state = defaults.bool(forKey: "\(name)_color") ? NSControl.StateValue.on : NSControl.StateValue.off + color.state = self.color.value ? NSControl.StateValue.on : NSControl.StateValue.off color.target = self - submenu.addItem(color) + let label = NSMenuItem(title: "Label", action: #selector(toggleLabel), keyEquivalent: "") + label.state = self.label.value ? NSControl.StateValue.on : NSControl.StateValue.off + label.target = self + + submenu.addItem(mini) + submenu.addItem(barChart) + + submenu.addItem(NSMenuItem.separator()) + + if self.widgetType == Widgets.BarChart { + submenu.addItem(label) + } + if self.widgetType == Widgets.Mini { + submenu.addItem(color) + } menu.submenu = submenu } @@ -71,9 +93,48 @@ class Disk: Module { } } + @objc func toggleWidget(_ sender: NSMenuItem) { + var widgetCode: Float = 0.0 + + switch sender.title { + case "Mini": + widgetCode = Widgets.Mini + case "Bar chart": + widgetCode = Widgets.BarChart + default: + break + } + + if self.widgetType == widgetCode { + return + } + + for item in self.submenu.items { + if item.title == "Mini" || item.title == "Bar chart" { + item.state = NSControl.StateValue.off + } + } + + sender.state = sender.state == NSControl.StateValue.on ? NSControl.StateValue.off : NSControl.StateValue.on + self.defaults.set(widgetCode, forKey: "\(name)_widget") + self.widgetType = widgetCode + self.active << false + self.initMenu() + self.initWidget() + self.active << true + } + @objc func toggleColor(_ sender: NSMenuItem) { sender.state = sender.state == NSControl.StateValue.on ? NSControl.StateValue.off : NSControl.StateValue.on self.defaults.set(sender.state == NSControl.StateValue.on, forKey: "\(name)_color") self.color << (sender.state == NSControl.StateValue.on) } + + @objc func toggleLabel(_ sender: NSMenuItem) { + sender.state = sender.state == NSControl.StateValue.on ? NSControl.StateValue.off : NSControl.StateValue.on + self.defaults.set(sender.state == NSControl.StateValue.on, forKey: "\(name)_label") + self.active << false + self.label << (sender.state == NSControl.StateValue.on) + self.active << true + } } diff --git a/Stats/Modules/Disk/DiskReader.swift b/Stats/Modules/Disk/DiskReader.swift index 582c1a16..f7714c46 100644 --- a/Stats/Modules/Disk/DiskReader.swift +++ b/Stats/Modules/Disk/DiskReader.swift @@ -9,12 +9,12 @@ import Foundation class DiskReader: Reader { - var value: Observable! + var value: Observable<[Double]>! var available: Bool = true var updateTimer: Timer! init() { - self.value = Observable(0) + self.value = Observable([]) read() } @@ -38,7 +38,7 @@ class DiskReader: Reader { let free = freeDiskSpaceInBytes() let usedSpace = total - free - self.value << (Double(usedSpace) / Double(total)) + self.value << [(Double(usedSpace) / Double(total))] } func totalDiskSpaceInBytes() -> Int64 { diff --git a/Stats/Modules/Memory/Memory.swift b/Stats/Modules/Memory/Memory.swift index d88b7c61..20ed211a 100644 --- a/Stats/Modules/Memory/Memory.swift +++ b/Stats/Modules/Memory/Memory.swift @@ -30,7 +30,7 @@ class Memory: Module { self.active = Observable(defaults.object(forKey: name) != nil ? defaults.bool(forKey: name) : true) self.widgetType = defaults.object(forKey: "\(name)_widget") != nil ? defaults.float(forKey: "\(name)_widget") : Widgets.Mini self.color = Observable(defaults.object(forKey: "\(name)_color") != nil ? defaults.bool(forKey: "\(name)_color") : false) - self.label = Observable(defaults.object(forKey: "\(name)_label") != nil ? defaults.bool(forKey: "\(name)_label") : false) + self.label = Observable(defaults.object(forKey: "\(name)_label") != nil ? defaults.bool(forKey: "\(name)_label") : true) initMenu() initWidget() } @@ -58,22 +58,31 @@ class Memory: Module { chartWithValue.state = self.widgetType == Widgets.ChartWithValue ? NSControl.StateValue.on : NSControl.StateValue.off chartWithValue.target = self + let barChart = NSMenuItem(title: "Bar chart", action: #selector(toggleWidget), keyEquivalent: "") + barChart.state = self.widgetType == Widgets.BarChart ? NSControl.StateValue.on : NSControl.StateValue.off + barChart.target = self + let color = NSMenuItem(title: "Color", action: #selector(toggleColor), keyEquivalent: "") - color.state = defaults.bool(forKey: "\(name)_color") ? NSControl.StateValue.on : NSControl.StateValue.off + color.state = self.color.value ? NSControl.StateValue.on : NSControl.StateValue.off color.target = self let label = NSMenuItem(title: "Label", action: #selector(toggleLabel), keyEquivalent: "") - label.state = defaults.bool(forKey: "\(name)_label") || defaults.object(forKey: "\(name)_label") == nil ? NSControl.StateValue.on : NSControl.StateValue.off + label.state = self.label.value ? NSControl.StateValue.on : NSControl.StateValue.off label.target = self submenu.addItem(mini) submenu.addItem(chart) submenu.addItem(chartWithValue) + submenu.addItem(barChart) submenu.addItem(NSMenuItem.separator()) - submenu.addItem(label) - submenu.addItem(color) + if self.widgetType == Widgets.BarChart || self.widgetType == Widgets.ChartWithValue || self.widgetType == Widgets.Chart { + submenu.addItem(label) + } + if self.widgetType == Widgets.Mini || self.widgetType == Widgets.ChartWithValue { + submenu.addItem(color) + } menu.submenu = submenu } @@ -115,7 +124,7 @@ class Memory: Module { } for item in self.submenu.items { - if item.title == "Mini" || item.title == "Chart" || item.title == "Chart with value" { + if item.title == "Mini" || item.title == "Chart" || item.title == "Chart with value" || item.title == "Bar chart" { item.state = NSControl.StateValue.off } } @@ -125,6 +134,7 @@ class Memory: Module { self.widgetType = widgetCode self.active << false self.initWidget() + self.initMenu() self.active << true } diff --git a/Stats/Modules/Memory/MemoryReader.swift b/Stats/Modules/Memory/MemoryReader.swift index 2df61bb5..58101500 100644 --- a/Stats/Modules/Memory/MemoryReader.swift +++ b/Stats/Modules/Memory/MemoryReader.swift @@ -9,13 +9,13 @@ import Foundation class MemoryReader: Reader { - var value: Observable! + var value: Observable<[Double]>! var available: Bool = true var updateTimer: Timer! var totalSize: Float init() { - self.value = Observable(0) + self.value = Observable([]) var stats = host_basic_info() var count = UInt32(MemoryLayout.size / MemoryLayout.size) @@ -68,7 +68,7 @@ class MemoryReader: Reader { let compressed = Float(stats.compressor_page_count) * Float(PAGE_SIZE) let free = totalSize - (active + wired + compressed) - self.value << Double((totalSize - free) / totalSize) + self.value << [Double((totalSize - free) / totalSize)] } else { print("Error with host_statistics64(): " + (String(cString: mach_error_string(kerr), encoding: String.Encoding.ascii) ?? "unknown error")) diff --git a/Stats/Modules/Module.swift b/Stats/Modules/Module.swift index 78ce928c..d7e5d248 100644 --- a/Stats/Modules/Module.swift +++ b/Stats/Modules/Module.swift @@ -55,26 +55,26 @@ extension Module { } widget.label = self.shortName - widget.color(state: self.color.value) - widget.label(state: self.label.value) + widget.toggleColor(state: self.color.value) + widget.toggleLabel(state: self.label.value) self.view = widget as! NSView } func start() { - if !self.reader.value.value.isNaN { + if !self.reader.value.value.isEmpty { guard let widget = self.view as? Widget else { return } - widget.value(value: self.reader.value.value) + widget.setValue(data: self.reader.value.value) } self.reader.start() self.reader.value.subscribe(observer: self) { (value, _) in - if !value.isNaN { + if !value.isEmpty { guard let widget = self.view as? Widget else { return } - widget.value(value: value) + widget.setValue(data: value) } } @@ -82,7 +82,7 @@ extension Module { guard let widget = self.view as? Widget else { return } - widget.color(state: value) + widget.toggleColor(state: value) widget.redraw() } @@ -90,7 +90,7 @@ extension Module { guard let widget = self.view as? Widget else { return } - widget.label(state: value) + widget.toggleLabel(state: value) } } diff --git a/Stats/Modules/Network/Network.swift b/Stats/Modules/Network/Network.swift index a71cd53c..11d341a3 100644 --- a/Stats/Modules/Network/Network.swift +++ b/Stats/Modules/Network/Network.swift @@ -37,8 +37,8 @@ class Network: Module { self.reader.start() self.reader.value.subscribe(observer: self) { (value, _) in - if !value.isNaN { - (self.view as! Widget).value(value: value) + if !value.isEmpty { + (self.view as! Widget).setValue(data: value) } } } diff --git a/Stats/Modules/Network/NetworkReader.swift b/Stats/Modules/Network/NetworkReader.swift index 6e3ad2e2..ae18a2c9 100644 --- a/Stats/Modules/Network/NetworkReader.swift +++ b/Stats/Modules/Network/NetworkReader.swift @@ -9,7 +9,7 @@ import Cocoa class NetworkReader: Reader { - var value: Observable! + var value: Observable<[Double]>! var available: Bool = true var updateTimer: Timer! @@ -17,7 +17,7 @@ class NetworkReader: Reader { var pipe: Pipe = Pipe() init() { - self.value = Observable(0) + self.value = Observable([]) netProcess.launchPath = "/usr/bin/env" netProcess.arguments = ["netstat", "-w1", "-l", "en0"] netProcess.standardOutput = pipe @@ -51,7 +51,7 @@ class NetworkReader: Reader { return } - self.value << value + self.value << [value] } } diff --git a/Stats/Modules/Reader.swift b/Stats/Modules/Reader.swift index 18d60b65..ae97568a 100644 --- a/Stats/Modules/Reader.swift +++ b/Stats/Modules/Reader.swift @@ -9,7 +9,7 @@ import Foundation protocol Reader { - var value: Observable! { get } + var value: Observable<[Double]>! { get } var available: Bool { get } var updateTimer: Timer! { get set } diff --git a/Stats/Supporting Files/Info.plist b/Stats/Supporting Files/Info.plist index 8fe65cd5..90fa3e4c 100755 --- a/Stats/Supporting Files/Info.plist +++ b/Stats/Supporting Files/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.2.4 + 1.2.5 CFBundleVersion 1 LSApplicationCategoryType diff --git a/Stats/Widgets/BarChart.swift b/Stats/Widgets/BarChart.swift index 5938a9b4..4daec4b4 100644 --- a/Stats/Widgets/BarChart.swift +++ b/Stats/Widgets/BarChart.swift @@ -41,9 +41,8 @@ class BarChart: NSView, Widget { super.draw(dirtyRect) let gradientColor: NSColor = NSColor(red: (26/255.0), green: (126/255.0), blue: (252/255.0), alpha: 0.8) - let width = self.frame.size.width - (MODULE_MARGIN * 2) - let height = self.frame.size.height - (MODULE_MARGIN * 2) + 1 + let height = self.frame.size.height - (MODULE_MARGIN * 2) var x = MODULE_MARGIN if labelEnabled { @@ -59,7 +58,10 @@ class BarChart: NSView, Widget { for i in 0..