mirror of
https://github.com/morgan9e/macos-stats
synced 2026-04-15 00:34:08 +09:00
- update structure name in modules
- add arguments support (--reset, --disable) - update a README
This commit is contained in:
@@ -17,7 +17,9 @@ public protocol Module_p {
|
||||
var widget: Widget_p? { get }
|
||||
var settings: Settings_p? { get }
|
||||
|
||||
func load()
|
||||
func mount()
|
||||
func unmount()
|
||||
|
||||
func terminate()
|
||||
}
|
||||
|
||||
@@ -112,18 +114,18 @@ open class Module: Module_p {
|
||||
}
|
||||
|
||||
// load function which call when app start
|
||||
public func load() {
|
||||
if self.enabled && self.widget != nil && self.ready {
|
||||
DispatchQueue.main.async {
|
||||
self.menuBarItem.button?.target = self
|
||||
self.menuBarItem.button?.action = #selector(self.togglePopup)
|
||||
self.menuBarItem.button?.sendAction(on: [.leftMouseDown, .rightMouseDown])
|
||||
|
||||
self.menuBarItem.length = self.widget!.frame.width
|
||||
self.menuBarItem.button?.addSubview(self.widget!)
|
||||
self.widgetLoaded = true
|
||||
}
|
||||
public func mount() {
|
||||
guard self.enabled else {
|
||||
return
|
||||
}
|
||||
|
||||
self.readers.forEach{ $0.start() }
|
||||
}
|
||||
|
||||
// disable module
|
||||
public func unmount() {
|
||||
self.enabled = false
|
||||
self.available = false
|
||||
}
|
||||
|
||||
// terminate function which call before app termination
|
||||
@@ -147,7 +149,7 @@ open class Module: Module_p {
|
||||
self.readers.forEach{ $0.start() }
|
||||
self.menuBarItem.isVisible = true
|
||||
if self.menuBarItem.length < 0 {
|
||||
self.load()
|
||||
self.loadWidget()
|
||||
}
|
||||
os_log(.debug, log: log, "Module enabled")
|
||||
}
|
||||
@@ -173,20 +175,17 @@ open class Module: Module_p {
|
||||
|
||||
// add reader to module. If module is enabled will fire a read function and start a reader
|
||||
public func addReader(_ reader: Reader_p) {
|
||||
if self.enabled {
|
||||
reader.start()
|
||||
}
|
||||
self.readers.append(reader)
|
||||
|
||||
os_log(.debug, log: log, "Successfully add reader %s", "\(reader.self)")
|
||||
os_log(.debug, log: log, "Reader %s was added", "\(reader.self)")
|
||||
}
|
||||
|
||||
// handler for reader, calls when main reader is ready, and return first value
|
||||
public func readyHandler() {
|
||||
os_log(.debug, log: log, "Reader report readiness")
|
||||
self.ready = true
|
||||
|
||||
if !self.widgetLoaded {
|
||||
self.load()
|
||||
self.loadWidget()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,6 +198,21 @@ open class Module: Module_p {
|
||||
// determine if module is available (can be overrided in module)
|
||||
open func isAvailable() -> Bool { return true }
|
||||
|
||||
// setup menu ber item
|
||||
private func loadWidget() {
|
||||
if self.widget != nil && self.ready {
|
||||
DispatchQueue.main.async {
|
||||
self.menuBarItem.button?.target = self
|
||||
self.menuBarItem.button?.action = #selector(self.togglePopup)
|
||||
self.menuBarItem.button?.sendAction(on: [.leftMouseDown, .rightMouseDown])
|
||||
|
||||
self.menuBarItem.length = self.widget!.frame.width
|
||||
self.menuBarItem.button?.addSubview(self.widget!)
|
||||
self.widgetLoaded = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
@@ -69,8 +69,10 @@ open class Reader<T>: ReaderInternal_p {
|
||||
public func callback(_ value: T?) {
|
||||
if !self.optional && !self.ready {
|
||||
if self.value == nil && value != nil {
|
||||
self.ready = true
|
||||
self.readyCallback()
|
||||
} else if self.value == nil && value == nil {
|
||||
os_log(.debug, log: self.log, "Reader is ready")
|
||||
} else if self.value == nil && value != nil {
|
||||
if self.nilCallbackCounter > 5 {
|
||||
os_log(.error, log: self.log, "Callback receive nil value more than 5 times. Please check this reader!")
|
||||
self.stop()
|
||||
@@ -87,10 +89,6 @@ open class Reader<T>: ReaderInternal_p {
|
||||
}
|
||||
|
||||
self.value = value
|
||||
if !self.ready {
|
||||
self.ready = true
|
||||
os_log(.debug, log: self.log, "Reader is ready")
|
||||
}
|
||||
if value != nil {
|
||||
if self.history?.count ?? 0 >= 300 {
|
||||
self.history!.remove(at: 0)
|
||||
|
||||
@@ -14,7 +14,7 @@ import StatsKit
|
||||
import ModuleKit
|
||||
import IOKit.ps
|
||||
|
||||
struct Usage: value_t {
|
||||
struct Battery_Usage: value_t {
|
||||
var powerSource: String = ""
|
||||
var state: String = ""
|
||||
var isCharged: Bool = false
|
||||
@@ -66,7 +66,7 @@ public class Battery: Module {
|
||||
return sources.count > 0
|
||||
}
|
||||
|
||||
private func usageCallback(_ value: Usage?) {
|
||||
private func usageCallback(_ value: Battery_Usage?) {
|
||||
if value == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ internal class Popup: NSView {
|
||||
self.adapterView = view
|
||||
}
|
||||
|
||||
public func usageCallback(_ value: Usage) {
|
||||
public func usageCallback(_ value: Battery_Usage) {
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.dashboardBatteryView?.setValue(abs(value.level))
|
||||
|
||||
|
||||
@@ -12,13 +12,13 @@
|
||||
import Cocoa
|
||||
import ModuleKit
|
||||
|
||||
internal class UsageReader: Reader<Usage> {
|
||||
internal class UsageReader: Reader<Battery_Usage> {
|
||||
private var service: io_connect_t = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleSmartBattery"))
|
||||
|
||||
private var source: CFRunLoopSource?
|
||||
private var loop: CFRunLoop?
|
||||
|
||||
private var usage: Usage = Usage()
|
||||
private var usage: Battery_Usage = Battery_Usage()
|
||||
|
||||
public override func start() {
|
||||
let context = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())
|
||||
|
||||
@@ -10,7 +10,7 @@ import Cocoa
|
||||
import ModuleKit
|
||||
import StatsKit
|
||||
|
||||
public struct Load: value_t {
|
||||
public struct CPU_Load: value_t {
|
||||
var totalUsage: Double = 0
|
||||
var usagePerCore: [Double] = []
|
||||
|
||||
@@ -35,13 +35,13 @@ public class CPU: Module {
|
||||
private let popupView: Popup = Popup()
|
||||
private var settingsView: Settings
|
||||
|
||||
private var loadReader: LoadReader = LoadReader()
|
||||
private var loadReader: LoadReader? = LoadReader()
|
||||
private let smc: UnsafePointer<SMCService>?
|
||||
|
||||
public init(_ store: UnsafePointer<Store>, _ smc: UnsafePointer<SMCService>) {
|
||||
self.smc = smc
|
||||
self.settingsView = Settings("CPU", store: store)
|
||||
self.loadReader.store = store
|
||||
self.loadReader!.store = store
|
||||
|
||||
super.init(
|
||||
store: store,
|
||||
@@ -50,20 +50,20 @@ public class CPU: Module {
|
||||
)
|
||||
|
||||
self.settingsView.callback = { [unowned self] in
|
||||
self.loadReader.read()
|
||||
self.loadReader?.read()
|
||||
}
|
||||
|
||||
self.loadReader.readyCallback = { [unowned self] in
|
||||
self.loadReader?.readyCallback = { [unowned self] in
|
||||
self.readyHandler()
|
||||
}
|
||||
self.loadReader.callbackHandler = { [unowned self] value in
|
||||
self.loadReader?.callbackHandler = { [unowned self] value in
|
||||
self.loadCallback(value)
|
||||
}
|
||||
|
||||
self.addReader(self.loadReader)
|
||||
self.addReader(self.loadReader!)
|
||||
}
|
||||
|
||||
private func loadCallback(_ value: Load?) {
|
||||
private func loadCallback(_ value: CPU_Load?) {
|
||||
if value == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ internal class Popup: NSView {
|
||||
return valueView
|
||||
}
|
||||
|
||||
public func loadCallback(_ value: Load, tempValue: Double?) {
|
||||
public func loadCallback(_ value: CPU_Load, tempValue: Double?) {
|
||||
var temperature: String = "Unknown"
|
||||
|
||||
DispatchQueue.main.async(execute: {
|
||||
|
||||
@@ -13,7 +13,7 @@ import Cocoa
|
||||
import StatsKit
|
||||
import ModuleKit
|
||||
|
||||
internal class LoadReader: Reader<Load> {
|
||||
internal class LoadReader: Reader<CPU_Load> {
|
||||
public var store: UnsafePointer<Store>? = nil
|
||||
|
||||
private var cpuInfo: processor_info_array_t!
|
||||
@@ -24,7 +24,7 @@ internal class LoadReader: Reader<Load> {
|
||||
private let CPUUsageLock: NSLock = NSLock()
|
||||
private var previousInfo = host_cpu_load_info()
|
||||
|
||||
private var response: Load = Load()
|
||||
private var response: CPU_Load = CPU_Load()
|
||||
private var numCPUsU: natural_t = 0
|
||||
private var usagePerCore: [Double] = []
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import Cocoa
|
||||
import StatsKit
|
||||
import ModuleKit
|
||||
|
||||
public struct Usage: value_t {
|
||||
public struct RAM_Usage: value_t {
|
||||
var active: Double?
|
||||
var inactive: Double?
|
||||
var wired: Double?
|
||||
@@ -52,7 +52,7 @@ public class Memory: Module {
|
||||
self.addReader(self.usageReader)
|
||||
}
|
||||
|
||||
private func loadCallback(value: Usage?) {
|
||||
private func loadCallback(value: RAM_Usage?) {
|
||||
if value == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ internal class Popup: NSView {
|
||||
return valueView
|
||||
}
|
||||
|
||||
public func loadCallback(_ value: Usage) {
|
||||
public func loadCallback(_ value: RAM_Usage) {
|
||||
DispatchQueue.main.async(execute: {
|
||||
if self.window!.isVisible || !self.initialized {
|
||||
self.activeField?.stringValue = Units(bytes: Int64(value.active!)).getReadableMemory()
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
import Cocoa
|
||||
import ModuleKit
|
||||
|
||||
internal class UsageReader: Reader<Usage> {
|
||||
internal class UsageReader: Reader<RAM_Usage> {
|
||||
public var totalSize: Double = 0
|
||||
|
||||
public override func setup() {
|
||||
@@ -53,7 +53,7 @@ internal class UsageReader: Reader<Usage> {
|
||||
let used = active + wired + compressed
|
||||
let free = self.totalSize - used
|
||||
|
||||
self.callback(Usage(
|
||||
self.callback(RAM_Usage(
|
||||
active: active,
|
||||
inactive: inactive,
|
||||
wired: wired,
|
||||
|
||||
@@ -18,7 +18,7 @@ public enum Network_t: String {
|
||||
case ethernet
|
||||
}
|
||||
|
||||
public struct Usage: value_t {
|
||||
public struct Network_Usage: value_t {
|
||||
var active: Bool = false
|
||||
|
||||
var download: Int64 = 0
|
||||
@@ -73,7 +73,7 @@ public class Network: Module {
|
||||
self.addReader(self.usageReader)
|
||||
}
|
||||
|
||||
private func usageCallback(_ value: Usage?) {
|
||||
private func usageCallback(_ value: Network_Usage?) {
|
||||
if value == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ internal class Popup: NSView {
|
||||
self.addSubview(view)
|
||||
}
|
||||
|
||||
public func usageCallback(_ value: Usage) {
|
||||
public func usageCallback(_ value: Network_Usage) {
|
||||
DispatchQueue.main.async(execute: {
|
||||
if !self.window!.isVisible && self.initialized && value.active {
|
||||
return
|
||||
|
||||
@@ -16,9 +16,9 @@ import Reachability
|
||||
import os.log
|
||||
import CoreWLAN
|
||||
|
||||
internal class UsageReader: Reader<Usage> {
|
||||
internal class UsageReader: Reader<Network_Usage> {
|
||||
private var reachability: Reachability? = nil
|
||||
private var usage: Usage = Usage()
|
||||
private var usage: Network_Usage = Network_Usage()
|
||||
|
||||
private var interfaceID: String? = nil
|
||||
|
||||
|
||||
13
README.md
13
README.md
@@ -1,6 +1,6 @@
|
||||
# Stats
|
||||
|
||||
<p align="center"><img src="https://serhiy.s3.eu-central-1.amazonaws.com/Github_repo/stats/logo.png?raw=true" width="120"></p>
|
||||
<a href="https://github.com/exelban/stats/releases"><p align="center"><img src="https://serhiy.s3.eu-central-1.amazonaws.com/Github_repo/stats/logo.png?raw=true" width="120"></p></a>
|
||||
|
||||
[](https://github.com/exelban/stats/releases)
|
||||
|
||||
@@ -25,14 +25,23 @@ Stats is a application which allows you to monitor your macOS system.
|
||||
- CPU Usage
|
||||
- Memory Usage
|
||||
- Disk utilization
|
||||
- Sensors information (Temperature/Voltage/Power)
|
||||
- Battery level
|
||||
- Network usage
|
||||
|
||||
## Troubleshoots
|
||||
The application supports a few arguments which can help to work with Stats. Also, it's very helpful to debug what module is not working properly (crash).
|
||||
|
||||
There are 2 arguments available:
|
||||
|
||||
- `--reset`: allows to reset application settings
|
||||
- `--disable`: allow to disable some of the modules. A list of modules can be passed. (Example: `--disable disk`)
|
||||
|
||||
## Developing
|
||||
|
||||
Pull requests and impovment proposals are welcomed.
|
||||
|
||||
If you want to run the project locally you need to have [carthage](https://github.com/Carthage/Carthage#installing-carthage) installed.
|
||||
If you want to run the project locally you need to have [carthage](https://github.com/Carthage/Carthage#installing-carthage) and [XCode](https://apps.apple.com/app/xcode/id497799835) installed.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/exelban/stats
|
||||
|
||||
@@ -59,6 +59,16 @@
|
||||
ReferencedContainer = "container:Stats.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<CommandLineArguments>
|
||||
<CommandLineArgument
|
||||
argument = "--reset"
|
||||
isEnabled = "NO">
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = "--disable"
|
||||
isEnabled = "NO">
|
||||
</CommandLineArgument>
|
||||
</CommandLineArguments>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "DYLD_PRINT_STATISTICS"
|
||||
@@ -71,13 +81,6 @@
|
||||
isEnabled = "NO">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
<AdditionalOptions>
|
||||
<AdditionalOption
|
||||
key = "MallocStackLogging"
|
||||
value = ""
|
||||
isEnabled = "YES">
|
||||
</AdditionalOption>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Debug"
|
||||
|
||||
@@ -31,10 +31,12 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
let startingPoint = Date()
|
||||
|
||||
self.parseArguments()
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(toggleSettingsHandler), name: .toggleSettings, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(checkForUpdates), name: .checkForUpdates, object: nil)
|
||||
|
||||
modules.forEach{ $0.load() }
|
||||
modules.forEach{ $0.mount() }
|
||||
|
||||
self.settingsWindow.setModules()
|
||||
|
||||
@@ -97,6 +99,27 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
store.set(key: key, value: currentVersion)
|
||||
}
|
||||
|
||||
private func parseArguments() {
|
||||
let args = CommandLine.arguments
|
||||
|
||||
if args.contains("--reset") {
|
||||
os_log(.info, log: log, "Receive --reset argument. Reseting store (UserDefaults)...")
|
||||
store.reset()
|
||||
}
|
||||
|
||||
if let disableIndex = args.firstIndex(of: "--disable") {
|
||||
if args.indices.contains(disableIndex+1) {
|
||||
let disableModules = args[disableIndex+1].split(separator: ",")
|
||||
|
||||
disableModules.forEach { (moduleName: Substring) in
|
||||
if let module = modules.first(where: { $0.config.name.lowercased() == moduleName.lowercased()}) {
|
||||
module.unmount()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func defaultValues() {
|
||||
if !store.exist(key: "runAtLoginInitialized") {
|
||||
store.set(key: "runAtLoginInitialized", value: true)
|
||||
|
||||
Reference in New Issue
Block a user