- add temperature module (CPU and GPU temperature);

- fix value margin in network widget view;
- add missing widgets name;
- improve battery module updates;
This commit is contained in:
Serhiy Mytrovtsiy
2020-04-04 11:57:38 +02:00
parent e6b942579e
commit b448979aa2
29 changed files with 959 additions and 133 deletions

View File

@@ -51,6 +51,11 @@
9A6CFC0122A1C9F5001E782D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9A6CFC0022A1C9F5001E782D /* Assets.xcassets */; };
9A74D59722B44498004FE1FA /* Mini.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A74D59622B44498004FE1FA /* Mini.swift */; };
9A79B36A22D3BEE600BF1C3A /* Widget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A79B36922D3BEE600BF1C3A /* Widget.swift */; };
9AA28DC1243774ED00D2B196 /* Temperature.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA28DC0243774ED00D2B196 /* Temperature.swift */; };
9AA28DC32437752D00D2B196 /* TemperatureMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA28DC22437752D00D2B196 /* TemperatureMenu.swift */; };
9AA28DC52437762C00D2B196 /* TemperatureReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA28DC42437762C00D2B196 /* TemperatureReader.swift */; };
9AA28DCF2437884200D2B196 /* SystemKit.c in Sources */ = {isa = PBXBuildFile; fileRef = 9AA28DCE2437884200D2B196 /* SystemKit.c */; };
9AA28DD1243799E500D2B196 /* TemperatureWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA28DD0243799E500D2B196 /* TemperatureWidget.swift */; };
9AF0F31B22DA924000026AE6 /* LineChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0F31A22DA924000026AE6 /* LineChart.swift */; };
9AF0F31D22DA925000026AE6 /* LineChartWithValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0F31C22DA925000026AE6 /* LineChartWithValue.swift */; };
9AF0F31F22DA925700026AE6 /* BarChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AF0F31E22DA925700026AE6 /* BarChart.swift */; };
@@ -139,6 +144,13 @@
9A79B36922D3BEE600BF1C3A /* Widget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Widget.swift; sourceTree = "<group>"; };
9A998CD722A199920087ADE7 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
9A998CD922A199970087ADE7 /* ServiceManagement.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ServiceManagement.framework; path = System/Library/Frameworks/ServiceManagement.framework; sourceTree = SDKROOT; };
9AA28DC0243774ED00D2B196 /* Temperature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Temperature.swift; sourceTree = "<group>"; };
9AA28DC22437752D00D2B196 /* TemperatureMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemperatureMenu.swift; sourceTree = "<group>"; };
9AA28DC42437762C00D2B196 /* TemperatureReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemperatureReader.swift; sourceTree = "<group>"; };
9AA28DC9243780C500D2B196 /* Stats.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Stats.h; sourceTree = "<group>"; };
9AA28DCD2437884200D2B196 /* SystemKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SystemKit.h; sourceTree = "<group>"; };
9AA28DCE2437884200D2B196 /* SystemKit.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = SystemKit.c; sourceTree = "<group>"; };
9AA28DD0243799E500D2B196 /* TemperatureWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemperatureWidget.swift; sourceTree = "<group>"; };
9AF0F31A22DA924000026AE6 /* LineChart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineChart.swift; sourceTree = "<group>"; };
9AF0F31C22DA925000026AE6 /* LineChartWithValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineChartWithValue.swift; sourceTree = "<group>"; };
9AF0F31E22DA925700026AE6 /* BarChart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarChart.swift; sourceTree = "<group>"; };
@@ -283,6 +295,7 @@
9A2D15F123CE390500C4C417 /* Disk */,
9A2D15F823CE3BDA00C4C417 /* Battery */,
9A2D160123CE444D00C4C417 /* Network */,
9AA28DBF243774DD00D2B196 /* Temperature */,
9A2D15D123CCEC7600C4C417 /* Module.swift */,
);
path = Modules;
@@ -295,6 +308,9 @@
9A426DB722C2B5EE00C064C4 /* MacAppUpdater.swift */,
9A59AE55231EE02F007989D6 /* ChartMarker.swift */,
9A2D15CF23C77BA300C4C417 /* Repeater.swift */,
9AA28DC9243780C500D2B196 /* Stats.h */,
9AA28DCD2437884200D2B196 /* SystemKit.h */,
9AA28DCE2437884200D2B196 /* SystemKit.c */,
);
path = libs;
sourceTree = "<group>";
@@ -305,6 +321,7 @@
9A54EF65232AB48100F7DC20 /* Battery */,
9AF0F31922DA923100026AE6 /* Network */,
9AF0F31822DA922800026AE6 /* Charts */,
9AA28DD224379F8700D2B196 /* Temperature */,
9A74D59622B44498004FE1FA /* Mini.swift */,
9A79B36922D3BEE600BF1C3A /* Widget.swift */,
);
@@ -324,6 +341,24 @@
name = Frameworks;
sourceTree = "<group>";
};
9AA28DBF243774DD00D2B196 /* Temperature */ = {
isa = PBXGroup;
children = (
9AA28DC0243774ED00D2B196 /* Temperature.swift */,
9AA28DC22437752D00D2B196 /* TemperatureMenu.swift */,
9AA28DC42437762C00D2B196 /* TemperatureReader.swift */,
);
path = Temperature;
sourceTree = "<group>";
};
9AA28DD224379F8700D2B196 /* Temperature */ = {
isa = PBXGroup;
children = (
9AA28DD0243799E500D2B196 /* TemperatureWidget.swift */,
);
path = Temperature;
sourceTree = "<group>";
};
9AF0F31822DA922800026AE6 /* Charts */ = {
isa = PBXGroup;
children = (
@@ -483,8 +518,10 @@
9A2D15E623CE291600C4C417 /* RAM.swift in Sources */,
9A09C8A222B3D94D0018426F /* BatteryWidget.swift in Sources */,
9A5349C723D8535900C23824 /* NetworkPopup.swift in Sources */,
9AA28DC32437752D00D2B196 /* TemperatureMenu.swift in Sources */,
9A426DB822C2B5EE00C064C4 /* MacAppUpdater.swift in Sources */,
9A606B4C232157BA00642F51 /* AboutViewController.swift in Sources */,
9AA28DC1243774ED00D2B196 /* Temperature.swift in Sources */,
9AF0F32522DA92C400026AE6 /* NetworkText.swift in Sources */,
9AF0F32322DA92B900026AE6 /* NetworkArrows.swift in Sources */,
9A2D15D223CCEC7600C4C417 /* Module.swift in Sources */,
@@ -493,8 +530,11 @@
9A606B4A2321577400642F51 /* UpdatesViewController.swift in Sources */,
9AF0F31D22DA925000026AE6 /* LineChartWithValue.swift in Sources */,
9A2D160723CE462400C4C417 /* NetworkReader.swift in Sources */,
9AA28DD1243799E500D2B196 /* TemperatureWidget.swift in Sources */,
9A2D15FA23CE3BE600C4C417 /* Battery.swift in Sources */,
9A54EF67232AB81800F7DC20 /* BatteryPercentageWidget.swift in Sources */,
9AA28DCF2437884200D2B196 /* SystemKit.c in Sources */,
9AA28DC52437762C00D2B196 /* TemperatureReader.swift in Sources */,
9A57A18522A1D26D0033E318 /* MenuBar.swift in Sources */,
9A2D15D923CD036400C4C417 /* CPUMenu.swift in Sources */,
9AF6F1FE231D732600B8E1E4 /* PopupViewController.swift in Sources */,
@@ -683,6 +723,7 @@
PRODUCT_BUNDLE_IDENTIFIER = eu.exelban.Stats;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/$(PROJECT_NAME)/libs/$(SWIFT_MODULE_NAME).h";
SWIFT_VERSION = 5.0;
};
name = Debug;
@@ -712,6 +753,7 @@
PRODUCT_BUNDLE_IDENTIFIER = eu.exelban.Stats;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/$(PROJECT_NAME)/libs/$(SWIFT_MODULE_NAME).h";
SWIFT_VERSION = 5.0;
};
name = Release;

View File

@@ -20,6 +20,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
private let popover = NSPopover()
func applicationDidFinishLaunching(_ aNotification: Notification) {
SMCOpen();
guard let menuBarButton = self.menuBarItem.button else {
NSApp.terminate(nil)
return
@@ -40,6 +42,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}
func applicationWillTerminate(_ aNotification: Notification) {
SMCClose()
menuBar?.destroy()
}

View File

@@ -13,7 +13,7 @@ import ServiceManagement
Class keeps a status bar item and has the main function for updating widgets.
*/
class MenuBar {
public let modules: [Module] = [CPU(), RAM(), Disk(), Battery(), Network()]
public let modules: [Module] = [CPU(), RAM(), Temperature(), Disk(), Battery(), Network()]
private let menuBarItem: NSStatusItem
private var menuBarButton: NSButton = NSButton()

View File

@@ -56,24 +56,14 @@ class Battery: Module {
self.initPopup()
readers.append(BatteryReader(self.usageUpdater))
self.task = Repeater.init(interval: .seconds(self.updateInterval), observer: { _ in
self.readers.forEach { reader in
reader.read()
}
})
}
public func start() {
if self.task != nil && self.task!.state.isRunning == false {
self.task!.start()
}
(readers[0] as! BatteryReader).start()
}
public func stop() {
if self.task != nil && self.task!.state.isRunning {
self.task?.pause()
}
(readers[0] as! BatteryReader).stop()
}
public func restart() {
@@ -90,11 +80,11 @@ class Battery: Module {
}
if self.widget.view is Widget {
(self.widget.view as! Widget).setValue(data: [abs(value.capacity), Double(time)])
(self.widget.view as! Widget).setValue(data: [abs(value.level), Double(time)])
if self.widget.view is BatteryWidget && value.capacity != 100 {
(self.widget.view as! BatteryWidget).setCharging(value: value.capacity > 0)
} else if self.widget.view is BatteryWidget && value.capacity == 100 {
if self.widget.view is BatteryWidget && value.level != 100 {
(self.widget.view as! BatteryWidget).setCharging(value: value.level > 0)
} else if self.widget.view is BatteryWidget && value.level == 100 {
(self.widget.view as! BatteryWidget).setCharging(value: false)
}
}

View File

@@ -40,9 +40,6 @@ extension Battery {
}
}
submenu.addItem(NSMenuItem.separator())
submenu.addItem(generateIntervalMenu())
if self.enabled {
menu.submenu = submenu
}
@@ -94,71 +91,4 @@ extension Battery {
self.initMenu()
menuBar!.reload(name: self.name)
}
private func generateIntervalMenu() -> NSMenuItem {
let updateInterval = NSMenuItem(title: "Update interval", action: nil, keyEquivalent: "")
let updateIntervals = NSMenu()
let updateInterval_1 = NSMenuItem(title: "1s", action: #selector(changeInterval), keyEquivalent: "")
updateInterval_1.state = self.updateInterval == 1 ? NSControl.StateValue.on : NSControl.StateValue.off
updateInterval_1.target = self
let updateInterval_2 = NSMenuItem(title: "3s", action: #selector(changeInterval), keyEquivalent: "")
updateInterval_2.state = self.updateInterval == 3 ? NSControl.StateValue.on : NSControl.StateValue.off
updateInterval_2.target = self
let updateInterval_3 = NSMenuItem(title: "5s", action: #selector(changeInterval), keyEquivalent: "")
updateInterval_3.state = self.updateInterval == 5 ? NSControl.StateValue.on : NSControl.StateValue.off
updateInterval_3.target = self
let updateInterval_4 = NSMenuItem(title: "10s", action: #selector(changeInterval), keyEquivalent: "")
updateInterval_4.state = self.updateInterval == 10 ? NSControl.StateValue.on : NSControl.StateValue.off
updateInterval_4.target = self
let updateInterval_5 = NSMenuItem(title: "15s", action: #selector(changeInterval), keyEquivalent: "")
updateInterval_5.state = self.updateInterval == 15 ? NSControl.StateValue.on : NSControl.StateValue.off
updateInterval_5.target = self
updateIntervals.addItem(updateInterval_1)
updateIntervals.addItem(updateInterval_2)
updateIntervals.addItem(updateInterval_3)
updateIntervals.addItem(updateInterval_4)
updateIntervals.addItem(updateInterval_5)
updateInterval.submenu = updateIntervals
return updateInterval
}
@objc func changeInterval(_ sender: NSMenuItem) {
var interval: Double = self.updateInterval
switch sender.title {
case "1s":
interval = 1
case "3s":
interval = 3
case "5s":
interval = 5
case "10s":
interval = 10
case "15s":
interval = 15
default:
break
}
if interval == self.updateInterval {
return
}
for item in self.submenu.items {
if item.title == "Update interval" {
for subitem in item.submenu!.items {
subitem.state = NSControl.StateValue.off
}
}
}
sender.state = NSControl.StateValue.on
self.updateInterval = interval
self.defaults.set(interval, forKey: "\(name)_interval")
self.task?.reset(.seconds(interval), restart: self.task!.state.isRunning)
}
}

View File

@@ -218,7 +218,7 @@ extension Battery {
self.popup.initialized = true
// makeMain
self.levelValue.stringValue = "\(Int(abs(value.capacity) * 100)) %"
self.levelValue.stringValue = "\(Int(abs(value.level) * 100)) %"
self.sourceValue.stringValue = value.powerSource
if value.powerSource == "Battery Power" {
self.timeLabel.stringValue = "Time to discharge"
@@ -252,6 +252,6 @@ extension Battery {
// makePowerAdapter
self.powerValue.stringValue = value.powerSource == "Battery Power" ? "Not connected" : "\(value.ACwatts) W"
self.chargingValue.stringValue = value.capacity > 0 ? "Yes" : "No"
self.chargingValue.stringValue = value.level > 0 ? "Yes" : "No"
}
}

View File

@@ -13,7 +13,7 @@ struct BatteryUsage {
var powerSource: String = ""
var state: String = ""
var isCharged: Bool = false
var capacity: Double = 0
var level: Double = 0
var cycles: Int = 0
var health: Int = 0
@@ -46,29 +46,49 @@ class BatteryReader: Reader {
public var initialized: Bool = false
public var callback: (BatteryUsage) -> Void = {_ in}
private var service: io_connect_t = 0
private var service: io_connect_t = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleSmartBattery"))
private var internalChecked: Bool = false
private var hasInternalBattery: Bool = false
private var source: CFRunLoopSource?
private var loop: CFRunLoop?
init(_ updater: @escaping (BatteryUsage) -> Void) {
self.callback = updater
self.service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleSmartBattery"))
if self.available {
DispatchQueue.global(qos: .default).async {
self.read()
}
}
self.read()
}
public func start() {
let context = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())
source = IOPSNotificationCreateRunLoopSource({ (context) in
guard let ctx = context else {
return
}
let watcher = Unmanaged<BatteryReader>.fromOpaque(ctx).takeUnretainedValue()
watcher.read()
}, context).takeRetainedValue()
loop = RunLoop.current.getCFRunLoop()
CFRunLoopAddSource(loop, source, .defaultMode)
}
public func stop() {
guard let runLoop = loop, let source = source else {
return
}
CFRunLoopRemoveSource(runLoop, source, .defaultMode)
}
public func read() {
if !self.enabled && self.initialized { return }
self.initialized = true
let psInfo = IOPSCopyPowerSourcesInfo().takeRetainedValue()
let psList = IOPSCopyPowerSourcesList(psInfo).takeRetainedValue() as [CFTypeRef]
for ps in psList {
if let list = IOPSGetPowerSourceDescription(psInfo, ps).takeUnretainedValue() as? Dictionary<String, Any> {
let powerSource = list[kIOPSPowerSourceStateKey] as? String ?? "AC Power"
@@ -80,42 +100,45 @@ class BatteryReader: Reader {
let timeToCharged = Int(list[kIOPSTimeToFullChargeKey] as! Int)
let cycles = self.getIntValue("CycleCount" as CFString) ?? 0
let maxCapacity = self.getIntValue("MaxCapacity" as CFString) ?? 1
let designCapacity = self.getIntValue("DesignCapacity" as CFString) ?? 1
let amperage = self.getIntValue("Amperage" as CFString) ?? 0
let voltage = self.getVoltage() ?? 0
let temperature = self.getTemperature() ?? 0
var ACwatts: Int = 0
if let ACDetails = IOPSCopyExternalPowerAdapterDetails() {
if let ACList = ACDetails.takeUnretainedValue() as? Dictionary<String, Any> {
ACwatts = Int(ACList[kIOPSPowerAdapterWattsKey] as! Int)
guard let watts = ACList[kIOPSPowerAdapterWattsKey] else {
return
}
ACwatts = Int(watts as! Int)
}
}
let ACstatus = self.getBoolValue("IsCharging" as CFString) ?? false
if powerSource == "Battery Power" {
cap = 0 - cap
}
DispatchQueue.main.async(execute: {
let usage = BatteryUsage(
powerSource: powerSource,
state: state,
isCharged: isCharged,
capacity: Double(cap),
level: Double(cap),
cycles: cycles,
health: (100 * maxCapacity) / designCapacity,
amperage: amperage,
voltage: voltage,
temperature: temperature,
ACwatts: ACwatts,
ACstatus: ACstatus,
timeToEmpty: timeToEmpty,
timeToCharge: timeToCharged
)
@@ -135,28 +158,28 @@ class BatteryReader: Reader {
}
return nil
}
private func getIntValue(_ identifier: CFString) -> Int? {
if let value = IORegistryEntryCreateCFProperty(self.service, identifier, kCFAllocatorDefault, 0) {
return value.takeRetainedValue() as? Int
}
return nil
}
private func getDoubleValue(_ identifier: CFString) -> Double? {
if let value = IORegistryEntryCreateCFProperty(self.service, identifier, kCFAllocatorDefault, 0) {
return value.takeRetainedValue() as? Double
}
return nil
}
private func getVoltage() -> Double? {
if let value = self.getDoubleValue("Voltage" as CFString) {
return value / 1000.0
}
return nil
}
private func getTemperature() -> Double? {
if let value = IORegistryEntryCreateCFProperty(self.service, "Temperature" as CFString, kCFAllocatorDefault, 0) {
return value.takeRetainedValue() as! Double / 100.0

View File

@@ -75,6 +75,8 @@ extension Module {
switch self.widget.type {
case Widgets.Mini:
widget = Mini()
case Widgets.Temperature:
widget = TemperatureWidget()
case Widgets.Chart:
widget = Chart()
case Widgets.ChartWithValue:

View File

@@ -0,0 +1,78 @@
//
// Temperature.swift
// Stats
//
// Created by Serhiy Mytrovtsiy on 03/04/2020.
// Copyright © 2020 Serhiy Mytrovtsiy. All rights reserved.
//
import Cocoa
class Temperature: Module {
public var name: String = "Temperature"
public var updateInterval: Double = 1
public var enabled: Bool = true
public var available: Bool = true
public var widget: ModuleWidget = ModuleWidget()
public var popup: ModulePopup = ModulePopup(false)
public var menu: NSMenuItem = NSMenuItem()
public var readers: [Reader] = []
public var task: Repeater?
internal let defaults = UserDefaults.standard
internal var submenu: NSMenu = NSMenu()
internal var cpu: String = SMC_TEMP_CPU_0_PROXIMITY
internal var gpu: String = SMC_TEMP_GPU_0_PROXIMITY
init() {
if !self.available { return }
self.enabled = defaults.object(forKey: name) != nil ? defaults.bool(forKey: name) : true
self.widget.type = defaults.object(forKey: "\(name)_widget") != nil ? defaults.float(forKey: "\(name)_widget") : Widgets.Temperature
self.cpu = (defaults.object(forKey: "\(name)_cpu") != nil ? defaults.string(forKey: "\(name)_cpu") : cpu)!
self.gpu = (defaults.object(forKey: "\(name)_gpu") != nil ? defaults.string(forKey: "\(name)_gpu") : gpu)!
self.initWidget()
self.initMenu()
readers.append(TemperatureReader(self.usageUpdater))
self.task = Repeater.init(interval: .seconds(self.updateInterval), observer: { _ in
self.readers.forEach { reader in
reader.read()
}
})
}
func start() {
if self.task != nil && self.task!.state.isRunning == false {
self.task!.start()
}
}
func stop() {
if self.task!.state.isRunning {
self.task?.pause()
}
}
func restart() {
self.stop()
self.start()
}
private func usageUpdater(value: TemperatureValue) {
if self.widget.view is Widget {
DispatchQueue.main.async(execute: {
let cpu: Double = self.cpu == SMC_TEMP_CPU_0_DIE ? value.CPUDie : value.CPUProximity
let gpu: Double = self.gpu == SMC_TEMP_GPU_0_DIODE ? value.GPUDie : value.GPUProximity
(self.widget.view as! Widget).setValue(data: [cpu, gpu])
})
}
}
}

View File

@@ -0,0 +1,127 @@
//
// TemperatureMenu.swift
// Stats
//
// Created by Serhiy Mytrovtsiy on 03/04/2020.
// Copyright © 2020 Serhiy Mytrovtsiy. All rights reserved.
//
import Cocoa
extension Temperature {
public func initMenu() {
menu = NSMenuItem(title: name, action: #selector(toggle), keyEquivalent: "")
submenu = NSMenu()
if defaults.object(forKey: name) != nil {
menu.state = defaults.bool(forKey: name) ? NSControl.StateValue.on : NSControl.StateValue.off
} else {
menu.state = NSControl.StateValue.on
}
menu.target = self
let cpuDie: NSMenuItem = NSMenuItem(title: "CPU Die", action: #selector(toggleCPU), keyEquivalent: "")
cpuDie.state = self.cpu == SMC_TEMP_CPU_0_DIE ? NSControl.StateValue.on : NSControl.StateValue.off
cpuDie.target = self
let cpuProximity: NSMenuItem = NSMenuItem(title: "CPU Proximity", action: #selector(toggleCPU), keyEquivalent: "")
cpuProximity.state = self.cpu == SMC_TEMP_CPU_0_PROXIMITY ? NSControl.StateValue.on : NSControl.StateValue.off
cpuProximity.target = self
let gpuDie: NSMenuItem = NSMenuItem(title: "GPU Die", action: #selector(toggleGPU), keyEquivalent: "")
gpuDie.state = self.gpu == SMC_TEMP_GPU_0_DIODE ? NSControl.StateValue.on : NSControl.StateValue.off
gpuDie.target = self
let gpuProximity: NSMenuItem = NSMenuItem(title: "GPU Proximity", action: #selector(toggleGPU), keyEquivalent: "")
gpuProximity.state = self.gpu == SMC_TEMP_GPU_0_PROXIMITY ? NSControl.StateValue.on : NSControl.StateValue.off
gpuProximity.target = self
submenu.addItem(cpuProximity)
submenu.addItem(cpuDie)
submenu.addItem(NSMenuItem.separator())
submenu.addItem(gpuProximity)
submenu.addItem(gpuDie)
submenu.addItem(NSMenuItem.separator())
if let view = self.widget.view as? Widget {
for widgetMenu in view.menus {
submenu.addItem(widgetMenu)
}
}
if self.enabled {
menu.submenu = submenu
}
}
@objc func toggle(_ sender: NSMenuItem) {
let state = sender.state != NSControl.StateValue.on
sender.state = sender.state == NSControl.StateValue.on ? NSControl.StateValue.off : NSControl.StateValue.on
self.defaults.set(state, forKey: name)
self.enabled = state
menuBar!.reload(name: self.name)
if !state {
menu.submenu = nil
} else {
menu.submenu = submenu
}
self.restart()
}
@objc func toggleCPU(_ sender: NSMenuItem) {
var cpu: String = sender.title
switch cpu {
case "CPU Die":
cpu = SMC_TEMP_CPU_0_DIE
case "CPU Proximity":
cpu = SMC_TEMP_CPU_0_PROXIMITY
default:
break
}
let state = sender.state == NSControl.StateValue.on
for item in self.submenu.items {
if item.title == "CPU Die" || item.title == "CPU Proximity" {
item.state = NSControl.StateValue.off
}
}
sender.state = state ? NSControl.StateValue.off : NSControl.StateValue.on
self.defaults.set(cpu, forKey: "\(name)_cpu")
self.cpu = cpu
self.initWidget()
self.initMenu()
menuBar!.reload(name: self.name)
}
@objc func toggleGPU(_ sender: NSMenuItem) {
var gpu: String = sender.title
switch gpu {
case "GPU Die":
gpu = SMC_TEMP_GPU_0_DIODE
case "GPU Proximity":
gpu = SMC_TEMP_GPU_0_PROXIMITY
default:
break
}
let state = sender.state == NSControl.StateValue.on
for item in self.submenu.items {
if item.title == "GPU Die" || item.title == "GPU Proximity" {
item.state = NSControl.StateValue.off
}
}
sender.state = state ? NSControl.StateValue.off : NSControl.StateValue.on
self.defaults.set(gpu, forKey: "\(name)_gpu")
self.gpu = gpu
self.initWidget()
self.initMenu()
menuBar!.reload(name: self.name)
}
}

View File

@@ -0,0 +1,55 @@
//
// TemperatureReader.swift
// Stats
//
// Created by Serhiy Mytrovtsiy on 03/04/2020.
// Copyright © 2020 Serhiy Mytrovtsiy. All rights reserved.
//
import IOKit
import Foundation
struct TemperatureValue {
var CPUDie: Double = 0
var CPUProximity: Double = 0
var GPUDie: Double = 0
var GPUProximity: Double = 0
}
class TemperatureReader: Reader {
public var name: String = "Temperature"
public var enabled: Bool = true
public var available: Bool = true
public var optional: Bool = false
public var initialized: Bool = false
public var callback: (TemperatureValue) -> Void = {_ in}
init(_ updater: @escaping (TemperatureValue) -> Void) {
self.callback = updater
if self.available {
DispatchQueue.global(qos: .default).async {
self.read()
}
}
}
func read() {
if !self.enabled && self.initialized { return }
self.initialized = true
let temp = TemperatureValue(
CPUDie: GetTemperature(SMC_TEMP_CPU_0_DIE.UTF8CString),
CPUProximity: GetTemperature(SMC_TEMP_CPU_0_PROXIMITY.UTF8CString),
GPUDie: GetTemperature(SMC_TEMP_GPU_0_DIODE.UTF8CString),
GPUProximity: GetTemperature(SMC_TEMP_GPU_0_PROXIMITY.UTF8CString)
)
self.callback(temp)
}
func toggleEnable(_ value: Bool) {
self.enabled = value
}
}

View File

@@ -1,8 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array/>
</dict>
<dict/>
</plist>

View File

@@ -17,6 +17,7 @@ class BatteryPercentageWidget: BatteryWidget {
override init(frame: NSRect) {
super.init(frame: CGRect(x: 0, y: 0, width: widgetSize.width, height: widgetSize.height))
self.name = "BatteryPercentage"
self.drawPercentage()
self.update()
}

View File

@@ -15,6 +15,7 @@ class BatteryTimeWidget: BatteryWidget {
override init(frame: NSRect) {
super.init(frame: CGRect(x: 0, y: 0, width: widgetSize.width, height: widgetSize.height))
self.name = "BatteryTime"
self.drawTime()
self.changeWidth(width: self.timeWidth)
self.update()

View File

@@ -9,7 +9,7 @@
import Cocoa
class BarChart: NSView, Widget {
public var name: String = ""
public var name: String = "BarChart"
public var menus: [NSMenuItem] = []
private var size: CGFloat = widgetSize.width + 10
@@ -136,7 +136,9 @@ class BarChart: NSView, Widget {
if self.frame.size.width != width {
self.setFrameSize(NSSize(width: width, height: self.frame.size.height))
menuBar!.refresh()
if menuBar != nil {
menuBar!.refresh()
}
}
self.display()

View File

@@ -9,7 +9,7 @@
import Cocoa
class Chart: NSView, Widget {
public var name: String = ""
public var name: String = "LineChart"
public var menus: [NSMenuItem] = []
internal let defaults = UserDefaults.standard

View File

@@ -19,6 +19,7 @@ class ChartWithValue: Chart {
override init(frame: NSRect) {
super.init(frame: CGRect(x: 0, y: 0, width: widgetSize.width + 7, height: widgetSize.height))
self.wantsLayer = true
self.name = "LineChartWithValue"
}
required init?(coder decoder: NSCoder) {

View File

@@ -9,7 +9,7 @@
import Cocoa
class Mini: NSView, Widget {
public var name: String = ""
public var name: String = "Mini"
public var menus: [NSMenuItem] = []
private var value: Double = 0
@@ -26,7 +26,6 @@ class Mini: NSView, Widget {
override init(frame: NSRect) {
super.init(frame: CGRect(x: 0, y: 0, width: self.size, height: widgetSize.height))
self.wantsLayer = true
let xOffset: CGFloat = 1.0

View File

@@ -11,7 +11,7 @@ import Cocoa
class NetworkArrowsView: NSView, Widget {
public var menus: [NSMenuItem] = []
public var size: CGFloat = 8
public var name: String = ""
public var name: String = "NetworkArrows"
private var download: Int64 = 0
private var upload: Int64 = 0

View File

@@ -11,7 +11,7 @@ import Cocoa
class NetworkArrowsTextView: NSView, Widget {
public var menus: [NSMenuItem] = []
public var size: CGFloat = widgetSize.width + 24
public var name: String = ""
public var name: String = "NetworkArrowsText"
private var download: Int64 = 0
private var upload: Int64 = 0
@@ -97,7 +97,7 @@ class NetworkArrowsTextView: NSView, Widget {
}
func valueView() {
downloadValue = NSTextField(frame: NSMakeRect(widgetSize.margin, widgetSize.margin, self.frame.size.width - widgetSize.margin, 9))
downloadValue = NSTextField(frame: NSMakeRect(widgetSize.margin, widgetSize.margin + 1, self.frame.size.width - widgetSize.margin, 9))
downloadValue.isEditable = false
downloadValue.isSelectable = false
downloadValue.isBezeled = false

View File

@@ -10,7 +10,7 @@ import Cocoa
class NetworkDotsView: NSView, Widget {
public var size: CGFloat = 12
public var name: String = ""
public var name: String = "NetworkDots"
public var menus: [NSMenuItem] = []
private var download: Int64 = 0

View File

@@ -11,7 +11,7 @@ import Cocoa
class NetworkDotsTextView: NSView, Widget {
public var menus: [NSMenuItem] = []
public var size: CGFloat = widgetSize.width + 26
public var name: String = ""
public var name: String = "NetworkDotsText"
private var download: Int64 = 0
private var upload: Int64 = 0
@@ -88,7 +88,7 @@ class NetworkDotsTextView: NSView, Widget {
}
func valueView() {
downloadValue = NSTextField(frame: NSMakeRect(widgetSize.margin, widgetSize.margin, self.frame.size.width - widgetSize.margin, 9))
downloadValue = NSTextField(frame: NSMakeRect(widgetSize.margin, widgetSize.margin + 1, self.frame.size.width - widgetSize.margin, 9))
downloadValue.isEditable = false
downloadValue.isSelectable = false
downloadValue.isBezeled = false

View File

@@ -11,7 +11,7 @@ import Cocoa
class NetworkTextView: NSView, Widget {
public var menus: [NSMenuItem] = []
public var size: CGFloat = widgetSize.width + 20
public var name: String = ""
public var name: String = "NetworkText"
private var downloadValue: NSTextField = NSTextField()
private var uploadValue: NSTextField = NSTextField()
@@ -51,7 +51,7 @@ class NetworkTextView: NSView, Widget {
}
func valueView() {
downloadValue = NSTextField(frame: NSMakeRect(widgetSize.margin, widgetSize.margin, self.frame.size.width - widgetSize.margin, 9))
downloadValue = NSTextField(frame: NSMakeRect(widgetSize.margin, widgetSize.margin + 1, self.frame.size.width - widgetSize.margin, 9))
downloadValue.isEditable = false
downloadValue.isSelectable = false
downloadValue.isBezeled = false

View File

@@ -0,0 +1,116 @@
//
// DoubleRow.swift
// Stats
//
// Created by Serhiy Mytrovtsiy on 03/04/2020.
// Copyright © 2020 Serhiy Mytrovtsiy. All rights reserved.
//
import Foundation
class TemperatureWidget: NSView, Widget {
public var name: String = "Temperature"
public var menus: [NSMenuItem] = []
private var value: [Double] = []
private var size: CGFloat = 24
private var topValueView: NSTextField = NSTextField()
private var bottomValueView: NSTextField = NSTextField()
private let defaults = UserDefaults.standard
private var color: Bool = true
override var intrinsicContentSize: CGSize {
return CGSize(width: self.frame.size.width, height: self.frame.size.height)
}
required init?(coder decoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override init(frame: NSRect) {
super.init(frame: CGRect(x: 0, y: 0, width: self.size, height: widgetSize.height))
self.wantsLayer = true
let xOffset: CGFloat = 1.0
let topValueView = NSTextField(frame: NSMakeRect(xOffset, 11, self.frame.size.width, 10))
topValueView.isEditable = false
topValueView.isSelectable = false
topValueView.isBezeled = false
topValueView.wantsLayer = true
topValueView.textColor = .labelColor
topValueView.backgroundColor = .controlColor
topValueView.canDrawSubviewsIntoLayer = true
topValueView.alignment = .natural
topValueView.font = NSFont.systemFont(ofSize: 9, weight: .light)
topValueView.stringValue = ""
topValueView.addSubview(NSView())
let bottomValueView = NSTextField(frame: NSMakeRect(xOffset, 2, self.frame.size.width, 10))
bottomValueView.isEditable = false
bottomValueView.isSelectable = false
bottomValueView.isBezeled = false
bottomValueView.wantsLayer = true
bottomValueView.textColor = .labelColor
bottomValueView.backgroundColor = .controlColor
bottomValueView.canDrawSubviewsIntoLayer = true
bottomValueView.alignment = .natural
bottomValueView.font = NSFont.systemFont(ofSize: 9, weight: .light)
bottomValueView.stringValue = ""
bottomValueView.addSubview(NSView())
self.topValueView = topValueView
self.bottomValueView = bottomValueView
self.addSubview(self.topValueView)
self.addSubview(self.bottomValueView)
}
func start() {
self.color = defaults.object(forKey: "\(name)_color") != nil ? defaults.bool(forKey: "\(name)_color") : true
self.initMenu()
self.redraw()
}
func redraw() {
if self.value.count == 2 {
self.topValueView.textColor = self.value[0].temperatureColor(color: self.color)
self.bottomValueView.textColor = self.value[1].temperatureColor(color: self.color)
}
self.display()
}
func setValue(data: [Double]) {
if self.value != data && data.count == 2 {
self.value = data
self.topValueView.stringValue = "\(Int(self.value[0]))°"
self.bottomValueView.stringValue = "\(Int(self.value[1]))°"
self.topValueView.textColor = self.value[0].temperatureColor(color: self.color)
self.bottomValueView.textColor = self.value[1].temperatureColor(color: self.color)
}
}
func initMenu() {
let color = NSMenuItem(title: "Color", action: #selector(toggleColor), keyEquivalent: "")
color.state = self.color ? NSControl.StateValue.on : NSControl.StateValue.off
color.target = self
self.menus.append(color)
}
@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
if self.value.count == 2 {
self.topValueView.textColor = self.value[0].temperatureColor(color: self.color)
self.bottomValueView.textColor = self.value[1].temperatureColor(color: self.color)
}
self.redraw()
}
}

View File

@@ -23,6 +23,8 @@ protocol Widget {
typealias WidgetType = Float
struct Widgets {
static let Mini: WidgetType = 0.0
static let Temperature: WidgetType = 0.1
static let Chart: WidgetType = 1.0
static let ChartWithValue: WidgetType = 1.1

View File

@@ -65,6 +65,23 @@ extension Double {
}
}
func temperatureColor(color: Bool = false) -> NSColor {
switch self {
case 0...70:
return NSColor.controlTextColor
case 70...90:
if !color {
return NSColor.controlTextColor
}
return NSColor.systemOrange
default:
if !color {
return NSColor.controlTextColor
}
return NSColor.systemRed
}
}
func splitAtDecimal() -> [Int64] {
return "\(self)".split(separator: ".").map{Int64($0)!}
}
@@ -180,6 +197,10 @@ extension String {
let components = self.components(separatedBy: .whitespacesAndNewlines)
return components.filter { !$0.isEmpty }.joined(separator: " ")
}
var UTF8CString: UnsafeMutablePointer<Int8> {
return UnsafeMutablePointer(mutating: (self as NSString).utf8String!)
}
}
extension NSBezierPath {

17
Stats/libs/Stats.h Normal file
View File

@@ -0,0 +1,17 @@
//
// Stats.h
// Stats
//
// Created by Serhiy Mytrovtsiy on 03/04/2020.
// Copyright © 2020 Serhiy Mytrovtsiy. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import "SystemKit.h"
//! Project version number for SMCKit.
FOUNDATION_EXPORT double SMCKitVersionNumber;
//! Project version string for SMCKit.
FOUNDATION_EXPORT const unsigned char SMCKitVersionString[];

277
Stats/libs/SystemKit.c Normal file
View File

@@ -0,0 +1,277 @@
//
// SystemKit.c
// Stats
//
// SMC code borrowed from https://github.com/lavoiesl/osx-cpu-temp.
//
// Created by Serhiy Mytrovtsiy on 03/04/2020.
// Copyright © 2020 Serhiy Mytrovtsiy. All rights reserved.
//
#include <IOKit/ps/IOPowerSources.h>
#include <IOKit/ps/IOPSKeys.h>
#include <IOKit/IOKitLib.h>
#include <stdio.h>
#include <string.h>
#include "SystemKit.h"
static io_connect_t conn;
UInt32 _strtoul(char* str, int size, int base) {
UInt32 total = 0;
int i;
for (i = 0; i < size; i++) {
if (base == 16)
total += str[i] << (size - 1 - i) * 8;
else
total += (unsigned char)(str[i] << (size - 1 - i) * 8);
}
return total;
}
void _ultostr(char* str, UInt32 val) {
str[0] = '\0';
sprintf(str, "%c%c%c%c",
(unsigned int)val >> 24,
(unsigned int)val >> 16,
(unsigned int)val >> 8,
(unsigned int)val);
}
void t(void * refcon,
io_service_t service,
uint32_t messageType,
void * messageArgument) {
printf("Hmmmm");
}
kern_return_t SMCOpen(void) {
kern_return_t result;
io_iterator_t iterator;
io_object_t device;
CFMutableDictionaryRef matchingDictionary = IOServiceMatching("AppleSMC");
result = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDictionary, &iterator);
if (result != kIOReturnSuccess) {
printf("Error: IOServiceGetMatchingServices() = %08x\n", result);
return 1;
}
device = IOIteratorNext(iterator);
IOObjectRelease(iterator);
if (device == 0) {
printf("Error: no SMC found\n");
return 1;
}
result = IOServiceOpen(device, mach_task_self(), 0, &conn);
IOObjectRelease(device);
if (result != kIOReturnSuccess) {
printf("Error: IOServiceOpen() = %08x\n", result);
return 1;
}
return kIOReturnSuccess;
}
kern_return_t SMCClose(void) {
return IOServiceClose(conn);
}
kern_return_t SMCCall(int index, SMCKeyData_t* inputStructure, SMCKeyData_t* outputStructure) {
size_t structureInputSize;
size_t structureOutputSize;
structureInputSize = sizeof(SMCKeyData_t);
structureOutputSize = sizeof(SMCKeyData_t);
#if MAC_OS_X_VERSION_10_5
return IOConnectCallStructMethod(conn, index,
// inputStructure
inputStructure, structureInputSize,
// ouputStructure
outputStructure, &structureOutputSize);
#else
return IOConnectMethodStructureIStructureO(conn, index,
structureInputSize, /* structureInputSize */
&structureOutputSize, /* structureOutputSize */
inputStructure, /* inputStructure */
outputStructure); /* ouputStructure */
#endif
}
kern_return_t SMCReadKey(UInt32Char_t key, SMCVal_t* val) {
kern_return_t result;
SMCKeyData_t inputStructure;
SMCKeyData_t outputStructure;
memset(&inputStructure, 0, sizeof(SMCKeyData_t));
memset(&outputStructure, 0, sizeof(SMCKeyData_t));
memset(val, 0, sizeof(SMCVal_t));
inputStructure.key = _strtoul(key, 4, 16);
inputStructure.data8 = SMC_CMD_READ_KEYINFO;
result = SMCCall(KERNEL_INDEX_SMC, &inputStructure, &outputStructure);
if (result != kIOReturnSuccess)
return result;
val->dataSize = outputStructure.keyInfo.dataSize;
_ultostr(val->dataType, outputStructure.keyInfo.dataType);
inputStructure.keyInfo.dataSize = val->dataSize;
inputStructure.data8 = SMC_CMD_READ_BYTES;
result = SMCCall(KERNEL_INDEX_SMC, &inputStructure, &outputStructure);
if (result != kIOReturnSuccess)
return result;
memcpy(val->bytes, outputStructure.bytes, sizeof(outputStructure.bytes));
return kIOReturnSuccess;
}
double GetTemperature(char* key) {
SMCVal_t val;
kern_return_t result;
result = SMCReadKey(key, &val);
if (result == kIOReturnSuccess) {
if (val.dataSize > 0) {
if (strcmp(val.dataType, DATATYPE_SP78) == 0) {
int intValue = val.bytes[0] * 256 + (unsigned char)val.bytes[1];
return intValue / 256.0;
}
}
} else {
printf("Error: SMCReadKey() = %08x\n", result);
}
return 0.0;
}
float GetFanRPM(char* key) {
SMCVal_t val;
kern_return_t result;
result = SMCReadKey(key, &val);
if (result == kIOReturnSuccess) {
if (val.dataSize > 0) {
if (strcmp(val.dataType, DATATYPE_FPE2) == 0) {
return ntohs(*(UInt16*)val.bytes) / 4.0;
}
}
}
return -1.f;
}
PowerManagmentInformation *GetPowerInfo() {
PowerManagmentInformation *info = malloc(sizeof(PowerManagmentInformation));
CFTypeRef power_sources = IOPSCopyPowerSourcesInfo();
CFTypeRef external_adapter = IOPSCopyExternalPowerAdapterDetails();
/* Get information aboud external adapter */
if (external_adapter != NULL) {
CFNumberRef watts = CFDictionaryGetValue(external_adapter, CFSTR(kIOPSPowerAdapterWattsKey));
if (watts) {
CFNumberGetValue(watts, kCFNumberDoubleType, &info->ACWatts);
CFRelease(watts);
}
CFRelease(external_adapter);
}
if(power_sources == NULL) {
return NULL;
}
CFArrayRef list = IOPSCopyPowerSourcesList(power_sources);
CFDictionaryRef battery = NULL;
if(list == NULL) {
CFRelease(power_sources);
return NULL;
}
/* Get the battery */
for(int i = 0; i < CFArrayGetCount(list) && battery == NULL; ++i) {
CFDictionaryRef candidate = IOPSGetPowerSourceDescription(power_sources, CFArrayGetValueAtIndex(list, i));
CFStringRef type;
if(candidate != NULL) {
type = (CFStringRef) CFDictionaryGetValue(candidate, CFSTR(kIOPSTransportTypeKey));
if(kCFCompareEqualTo == CFStringCompare(type, CFSTR(kIOPSInternalType), 0)) {
CFRetain(candidate);
battery = candidate;
}
}
}
if(battery != NULL) {
CFStringRef power_state = CFDictionaryGetValue(battery, CFSTR(kIOPSPowerSourceStateKey));
CFNumberRef max_capacity = CFDictionaryGetValue(battery, CFSTR(kIOPSMaxCapacityKey));
CFNumberRef design_capacity = CFDictionaryGetValue(battery, CFSTR(kIOPSDesignCapacityKey));
CFNumberRef current_capacity = CFDictionaryGetValue(battery, CFSTR(kIOPSCurrentCapacityKey));
CFNumberRef amperage = CFDictionaryGetValue(battery, CFSTR(kIOPSCurrentKey));
CFNumberRef voltage = CFDictionaryGetValue(battery, CFSTR(kIOPSVoltageKey));
CFNumberRef temperature = CFDictionaryGetValue(battery, CFSTR(kIOPSTemperatureKey));
/* Determine the AC state */
info->isCharging = kCFCompareEqualTo == CFStringCompare(power_state, CFSTR(kIOPSACPowerValue), 0);
/* Get capacity */
if (max_capacity) {
CFNumberGetValue(max_capacity, kCFNumberDoubleType, &info->maxCapacity);
CFRelease(max_capacity);
}
if (design_capacity) {
CFNumberGetValue(design_capacity, kCFNumberDoubleType, &info->designCapacity);
CFRelease(design_capacity);
}
if (current_capacity) {
CFNumberGetValue(current_capacity, kCFNumberDoubleType, &info->currentCapacity);
CFRelease(current_capacity);
}
/* Determine the level */
if (info->currentCapacity && info->maxCapacity) {
info->level = (info->currentCapacity * 100.0) / info->maxCapacity;
}
/* Get the parameters */
if (amperage) {
CFNumberGetValue(amperage, kCFNumberDoubleType, &info->amperage);
CFRelease(amperage);
}
if (voltage) {
printf("2\n");
CFNumberGetValue(voltage, kCFNumberDoubleType, &info->voltage);
CFRelease(voltage);
}
if (temperature) {
printf("3\n");
CFNumberGetValue(temperature, kCFNumberDoubleType, &info->voltage);
CFRelease(temperature);
}
// printf("%f\n", info->amperage);
// printf("%f\n", test);
// printf("%f\n", info->temperature);
// printf("\n%hhu\n", info->isCharging);
// printf("%u", info->level);
CFRelease(battery);
}
CFRelease(list);
CFRelease(power_sources);
free(info);
return info;
}

142
Stats/libs/SystemKit.h Normal file
View File

@@ -0,0 +1,142 @@
//
// SystemKit.h
// Stats
//
// Created by Serhiy Mytrovtsiy on 03/04/2020.
// Copyright © 2020 Serhiy Mytrovtsiy. All rights reserved.
//
#define SMC_TEMP_AMBIENT_AIR_0 "TA0P"
#define SMC_TEMP_AMBIENT_AIR_1 "TA1P"
#define SMC_TEMP_CPU_0_DIE "TC0F"
#define SMC_TEMP_CPU_0_DIODE "TC0D"
#define SMC_TEMP_CPU_0_HEATSINK "TC0H"
#define SMC_TEMP_CPU_0_PROXIMITY "TC0P"
#define SMC_TEMP_ENCLOSURE_BASE_0 "TB0T"
#define SMC_TEMP_ENCLOSURE_BASE_1 "TB1T"
#define SMC_TEMP_ENCLOSURE_BASE_2 "TB2T"
#define SMC_TEMP_ENCLOSURE_BASE_3 "TB3T"
#define SMC_TEMP_GPU_0_DIODE "TG0D"
#define SMC_TEMP_GPU_0_HEATSINK "TG0H"
#define SMC_TEMP_GPU_0_PROXIMITY "TG0P"
#define SMC_TEMP_HDD_PROXIMITY "TH0P"
#define SMC_TEMP_LCD_PROXIMITY "TL0P"
#define SMC_TEMP_MISC_PROXIMITY "Tm0P"
#define SMC_TEMP_ODD_PROXIMITY "TO0P"
#define SMC_TEMP_HEATSINK_0 "Th0H"
#define SMC_TEMP_HEATSINK_1 "Th1H"
#define SMC_TEMP_HEATSINK_2 "Th2H"
#define SMC_TEMP_MEM_SLOT_0 "TM0S"
#define SMC_TEMP_MEM_SLOTS_PROXIMITY "TM0P"
#define SMC_TEMP_NORTHBRIDGE "TN0H"
#define SMC_TEMP_NORTHBRIDGE_DIODE "TN0D"
#define SMC_TEMP_NORTHBRIDGE_PROXIMITY "TN0P"
#define SMC_TEMP_PALM_REST "Ts0P"
#define PWR_TEMP_SUPPLY_PROXIMITY "Tp0P"
#define SMC_TEMP_THUNDERBOLT_0 "TI0P"
#define SMC_TEMP_THUNDERBOLT_1 "TI1P"
#define SMC_FAN0_RPM "F0Ac"
typedef enum {
Off_line,
AC_Power,
Battery_Power
} PowerSource;
typedef struct {
unsigned int level;
Boolean isCharging;
double amperage;
double voltage;
double temperature;
double cycles;
double currentCapacity;
double maxCapacity;
double designCapacity;
double timeToEmpty;
double timeToFull;
PowerSource powerSource;
double ACWatts;
} PowerManagmentInformation;
kern_return_t SMCOpen(void);
kern_return_t SMCClose(void);
double GetTemperature(char* key);
float GetFanRPM(char* key);
//PowerManagmentInformation *GetPowerInfo(void);
// INTERNAL
#define KERNEL_INDEX_SMC 2
#define SMC_CMD_READ_BYTES 5
#define SMC_CMD_WRITE_BYTES 6
#define SMC_CMD_READ_INDEX 8
#define SMC_CMD_READ_KEYINFO 9
#define SMC_CMD_READ_PLIMIT 11
#define SMC_CMD_READ_VERS 12
#define DATATYPE_FPE2 "fpe2"
#define DATATYPE_UINT8 "ui8 "
#define DATATYPE_UINT16 "ui16"
#define DATATYPE_UINT32 "ui32"
#define DATATYPE_SP78 "sp78"
typedef char UInt32Char_t[5];
typedef char SMCBytes_t[32];
typedef struct {
UInt32Char_t key;
UInt32 dataSize;
UInt32Char_t dataType;
SMCBytes_t bytes;
} SMCVal_t;
typedef struct {
char major;
char minor;
char build;
char reserved[1];
UInt16 release;
} SMCKeyData_vers_t;
typedef struct {
UInt16 version;
UInt16 length;
UInt32 cpuPLimit;
UInt32 gpuPLimit;
UInt32 memPLimit;
} SMCKeyData_pLimitData_t;
typedef struct {
UInt32 dataSize;
UInt32 dataType;
char dataAttributes;
} SMCKeyData_keyInfo_t;
typedef struct {
UInt32 key;
SMCKeyData_vers_t vers;
SMCKeyData_pLimitData_t pLimitData;
SMCKeyData_keyInfo_t keyInfo;
char result;
char status;
char data8;
UInt32 data32;
SMCBytes_t bytes;
} SMCKeyData_t;