From 0d63ae1061da7fda84eb832357d61cdea8356941 Mon Sep 17 00:00:00 2001 From: Serhiy Mytrovtsiy Date: Mon, 17 Aug 2020 17:30:09 +0200 Subject: [PATCH 1/4] init gpu module --- Modules/GPU/Info.plist | 24 ++++ Modules/GPU/config.plist | 10 ++ Modules/GPU/main.swift | 45 +++++++ Modules/GPU/reader.swift | 21 ++++ Stats.xcodeproj/project.pbxproj | 209 ++++++++++++++++++++++++++++++++ Stats/AppDelegate.swift | 3 +- 6 files changed, 311 insertions(+), 1 deletion(-) create mode 100644 Modules/GPU/Info.plist create mode 100644 Modules/GPU/config.plist create mode 100644 Modules/GPU/main.swift create mode 100644 Modules/GPU/reader.swift diff --git a/Modules/GPU/Info.plist b/Modules/GPU/Info.plist new file mode 100644 index 00000000..20202aa7 --- /dev/null +++ b/Modules/GPU/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSHumanReadableCopyright + Copyright © 2020 Serhiy Mytrovtsiy. All rights reserved. + + diff --git a/Modules/GPU/config.plist b/Modules/GPU/config.plist new file mode 100644 index 00000000..43302fd6 --- /dev/null +++ b/Modules/GPU/config.plist @@ -0,0 +1,10 @@ + + + + + Name + GPU + State + + + diff --git a/Modules/GPU/main.swift b/Modules/GPU/main.swift new file mode 100644 index 00000000..c6ab28a2 --- /dev/null +++ b/Modules/GPU/main.swift @@ -0,0 +1,45 @@ +// +// main.swift +// GPU +// +// Created by Serhiy Mytrovtsiy on 17/08/2020. +// Using Swift 5.0. +// Running on macOS 10.15. +// +// Copyright © 2020 Serhiy Mytrovtsiy. All rights reserved. +// + +import Cocoa +import ModuleKit +import StatsKit + +public struct GPU_Load {} + +public class GPU: Module { + private let smc: UnsafePointer? + private let store: UnsafePointer + + private var loadReader: LoadReader? = nil + + public init(_ store: UnsafePointer, _ smc: UnsafePointer) { + self.store = store + self.smc = smc + + super.init( + store: store, + popup: nil, + settings: nil + ) + guard self.available else { return } + + self.loadReader = LoadReader() + + self.loadReader?.readyCallback = { [unowned self] in + self.readyHandler() + } + + if let reader = self.loadReader { + self.addReader(reader) + } + } +} diff --git a/Modules/GPU/reader.swift b/Modules/GPU/reader.swift new file mode 100644 index 00000000..879b7966 --- /dev/null +++ b/Modules/GPU/reader.swift @@ -0,0 +1,21 @@ +// +// reader.swift +// GPU +// +// Created by Serhiy Mytrovtsiy on 17/08/2020. +// Using Swift 5.0. +// Running on macOS 10.15. +// +// Copyright © 2020 Serhiy Mytrovtsiy. All rights reserved. +// + +import Cocoa +import StatsKit +import ModuleKit +import os.log + +internal class LoadReader: Reader { + public override func read() { + + } +} diff --git a/Stats.xcodeproj/project.pbxproj b/Stats.xcodeproj/project.pbxproj index 78961cae..5b1473d3 100644 --- a/Stats.xcodeproj/project.pbxproj +++ b/Stats.xcodeproj/project.pbxproj @@ -57,6 +57,15 @@ 9A81C76B2449AE9400825D92 /* StatsKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A0C82DA24460F7200FAE3D4 /* StatsKit.framework */; }; 9A81C76C2449AE9400825D92 /* StatsKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9A0C82DA24460F7200FAE3D4 /* StatsKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 9A81C7702449B8D500825D92 /* Charts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A81C76F2449B8D500825D92 /* Charts.swift */; }; + 9A90E19024EAD2BB00471E9A /* GPU.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A90E18924EAD2BB00471E9A /* GPU.framework */; }; + 9A90E19124EAD2BB00471E9A /* GPU.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9A90E18924EAD2BB00471E9A /* GPU.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 9A90E19624EAD35F00471E9A /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A90E19524EAD35F00471E9A /* main.swift */; }; + 9A90E19824EAD3B000471E9A /* config.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9A90E19724EAD3B000471E9A /* config.plist */; }; + 9A90E19924EAD3E300471E9A /* ModuleKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9AABEADD243FB13500668CB0 /* ModuleKit.framework */; }; + 9A90E19A24EAD3E300471E9A /* ModuleKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9AABEADD243FB13500668CB0 /* ModuleKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 9A90E19D24EAD3E300471E9A /* StatsKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A0C82DA24460F7200FAE3D4 /* StatsKit.framework */; }; + 9A90E19E24EAD3E300471E9A /* StatsKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9A0C82DA24460F7200FAE3D4 /* StatsKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 9A90E1A324EAD66600471E9A /* reader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A90E1A224EAD66600471E9A /* reader.swift */; }; 9A944D55244920690058F32A /* reader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A944D54244920690058F32A /* reader.swift */; }; 9A944D59244920FE0058F32A /* readers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A944D58244920FE0058F32A /* readers.swift */; }; 9A944D5B244925720058F32A /* widget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A944D5A244925720058F32A /* widget.swift */; }; @@ -191,6 +200,27 @@ remoteGlobalIDString = 9A0C82D924460F7200FAE3D4; remoteInfo = StatsKit; }; + 9A90E18E24EAD2BB00471E9A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 9A1410ED229E721100D29793 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 9A90E18824EAD2BB00471E9A; + remoteInfo = GPU; + }; + 9A90E19B24EAD3E300471E9A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 9A1410ED229E721100D29793 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 9AABEADC243FB13500668CB0; + remoteInfo = ModuleKit; + }; + 9A90E19F24EAD3E300471E9A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 9A1410ED229E721100D29793 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 9A0C82D924460F7200FAE3D4; + remoteInfo = StatsKit; + }; 9AABEAE2243FB13500668CB0 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 9A1410ED229E721100D29793 /* Project object */; @@ -310,6 +340,7 @@ 9AABEAE5243FB13500668CB0 /* ModuleKit.framework in Embed Frameworks */, 9A3E17D4247A94AF00449CD1 /* Net.framework in Embed Frameworks */, 9A0C82E224460F7200FAE3D4 /* StatsKit.framework in Embed Frameworks */, + 9A90E19124EAD2BB00471E9A /* GPU.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -326,6 +357,18 @@ name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; + 9A90E1A124EAD3E300471E9A /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 9A90E19A24EAD3E300471E9A /* ModuleKit.framework in Embed Frameworks */, + 9A90E19E24EAD3E300471E9A /* StatsKit.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; 9AABEB76243FCEEF00668CB0 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 12; @@ -437,6 +480,11 @@ 9A81C7672449A43600825D92 /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 9A81C7682449A43600825D92 /* readers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = readers.swift; sourceTree = ""; }; 9A81C76F2449B8D500825D92 /* Charts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Charts.swift; sourceTree = ""; }; + 9A90E18924EAD2BB00471E9A /* GPU.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GPU.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 9A90E18C24EAD2BB00471E9A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 9A90E19524EAD35F00471E9A /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; + 9A90E19724EAD3B000471E9A /* config.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = config.plist; sourceTree = ""; }; + 9A90E1A224EAD66600471E9A /* reader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = reader.swift; sourceTree = ""; }; 9A944D54244920690058F32A /* reader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = reader.swift; sourceTree = ""; }; 9A944D58244920FE0058F32A /* readers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = readers.swift; sourceTree = ""; }; 9A944D5A244925720058F32A /* widget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = widget.swift; sourceTree = ""; }; @@ -508,6 +556,7 @@ 9A0C82E124460F7200FAE3D4 /* StatsKit.framework in Frameworks */, 9A3E17D3247A94AF00449CD1 /* Net.framework in Frameworks */, 9AABEB6B243FCE8A00668CB0 /* CPU.framework in Frameworks */, + 9A90E19024EAD2BB00471E9A /* GPU.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -537,6 +586,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 9A90E18624EAD2BB00471E9A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9A90E19924EAD3E300471E9A /* ModuleKit.framework in Frameworks */, + 9A90E19D24EAD3E300471E9A /* StatsKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 9AABEADA243FB13500668CB0 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -628,6 +686,7 @@ 9A3E17CC247A94AF00449CD1 /* Net.framework */, 9ABFF8F6248BEBCB00C9041A /* Battery.framework */, 9AE29AD5249A50350071B02D /* Sensors.framework */, + 9A90E18924EAD2BB00471E9A /* GPU.framework */, ); name = Products; sourceTree = ""; @@ -713,6 +772,17 @@ path = Memory; sourceTree = ""; }; + 9A90E18A24EAD2BB00471E9A /* GPU */ = { + isa = PBXGroup; + children = ( + 9A90E19524EAD35F00471E9A /* main.swift */, + 9A90E1A224EAD66600471E9A /* reader.swift */, + 9A90E18C24EAD2BB00471E9A /* Info.plist */, + 9A90E19724EAD3B000471E9A /* config.plist */, + ); + path = GPU; + sourceTree = ""; + }; 9A998CD622A199920087ADE7 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -765,6 +835,7 @@ isa = PBXGroup; children = ( 9AABEB65243FCE8A00668CB0 /* CPU */, + 9A90E18A24EAD2BB00471E9A /* GPU */, 9A81C7572449A41400825D92 /* Memory */, 9AF9EE0324648751005D2270 /* Disk */, 9AE29AD6249A50350071B02D /* Sensors */, @@ -840,6 +911,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 9A90E18424EAD2BB00471E9A /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 9AABEAD8243FB13500668CB0 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -919,6 +997,7 @@ 9A3E17D2247A94AF00449CD1 /* PBXTargetDependency */, 9ABFF8FC248BEBCB00C9041A /* PBXTargetDependency */, 9AE29ADB249A50350071B02D /* PBXTargetDependency */, + 9A90E18F24EAD2BB00471E9A /* PBXTargetDependency */, ); name = Stats; productName = "Mini Stats"; @@ -984,6 +1063,27 @@ productReference = 9A81C7562449A41400825D92 /* Memory.framework */; productType = "com.apple.product-type.framework"; }; + 9A90E18824EAD2BB00471E9A /* GPU */ = { + isa = PBXNativeTarget; + buildConfigurationList = 9A90E19424EAD2BB00471E9A /* Build configuration list for PBXNativeTarget "GPU" */; + buildPhases = ( + 9A90E18424EAD2BB00471E9A /* Headers */, + 9A90E18524EAD2BB00471E9A /* Sources */, + 9A90E18624EAD2BB00471E9A /* Frameworks */, + 9A90E18724EAD2BB00471E9A /* Resources */, + 9A90E1A124EAD3E300471E9A /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 9A90E19C24EAD3E300471E9A /* PBXTargetDependency */, + 9A90E1A024EAD3E300471E9A /* PBXTargetDependency */, + ); + name = GPU; + productName = GPU; + productReference = 9A90E18924EAD2BB00471E9A /* GPU.framework */; + productType = "com.apple.product-type.framework"; + }; 9AABEADC243FB13500668CB0 /* ModuleKit */ = { isa = PBXNativeTarget; buildConfigurationList = 9AABEAE8243FB13500668CB0 /* Build configuration list for PBXNativeTarget "ModuleKit" */; @@ -1128,6 +1228,10 @@ CreatedOnToolsVersion = 11.4.1; LastSwiftMigration = 1140; }; + 9A90E18824EAD2BB00471E9A = { + CreatedOnToolsVersion = 11.6; + LastSwiftMigration = 1160; + }; 9AABEADC243FB13500668CB0 = { CreatedOnToolsVersion = 11.4; LastSwiftMigration = 1140; @@ -1172,6 +1276,7 @@ 9A3E17CB247A94AF00449CD1 /* Net */, 9ABFF8F5248BEBCB00C9041A /* Battery */, 9AE29AD4249A50350071B02D /* Sensors */, + 9A90E18824EAD2BB00471E9A /* GPU */, ); }; /* End PBXProject section */ @@ -1216,6 +1321,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 9A90E18724EAD2BB00471E9A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9A90E19824EAD3B000471E9A /* config.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 9AABEADB243FB13500668CB0 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1316,6 +1429,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 9A90E18524EAD2BB00471E9A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9A90E1A324EAD66600471E9A /* reader.swift in Sources */, + 9A90E19624EAD35F00471E9A /* main.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 9AABEAD9243FB13500668CB0 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1434,6 +1556,21 @@ target = 9A0C82D924460F7200FAE3D4 /* StatsKit */; targetProxy = 9A81C76D2449AE9400825D92 /* PBXContainerItemProxy */; }; + 9A90E18F24EAD2BB00471E9A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 9A90E18824EAD2BB00471E9A /* GPU */; + targetProxy = 9A90E18E24EAD2BB00471E9A /* PBXContainerItemProxy */; + }; + 9A90E19C24EAD3E300471E9A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 9AABEADC243FB13500668CB0 /* ModuleKit */; + targetProxy = 9A90E19B24EAD3E300471E9A /* PBXContainerItemProxy */; + }; + 9A90E1A024EAD3E300471E9A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 9A0C82D924460F7200FAE3D4 /* StatsKit */; + targetProxy = 9A90E19F24EAD3E300471E9A /* PBXContainerItemProxy */; + }; 9AABEAE3243FB13500668CB0 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 9AABEADC243FB13500668CB0 /* ModuleKit */; @@ -1928,6 +2065,69 @@ }; name = Release; }; + 9A90E19224EAD2BB00471E9A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = RP2S87B72W; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Modules/GPU/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.14; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = eu.exelban.Stats.GPU; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 9A90E19324EAD2BB00471E9A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = RP2S87B72W; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Modules/GPU/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.14; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = eu.exelban.Stats.GPU; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; 9AABEAE6243FB13500668CB0 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -2315,6 +2515,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 9A90E19424EAD2BB00471E9A /* Build configuration list for PBXNativeTarget "GPU" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9A90E19224EAD2BB00471E9A /* Debug */, + 9A90E19324EAD2BB00471E9A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 9AABEAE8243FB13500668CB0 /* Build configuration list for PBXNativeTarget "ModuleKit" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Stats/AppDelegate.swift b/Stats/AppDelegate.swift index ae81a7f7..57044f1e 100755 --- a/Stats/AppDelegate.swift +++ b/Stats/AppDelegate.swift @@ -16,12 +16,13 @@ import Disk import Net import Battery import Sensors +import GPU var store: Store = Store() let updater = macAppUpdater(user: "exelban", repo: "stats") let systemKit: SystemKit = SystemKit() var smc: SMCService = SMCService() -var modules: [Module] = [Battery(&store), Network(&store), Sensors(&store, &smc), Disk(&store), Memory(&store), CPU(&store, &smc)].reversed() +var modules: [Module] = [Battery(&store), Network(&store), Sensors(&store, &smc), Disk(&store), Memory(&store), GPU(&store, &smc), CPU(&store, &smc)].reversed() var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "Stats") class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDelegate { From 98fb84163e937f2cb08dedb5417b46f92b4c1095 Mon Sep 17 00:00:00 2001 From: Serhiy Mytrovtsiy Date: Mon, 17 Aug 2020 20:37:20 +0200 Subject: [PATCH 2/4] add GPU reader and settings init GPU popup --- Modules/Disk/settings.swift | 9 ++- Modules/GPU/config.plist | 50 +++++++++++++ Modules/GPU/main.swift | 84 +++++++++++++++++++-- Modules/GPU/popup.swift | 32 ++++++++ Modules/GPU/reader.swift | 88 +++++++++++++++++++++- Modules/GPU/settings.swift | 126 ++++++++++++++++++++++++++++++++ Modules/Net/settings.swift | 4 +- Modules/Sensors/values.swift | 28 +++---- Stats.xcodeproj/project.pbxproj | 8 ++ StatsKit/SystemKit.swift | 1 + StatsKit/extensions.swift | 20 +++++ 11 files changed, 424 insertions(+), 26 deletions(-) create mode 100644 Modules/GPU/popup.swift create mode 100644 Modules/GPU/settings.swift diff --git a/Modules/Disk/settings.swift b/Modules/Disk/settings.swift index e07b4c82..d502c8f7 100644 --- a/Modules/Disk/settings.swift +++ b/Modules/Disk/settings.swift @@ -78,13 +78,18 @@ internal class Settings: NSView, Settings_v { } private func addDiskSelector() { - let view: NSView = NSView(frame: NSRect(x: Constants.Settings.margin, y: Constants.Settings.margin*2 + 30, width: self.frame.width, height: 29)) + let view: NSView = NSView(frame: NSRect( + x: Constants.Settings.margin, + y: Constants.Settings.margin*2 + 30, + width: self.frame.width - Constants.Settings.margin*2, + height: 30 + )) let rowTitle: NSTextField = LabelField(frame: NSRect(x: 0, y: (view.frame.height - 16)/2, width: view.frame.width - 52, height: 17), "Disk to show") rowTitle.font = NSFont.systemFont(ofSize: 13, weight: .light) rowTitle.textColor = .textColor - self.button = NSPopUpButton(frame: NSRect(x: view.frame.width - 140 - Constants.Settings.margin*2, y: -1, width: 140, height: 30)) + self.button = NSPopUpButton(frame: NSRect(x: view.frame.width - 140, y: -1, width: 140, height: 30)) self.button!.target = self self.button?.action = #selector(self.handleSelection) diff --git a/Modules/GPU/config.plist b/Modules/GPU/config.plist index 43302fd6..c9c64a7a 100644 --- a/Modules/GPU/config.plist +++ b/Modules/GPU/config.plist @@ -6,5 +6,55 @@ GPU State + Widgets + + mini + + Default + + Preview + + Value + 0.32 + + Unsupported colors + + pressure + + Order + 0 + + line_chart + + Default + + Color + systemAccent + Unsupported colors + + pressure + + Order + 1 + + bar_chart + + Default + + Preview + + Value + 0.36,0.28,0.32,0.26 + Color + + + Unsupported colors + + pressure + + Order + 2 + + diff --git a/Modules/GPU/main.swift b/Modules/GPU/main.swift index c6ab28a2..61e18f7b 100644 --- a/Modules/GPU/main.swift +++ b/Modules/GPU/main.swift @@ -13,33 +13,103 @@ import Cocoa import ModuleKit import StatsKit -public struct GPU_Load {} +public struct GPU_Info { + public let name: String + public let IOclass: String + public var state: Bool = false + + public var utilization: Double = 0 + + public var totalVram: Int = 0 + public var freeVram: Int = 0 + public var coreClock: Int = 0 + public var power: Int = 0 + public var temperature: Int = 0 +} + +public struct GPUs: value_t { + public var list: [GPU_Info] = [] + + internal func active() -> [GPU_Info] { + return self.list.filter{ $0.state } + } + + internal func igpu() -> GPU_Info? { + return self.active().first{ $0.IOclass == "IntelAccelerator" } + } + + public var widget_value: Double { + get { + return list[0].utilization + } + } +} public class GPU: Module { private let smc: UnsafePointer? private let store: UnsafePointer - private var loadReader: LoadReader? = nil + private var infoReader: InfoReader? = nil + private var settingsView: Settings + private var popupView: Popup = Popup() + + private var selectedGPU: String = "" public init(_ store: UnsafePointer, _ smc: UnsafePointer) { self.store = store self.smc = smc + self.settingsView = Settings("GPU", store: store) super.init( store: store, - popup: nil, - settings: nil + popup: self.popupView, + settings: self.settingsView ) guard self.available else { return } - self.loadReader = LoadReader() + self.infoReader = InfoReader() + self.infoReader?.smc = smc + self.selectedGPU = store.pointee.string(key: "\(self.config.name)_gpu", defaultValue: self.selectedGPU) - self.loadReader?.readyCallback = { [unowned self] in + self.infoReader?.readyCallback = { [unowned self] in self.readyHandler() } + self.infoReader?.callbackHandler = { [unowned self] value in + self.infoCallback(value) + } - if let reader = self.loadReader { + self.settingsView.selectedGPUHandler = { [unowned self] value in + self.selectedGPU = value + self.infoReader?.read() + } + self.settingsView.setInterval = { [unowned self] value in + self.infoReader?.setInterval(value) + } + + if let reader = self.infoReader { self.addReader(reader) } } + + private func infoCallback(_ value: GPUs?) { + guard value != nil else { + return + } + + self.popupView.infoCallback(value!) + self.settingsView.setList(value!) + + let activeGPU = value!.active() + let selectedGPU = activeGPU.first{ $0.name == self.selectedGPU } ?? value!.igpu() ?? value!.list[0] + + if let widget = self.widget as? Mini { + widget.setValue(selectedGPU.utilization, sufix: "%") + } + if let widget = self.widget as? LineChart { + widget.setValue(selectedGPU.utilization) + } + if let widget = self.widget as? BarChart { + widget.setValue([selectedGPU.utilization]) + } + } } diff --git a/Modules/GPU/popup.swift b/Modules/GPU/popup.swift new file mode 100644 index 00000000..10d7e8a8 --- /dev/null +++ b/Modules/GPU/popup.swift @@ -0,0 +1,32 @@ +// +// popup.swift +// GPU +// +// Created by Serhiy Mytrovtsiy on 17/08/2020. +// Using Swift 5.0. +// Running on macOS 10.15. +// +// Copyright © 2020 Serhiy Mytrovtsiy. All rights reserved. +// + +import Cocoa +import StatsKit +import ModuleKit + +internal class Popup: NSView { + public init() { + super.init(frame: NSRect(x: 0, y: 0, width: Constants.Popup.width, height: 0)) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + internal func infoCallback(_ value: GPUs) { + print(value) + } +} + +private class GPUView: NSView { + +} diff --git a/Modules/GPU/reader.swift b/Modules/GPU/reader.swift index 879b7966..9a3c77cd 100644 --- a/Modules/GPU/reader.swift +++ b/Modules/GPU/reader.swift @@ -14,8 +14,94 @@ import StatsKit import ModuleKit import os.log -internal class LoadReader: Reader { +internal class InfoReader: Reader { + internal var smc: UnsafePointer? = nil + private var gpus: GPUs = GPUs() + public override func read() { + guard let devices = fetchIOService("IOPCIDevice") else { + return + } + let gpus = devices.filter{ $0.object(forKey: "IOName") as? String == "display" } + guard let acceletators = fetchIOService(kIOAcceleratorClassName) else { + return + } + + acceletators.forEach { (accelerator: NSDictionary) in + guard let matchedGPU = gpus.first(where: { (gpu: NSDictionary) -> Bool in + guard let deviceID = gpu["device-id"] as? Data, let vendorID = gpu["vendor-id"] as? Data else { + return false + } + + let pciMatch = "0x" + Data([deviceID[1], deviceID[0], vendorID[1], vendorID[0]]).map { String(format: "%02hhX", $0) }.joined() + let accMatch = accelerator["IOPCIMatch"] as? String ?? accelerator["IOPCIPrimaryMatch"] as? String ?? "" + + return accMatch.range(of: pciMatch) != nil + }) else { return } + + guard let agcInfo = accelerator["AGCInfo"] as? [String:Int] else { + return + } + + guard let stats = accelerator["PerformanceStatistics"] as? [String:Any] else { + return + } + + guard let model = matchedGPU.object(forKey: "model") as? Data else { + return + } + let modelName = String(data: model, encoding: .ascii)!.replacingOccurrences(of: "\0", with: "") + + guard let IOClass = accelerator.object(forKey: "IOClass") as? String else { + return + } + + if self.gpus.list.first(where: { $0.name == modelName }) == nil { + self.gpus.list.append(GPU_Info(name: modelName, IOclass: IOClass)) + } + + guard let idx = self.gpus.list.firstIndex(where: { $0.name == modelName }) else { + return + } + + let utilization = stats["Device Utilization %"] as? Int ?? 0 + let totalVram = accelerator["VRAM,totalMB"] as? Int ?? matchedGPU["VRAM,totalMB"] as? Int ?? 0 + let freeVram = stats["vramFreeBytes"] as? Int ?? 0 + let coreClock = stats["Core Clock(MHz)"] as? Int ?? 0 + var power = stats["Total Power(W)"] as? Int ?? 0 + var temperature = stats["Temperature(C)"] as? Int ?? 0 + + if IOClass == "IntelAccelerator" { + if temperature == 0 { + if let tmp = self.smc?.pointee.getValue("TCGC") { + temperature = Int(tmp) + } else if let tmp = self.smc?.pointee.getValue("TG0D") { + temperature = Int(tmp) + } + } + + if power == 0 { + if let pwr = self.smc?.pointee.getValue("PCPG") { + power = Int(pwr) + } else if let pwr = self.smc?.pointee.getValue("PCGC") { + power = Int(pwr) + } else if let pwr = self.smc?.pointee.getValue("PCGM") { + power = Int(pwr) + } + } + } + + self.gpus.list[idx].state = agcInfo["poweredOffByAGC"] == 0 + + self.gpus.list[idx].utilization = utilization == 0 ? 0 : Double(utilization)/100 + self.gpus.list[idx].totalVram = totalVram + self.gpus.list[idx].freeVram = freeVram + self.gpus.list[idx].coreClock = coreClock + self.gpus.list[idx].power = power + self.gpus.list[idx].temperature = temperature + } + + self.callback(self.gpus) } } diff --git a/Modules/GPU/settings.swift b/Modules/GPU/settings.swift new file mode 100644 index 00000000..4381afbc --- /dev/null +++ b/Modules/GPU/settings.swift @@ -0,0 +1,126 @@ +// +// settings.swift +// GPU +// +// Created by Serhiy Mytrovtsiy on 17/08/2020. +// Using Swift 5.0. +// Running on macOS 10.15. +// +// Copyright © 2020 Serhiy Mytrovtsiy. All rights reserved. +// + +import Cocoa +import StatsKit +import ModuleKit + +internal class Settings: NSView, Settings_v { + private var updateIntervalValue: String = "1" + private let listOfUpdateIntervals: [String] = ["1", "2", "3", "5", "10", "15", "30"] + private var selectedGPU: String + + private let title: String + private let store: UnsafePointer + + public var selectedGPUHandler: (String) -> Void = {_ in } + public var callback: (() -> Void) = {} + public var setInterval: ((_ value: Double) -> Void) = {_ in } + + private var hyperthreadView: NSView? = nil + + private var button: NSPopUpButton? + + public init(_ title: String, store: UnsafePointer) { + self.title = title + self.store = store + self.selectedGPU = store.pointee.string(key: "\(self.title)_gpu", defaultValue: "") + self.updateIntervalValue = store.pointee.string(key: "\(self.title)_updateInterval", defaultValue: self.updateIntervalValue) + + super.init(frame: CGRect( + x: 0, + y: 0, + width: Constants.Settings.width - (Constants.Settings.margin*2), + height: 0 + )) + + self.wantsLayer = true + self.canDrawConcurrently = true + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + public func load(widget: widget_t) { + self.subviews.forEach{ $0.removeFromSuperview() } + + let rowHeight: CGFloat = 30 + let num: CGFloat = 1 + + self.addSubview(SelectTitleRow( + frame: NSRect(x: Constants.Settings.margin, y: Constants.Settings.margin + (rowHeight + Constants.Settings.margin) * num, width: self.frame.width - (Constants.Settings.margin*2), height: rowHeight), + title: "Update interval", + action: #selector(changeUpdateInterval), + items: self.listOfUpdateIntervals.map{ "\($0) sec" }, + selected: "\(self.updateIntervalValue) sec" + )) + + self.addGPUSelector() + + self.setFrameSize(NSSize(width: self.frame.width, height: (rowHeight*(num+1)) + (Constants.Settings.margin*(2+num)))) + } + + private func addGPUSelector() { + let view: NSView = NSView(frame: NSRect( + x: Constants.Settings.margin, + y: Constants.Settings.margin, + width: self.frame.width - Constants.Settings.margin*2, + height: 30 + )) + + let rowTitle: NSTextField = LabelField(frame: NSRect(x: 0, y: (view.frame.height - 16)/2, width: view.frame.width - 52, height: 17), "GPU to show") + rowTitle.font = NSFont.systemFont(ofSize: 13, weight: .light) + rowTitle.textColor = .textColor + + self.button = NSPopUpButton(frame: NSRect(x: view.frame.width - 200, y: -1, width: 200, height: 30)) + self.button!.target = self + self.button?.action = #selector(self.handleSelection) + + view.addSubview(rowTitle) + view.addSubview(self.button!) + + self.addSubview(view) + } + + internal func setList(_ list: GPUs) { + let disks = list.active().map{ $0.name } + DispatchQueue.main.async(execute: { + if self.button?.itemTitles.count != disks.count { + self.button?.removeAllItems() + } + + if disks != self.button?.itemTitles { + self.button?.addItems(withTitles: disks) + if self.selectedGPU != "" { + self.button?.selectItem(withTitle: self.selectedGPU) + } + } + }) + } + + @objc private func changeUpdateInterval(_ sender: NSMenuItem) { + let newUpdateInterval = sender.title.replacingOccurrences(of: " sec", with: "") + self.updateIntervalValue = newUpdateInterval + store.pointee.set(key: "\(self.title)_updateInterval", value: self.updateIntervalValue) + + if let value = Double(self.updateIntervalValue) { + self.setInterval(value) + } + } + + @objc private func handleSelection(_ sender: NSPopUpButton) { + guard let item = sender.selectedItem else { return } + self.selectedGPU = item.title + self.store.pointee.set(key: "\(self.title)_gpu", value: item.title) + self.selectedGPUHandler(item.title) + } +} diff --git a/Modules/Net/settings.swift b/Modules/Net/settings.swift index 6132d7c2..2c4a20a6 100644 --- a/Modules/Net/settings.swift +++ b/Modules/Net/settings.swift @@ -58,13 +58,13 @@ internal class Settings: NSView, Settings_v { } private func addNetworkSelector() { - let view: NSView = NSView(frame: NSRect(x: Constants.Settings.margin, y: Constants.Settings.margin, width: self.frame.width, height: 29)) + let view: NSView = NSView(frame: NSRect(x: Constants.Settings.margin, y: Constants.Settings.margin, width: self.frame.width, height: 30)) let rowTitle: NSTextField = LabelField(frame: NSRect(x: 0, y: (view.frame.height - 16)/2, width: view.frame.width - 52, height: 17), "Network interface") rowTitle.font = NSFont.systemFont(ofSize: 13, weight: .light) rowTitle.textColor = .textColor - self.button = NSPopUpButton(frame: NSRect(x: view.frame.width - 200 - Constants.Settings.margin*2, y: -1, width: 200, height: 30)) + self.button = NSPopUpButton(frame: NSRect(x: view.frame.width - 200 - Constants.Settings.margin*2, y: 0, width: 200, height: 30)) self.button!.target = self self.button?.action = #selector(self.handleSelection) diff --git a/Modules/Sensors/values.swift b/Modules/Sensors/values.swift index f84882a7..db418c2c 100644 --- a/Modules/Sensors/values.swift +++ b/Modules/Sensors/values.swift @@ -104,7 +104,7 @@ let SensorsList: [Sensor_t] = [ Sensor_t(key: "Th3H", name: "Heatpipe 4", group: SensorGroup.Sensor.rawValue, type: SensorType.Temperature.rawValue), Sensor_t(key: "TZ0C", name: "Termal zone 1", group: SensorGroup.Sensor.rawValue, type: SensorType.Temperature.rawValue), Sensor_t(key: "TZ1C", name: "Termal zone 2", group: SensorGroup.Sensor.rawValue, type: SensorType.Temperature.rawValue), - + Sensor_t(key: "TC0E", name: "CPU 1", group: SensorGroup.CPU.rawValue, type: SensorType.Temperature.rawValue), Sensor_t(key: "TC0F", name: "CPU 2", group: SensorGroup.CPU.rawValue, type: SensorType.Temperature.rawValue), Sensor_t(key: "TC0D", name: "CPU die", group: SensorGroup.CPU.rawValue, type: SensorType.Temperature.rawValue), @@ -112,7 +112,7 @@ let SensorsList: [Sensor_t] = [ Sensor_t(key: "TC0H", name: "CPU heatsink", group: SensorGroup.CPU.rawValue, type: SensorType.Temperature.rawValue), Sensor_t(key: "TC0P", name: "CPU proximity", group: SensorGroup.CPU.rawValue, type: SensorType.Temperature.rawValue), Sensor_t(key: "TCAD", name: "CPU package", group: SensorGroup.CPU.rawValue, type: SensorType.Temperature.rawValue), - + Sensor_t(key: "TC0c", name: "CPU core 1", group: SensorGroup.CPU.rawValue, type: SensorType.Temperature.rawValue), Sensor_t(key: "TC1c", name: "CPU core 2", group: SensorGroup.CPU.rawValue, type: SensorType.Temperature.rawValue), Sensor_t(key: "TC2c", name: "CPU core 3", group: SensorGroup.CPU.rawValue, type: SensorType.Temperature.rawValue), @@ -129,12 +129,12 @@ let SensorsList: [Sensor_t] = [ Sensor_t(key: "TC13c", name: "CPU core 14", group: SensorGroup.CPU.rawValue, type: SensorType.Temperature.rawValue), Sensor_t(key: "TC14c", name: "CPU core 15", group: SensorGroup.CPU.rawValue, type: SensorType.Temperature.rawValue), Sensor_t(key: "TC15c", name: "CPU core 16", group: SensorGroup.CPU.rawValue, type: SensorType.Temperature.rawValue), - + Sensor_t(key: "TCGC", name: "GPU Intel Graphics", group: SensorGroup.GPU.rawValue, type: SensorType.Temperature.rawValue), Sensor_t(key: "TG0D", name: "GPU die", group: SensorGroup.GPU.rawValue, type: SensorType.Temperature.rawValue), Sensor_t(key: "TG0H", name: "GPU heatsink", group: SensorGroup.GPU.rawValue, type: SensorType.Temperature.rawValue), Sensor_t(key: "TG0P", name: "GPU proximity", group: SensorGroup.GPU.rawValue, type: SensorType.Temperature.rawValue), - + Sensor_t(key: "Tm0P", name: "Mainboard", group: SensorGroup.System.rawValue, type: SensorType.Temperature.rawValue), Sensor_t(key: "Tp0P", name: "Powerboard", group: SensorGroup.System.rawValue, type: SensorType.Temperature.rawValue), Sensor_t(key: "TB1T", name: "Battery", group: SensorGroup.System.rawValue, type: SensorType.Temperature.rawValue), @@ -144,11 +144,11 @@ let SensorsList: [Sensor_t] = [ Sensor_t(key: "TI1P", name: "Thunderbold 2", group: SensorGroup.System.rawValue, type: SensorType.Temperature.rawValue), Sensor_t(key: "TI2P", name: "Thunderbold 3", group: SensorGroup.System.rawValue, type: SensorType.Temperature.rawValue), Sensor_t(key: "TI3P", name: "Thunderbold 4", group: SensorGroup.System.rawValue, type: SensorType.Temperature.rawValue), - + Sensor_t(key: "TN0D", name: "Northbridge die", group: SensorGroup.System.rawValue, type: SensorType.Temperature.rawValue), Sensor_t(key: "TN0H", name: "Northbridge heatsink", group: SensorGroup.System.rawValue, type: SensorType.Temperature.rawValue), Sensor_t(key: "TN0P", name: "Northbridge proximity", group: SensorGroup.System.rawValue, type: SensorType.Temperature.rawValue), - + /// Voltage Sensor_t(key: "VCAC", name: "CPU IA", group: SensorGroup.CPU.rawValue, type: SensorType.Voltage.rawValue), Sensor_t(key: "VCSC", name: "CPU System Agent", group: SensorGroup.CPU.rawValue, type: SensorType.Voltage.rawValue), @@ -168,13 +168,13 @@ let SensorsList: [Sensor_t] = [ Sensor_t(key: "VC13C", name: "CPU Core 14", group: SensorGroup.CPU.rawValue, type: SensorType.Voltage.rawValue), Sensor_t(key: "VC14C", name: "CPU Core 15", group: SensorGroup.CPU.rawValue, type: SensorType.Voltage.rawValue), Sensor_t(key: "VC15C", name: "CPU Core 16", group: SensorGroup.CPU.rawValue, type: SensorType.Voltage.rawValue), - + Sensor_t(key: "VCTC", name: "GPU Intel Graphics", group: SensorGroup.GPU.rawValue, type: SensorType.Voltage.rawValue), Sensor_t(key: "VG0C", name: "GPU", group: SensorGroup.GPU.rawValue, type: SensorType.Voltage.rawValue), - + Sensor_t(key: "VM0R", name: "Memory", group: SensorGroup.System.rawValue, type: SensorType.Voltage.rawValue), Sensor_t(key: "Vb0R", name: "CMOS", group: SensorGroup.System.rawValue, type: SensorType.Voltage.rawValue), - + Sensor_t(key: "VD0R", name: "DC In", group: SensorGroup.Sensor.rawValue, type: SensorType.Voltage.rawValue), Sensor_t(key: "VP0R", name: "12V rail", group: SensorGroup.Sensor.rawValue, type: SensorType.Voltage.rawValue), Sensor_t(key: "Vp0C", name: "12V vcc", group: SensorGroup.Sensor.rawValue, type: SensorType.Voltage.rawValue), @@ -183,7 +183,7 @@ let SensorsList: [Sensor_t] = [ Sensor_t(key: "VV1S", name: "5V", group: SensorGroup.Sensor.rawValue, type: SensorType.Voltage.rawValue), Sensor_t(key: "VV9S", name: "12V", group: SensorGroup.Sensor.rawValue, type: SensorType.Voltage.rawValue), Sensor_t(key: "VeES", name: "PCI 12V", group: SensorGroup.Sensor.rawValue, type: SensorType.Voltage.rawValue), - + /// Power Sensor_t(key: "PC0C", name: "CPU Core", group: SensorGroup.CPU.rawValue, type: SensorType.Power.rawValue), Sensor_t(key: "PCAM", name: "CPU Core (IMON)", group: SensorGroup.CPU.rawValue, type: SensorType.Power.rawValue), @@ -193,7 +193,7 @@ let SensorsList: [Sensor_t] = [ Sensor_t(key: "PCPR", name: "CPU Package total (SMC)", group: SensorGroup.CPU.rawValue, type: SensorType.Power.rawValue), Sensor_t(key: "PC0R", name: "CPU Computing high side", group: SensorGroup.CPU.rawValue, type: SensorType.Power.rawValue), Sensor_t(key: "PC0G", name: "CPU GFX", group: SensorGroup.CPU.rawValue, type: SensorType.Power.rawValue), - + Sensor_t(key: "PCPG", name: "GPU Intel Graphics", group: SensorGroup.GPU.rawValue, type: SensorType.Power.rawValue), Sensor_t(key: "PG0R", name: "GPU", group: SensorGroup.GPU.rawValue, type: SensorType.Power.rawValue), Sensor_t(key: "PCGC", name: "Intel GPU", group: SensorGroup.GPU.rawValue, type: SensorType.Power.rawValue), @@ -203,7 +203,7 @@ let SensorsList: [Sensor_t] = [ Sensor_t(key: "PPBR", name: "Battery", group: SensorGroup.Sensor.rawValue, type: SensorType.Power.rawValue), Sensor_t(key: "PDTR", name: "DC In", group: SensorGroup.Sensor.rawValue, type: SensorType.Power.rawValue), Sensor_t(key: "PSTR", name: "System total", group: SensorGroup.Sensor.rawValue, type: SensorType.Power.rawValue), - + /// Frequency Sensor_t(key: "FRC0", name: "CPU 1", group: SensorGroup.CPU.rawValue, type: SensorType.Frequency.rawValue), Sensor_t(key: "FRC1", name: "CPU 2", group: SensorGroup.CPU.rawValue, type: SensorType.Frequency.rawValue), @@ -221,11 +221,11 @@ let SensorsList: [Sensor_t] = [ Sensor_t(key: "FRC13", name: "CPU 14", group: SensorGroup.CPU.rawValue, type: SensorType.Frequency.rawValue), Sensor_t(key: "FRC14", name: "CPU 15", group: SensorGroup.CPU.rawValue, type: SensorType.Frequency.rawValue), Sensor_t(key: "FRC15", name: "CPU 16", group: SensorGroup.CPU.rawValue, type: SensorType.Frequency.rawValue), - + Sensor_t(key: "CG0C", name: "GPU", group: SensorGroup.GPU.rawValue, type: SensorType.Frequency.rawValue), Sensor_t(key: "CG0S", name: "GPU shader", group: SensorGroup.GPU.rawValue, type: SensorType.Frequency.rawValue), Sensor_t(key: "CG0M", name: "GPU memory", group: SensorGroup.GPU.rawValue, type: SensorType.Frequency.rawValue), - + /// Battery Sensor_t(key: "B0AV", name: "Voltage", group: SensorGroup.Sensor.rawValue, type: SensorType.Battery.rawValue), Sensor_t(key: "B0AC", name: "Amperage", group: SensorGroup.Sensor.rawValue, type: SensorType.Battery.rawValue), diff --git a/Stats.xcodeproj/project.pbxproj b/Stats.xcodeproj/project.pbxproj index 5b1473d3..1cae54a4 100644 --- a/Stats.xcodeproj/project.pbxproj +++ b/Stats.xcodeproj/project.pbxproj @@ -37,6 +37,8 @@ 9A3E17E8247AA8E100449CD1 /* Speed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A3E17E7247AA8E100449CD1 /* Speed.swift */; }; 9A3E17EA247B07BF00449CD1 /* popup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A3E17E9247B07BF00449CD1 /* popup.swift */; }; 9A41530C24ABC3AF00A2BDA7 /* Memory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A41530B24ABC3AF00A2BDA7 /* Memory.swift */; }; + 9A53EBF924EAFA5200648841 /* settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A53EBF824EAFA5200648841 /* settings.swift */; }; + 9A53EBFB24EB041E00648841 /* popup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A53EBFA24EB041E00648841 /* popup.swift */; }; 9A58DE9E24B363D800716A9F /* popup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A58DE9D24B363D800716A9F /* popup.swift */; }; 9A58DEA024B363F300716A9F /* settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A58DE9F24B363F300716A9F /* settings.swift */; }; 9A58DEA424B3647600716A9F /* settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A58DEA324B3647600716A9F /* settings.swift */; }; @@ -462,6 +464,8 @@ 9A3E17E9247B07BF00449CD1 /* popup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = popup.swift; sourceTree = ""; }; 9A41530B24ABC3AF00A2BDA7 /* Memory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Memory.swift; sourceTree = ""; }; 9A5349CD23D8832E00C23824 /* Reachability.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Reachability.framework; path = Carthage/Build/Mac/Reachability.framework; sourceTree = ""; }; + 9A53EBF824EAFA5200648841 /* settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = settings.swift; sourceTree = ""; }; + 9A53EBFA24EB041E00648841 /* popup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = popup.swift; sourceTree = ""; }; 9A58DE9D24B363D800716A9F /* popup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = popup.swift; sourceTree = ""; }; 9A58DE9F24B363F300716A9F /* settings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = settings.swift; sourceTree = ""; }; 9A58DEA324B3647600716A9F /* settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = settings.swift; sourceTree = ""; }; @@ -777,6 +781,8 @@ children = ( 9A90E19524EAD35F00471E9A /* main.swift */, 9A90E1A224EAD66600471E9A /* reader.swift */, + 9A53EBF824EAFA5200648841 /* settings.swift */, + 9A53EBFA24EB041E00648841 /* popup.swift */, 9A90E18C24EAD2BB00471E9A /* Info.plist */, 9A90E19724EAD3B000471E9A /* config.plist */, ); @@ -1435,6 +1441,8 @@ files = ( 9A90E1A324EAD66600471E9A /* reader.swift in Sources */, 9A90E19624EAD35F00471E9A /* main.swift in Sources */, + 9A53EBFB24EB041E00648841 /* popup.swift in Sources */, + 9A53EBF924EAFA5200648841 /* settings.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/StatsKit/SystemKit.swift b/StatsKit/SystemKit.swift index dcc18f9b..7b4fd03c 100644 --- a/StatsKit/SystemKit.swift +++ b/StatsKit/SystemKit.swift @@ -382,4 +382,5 @@ let osDict: [Int: String] = [ 13: "High Sierra", 14: "Mojave", 15: "Catalina", + 16: "Big Sur", ] diff --git a/StatsKit/extensions.swift b/StatsKit/extensions.swift index f2716b1d..94a8a8b8 100644 --- a/StatsKit/extensions.swift +++ b/StatsKit/extensions.swift @@ -876,6 +876,26 @@ public func getIOParent(_ obj: io_registry_entry_t) -> io_registry_entry_t? { return parent } +public func fetchIOService(_ name: String) -> [NSDictionary]? { + var iterator: io_iterator_t = io_iterator_t() + var obj: io_registry_entry_t = 1 + var list: [NSDictionary] = [] + + let result = IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching(name), &iterator) + if result == kIOReturnSuccess { + while obj != 0 { + obj = IOIteratorNext(iterator) + if let props = getIOProperties(obj) { + list.append(props) + } + IOObjectRelease(obj) + } + IOObjectRelease(iterator) + } + + return list.isEmpty ? nil : list +} + public func getIOProperties(_ entry: io_registry_entry_t) -> NSDictionary? { var properties: Unmanaged? = nil From 95f941149fa1d4903c55eef73ebbc11f8055da52 Mon Sep 17 00:00:00 2001 From: Serhiy Mytrovtsiy Date: Mon, 17 Aug 2020 22:51:21 +0200 Subject: [PATCH 3/4] - fix unnecessary popup interface update (when popup closed) GPU module MVP --- Modules/Battery/popup.swift | 16 ++-- Modules/CPU/popup.swift | 42 ++++----- Modules/Disk/popup.swift | 37 ++++---- Modules/GPU/main.swift | 5 -- Modules/GPU/popup.swift | 147 +++++++++++++++++++++++++++++++- Modules/GPU/reader.swift | 21 +---- Modules/Memory/popup.swift | 33 +++---- Modules/Net/popup.swift | 82 +++++++++--------- Stats.xcodeproj/project.pbxproj | 2 +- 9 files changed, 261 insertions(+), 124 deletions(-) diff --git a/Modules/Battery/popup.swift b/Modules/Battery/popup.swift index fe888f46..6fc76f3c 100644 --- a/Modules/Battery/popup.swift +++ b/Modules/Battery/popup.swift @@ -189,13 +189,15 @@ internal class Popup: NSView { public func processCallback(_ list: [TopProcess]) { DispatchQueue.main.async(execute: { - for i in 0.. { } let utilization = stats["Device Utilization %"] as? Int ?? 0 - let totalVram = accelerator["VRAM,totalMB"] as? Int ?? matchedGPU["VRAM,totalMB"] as? Int ?? 0 - let freeVram = stats["vramFreeBytes"] as? Int ?? 0 - let coreClock = stats["Core Clock(MHz)"] as? Int ?? 0 - var power = stats["Total Power(W)"] as? Int ?? 0 +// let totalVram = (accelerator["VRAM,totalMB"] as? Int ?? matchedGPU["VRAM,totalMB"] as? Int ?? 0) * 1000000 +// let freeVram = stats["vramFreeBytes"] as? Int ?? 0 +// let coreClock = stats["Core Clock(MHz)"] as? Int ?? 0 var temperature = stats["Temperature(C)"] as? Int ?? 0 if IOClass == "IntelAccelerator" { @@ -80,25 +79,11 @@ internal class InfoReader: Reader { temperature = Int(tmp) } } - - if power == 0 { - if let pwr = self.smc?.pointee.getValue("PCPG") { - power = Int(pwr) - } else if let pwr = self.smc?.pointee.getValue("PCGC") { - power = Int(pwr) - } else if let pwr = self.smc?.pointee.getValue("PCGM") { - power = Int(pwr) - } - } } self.gpus.list[idx].state = agcInfo["poweredOffByAGC"] == 0 self.gpus.list[idx].utilization = utilization == 0 ? 0 : Double(utilization)/100 - self.gpus.list[idx].totalVram = totalVram - self.gpus.list[idx].freeVram = freeVram - self.gpus.list[idx].coreClock = coreClock - self.gpus.list[idx].power = power self.gpus.list[idx].temperature = temperature } diff --git a/Modules/Memory/popup.swift b/Modules/Memory/popup.swift index 63666cc5..1ee80c64 100644 --- a/Modules/Memory/popup.swift +++ b/Modules/Memory/popup.swift @@ -161,29 +161,32 @@ internal class Popup: NSView { self.totalField?.stringValue = Units(bytes: Int64(value.total)).getReadableMemory() self.usedField?.stringValue = Units(bytes: Int64(value.used)).getReadableMemory() self.freeField?.stringValue = Units(bytes: Int64(value.free)).getReadableMemory() + + self.circle?.setValue(value.usage) + self.circle?.setSegments([ + circle_segment(value: value.active/value.total, color: NSColor.systemBlue), + circle_segment(value: value.wired/value.total, color: NSColor.systemOrange), + circle_segment(value: value.compressed/value.total, color: NSColor.systemPink) + ]) + self.level?.setLevel(value.pressureLevel) + self.initialized = true } - - self.circle?.setValue(value.usage) - self.circle?.setSegments([ - circle_segment(value: value.active/value.total, color: NSColor.systemBlue), - circle_segment(value: value.wired/value.total, color: NSColor.systemOrange), - circle_segment(value: value.compressed/value.total, color: NSColor.systemPink) - ]) self.chart?.addValue(value.usage) - self.level?.setLevel(value.pressureLevel) }) } public func processCallback(_ list: [TopProcess]) { DispatchQueue.main.async(execute: { - for i in 0.. Date: Tue, 18 Aug 2020 00:04:48 +0200 Subject: [PATCH 4/4] - fix popup processes when open for first time - update GPU popup view --- Modules/Battery/popup.swift | 5 +- Modules/CPU/popup.swift | 11 +-- Modules/GPU/config.plist | 2 +- Modules/GPU/popup.swift | 135 ++++++++++++++++++++++++------------ Modules/GPU/reader.swift | 16 +++-- Modules/Memory/popup.swift | 9 ++- Modules/Net/popup.swift | 1 + Stats/AppDelegate.swift | 10 ++- 8 files changed, 128 insertions(+), 61 deletions(-) diff --git a/Modules/Battery/popup.swift b/Modules/Battery/popup.swift index 6fc76f3c..46c492fc 100644 --- a/Modules/Battery/popup.swift +++ b/Modules/Battery/popup.swift @@ -40,6 +40,7 @@ internal class Popup: NSView { private var chargingStateField: NSTextField? = nil private var processes: [ProcessView] = [] + private var processesInitialized: Bool = false public init() { super.init(frame: NSRect( @@ -189,7 +190,7 @@ internal class Popup: NSView { public func processCallback(_ list: [TopProcess]) { DispatchQueue.main.async(execute: { - if (self.window?.isVisible ?? false) { + if (self.window?.isVisible ?? false) || !self.processesInitialized { for i in 0..Name GPU State - + Widgets mini diff --git a/Modules/GPU/popup.swift b/Modules/GPU/popup.swift index 04dce4dd..54477896 100644 --- a/Modules/GPU/popup.swift +++ b/Modules/GPU/popup.swift @@ -61,18 +61,17 @@ private class GPUView: NSView { private let height: CGFloat = 60 private let margin: CGFloat = 4 - private var name: String - private var state: Bool + private var value: GPU_Info - private var chart: LineChartView? = nil - private var utilization: HalfCircleGraphView? = nil - private var temperature: HalfCircleGraphView? = nil + private var temperatureChart: LineChartView? = nil + private var utilizationChart: LineChartView? = nil + private var temperatureCirle: HalfCircleGraphView? = nil + private var utilizationCircle: HalfCircleGraphView? = nil private var stateView: NSView? = nil public init(_ frame: NSRect, gpu: GPU_Info) { - self.name = gpu.name - self.state = gpu.state + self.value = gpu super.init(frame: frame) @@ -80,8 +79,8 @@ private class GPUView: NSView { self.layer?.cornerRadius = 2 self.initName() - self.initCircles() - self.initChart() + self.initTemperature() + self.initUtilization() } required init?(coder: NSCoder) { @@ -89,21 +88,21 @@ private class GPUView: NSView { } private func initName() { - let y: CGFloat = self.frame.height - Constants.Popup.separatorHeight - let width: CGFloat = self.name.widthOfString(usingFont: NSFont.systemFont(ofSize: 12, weight: .medium)) + 16 + let y: CGFloat = self.frame.height - 23 + let width: CGFloat = self.value.name.widthOfString(usingFont: NSFont.systemFont(ofSize: 12, weight: .medium)) + 16 - let view: NSView = NSView(frame: NSRect(x: (self.frame.width - width)/2, y: y, width: width, height: 30)) + let view: NSView = NSView(frame: NSRect(x: (self.frame.width - width)/2, y: y, width: width, height: 20)) let labelView: NSTextField = TextView(frame: NSRect(x: 0, y: (view.frame.height-15)/2, width: width - 8, height: 15)) labelView.alignment = .center labelView.textColor = .secondaryLabelColor labelView.font = NSFont.systemFont(ofSize: 12, weight: .medium) - labelView.stringValue = self.name + labelView.stringValue = self.value.name let stateView: NSView = NSView(frame: NSRect(x: width - 8, y: (view.frame.height-7)/2, width: 6, height: 6)) stateView.wantsLayer = true - stateView.layer?.backgroundColor = (self.state ? NSColor.systemGreen : NSColor.systemRed).cgColor - stateView.toolTip = "GPU \(self.state ? "enabled" : "disabled")" + stateView.layer?.backgroundColor = (self.value.state ? NSColor.systemGreen : NSColor.systemRed).cgColor + stateView.toolTip = "GPU \(self.value.state ? "enabled" : "disabled")" stateView.layer?.cornerRadius = 4 view.addSubview(labelView) @@ -113,7 +112,7 @@ private class GPUView: NSView { self.stateView = stateView } - private func initCircles() { + private func initTemperature() { let view: NSView = NSView(frame: NSRect( x: self.margin, y: self.height + (self.margin*2), @@ -121,37 +120,80 @@ private class GPUView: NSView { height: self.height )) - let circleSize: CGFloat = 50 - self.temperature = HalfCircleGraphView(frame: NSRect( - x: ((view.frame.width/2) - circleSize)/2 + 10, - y: 5, - width: circleSize, - height: circleSize - )) - self.temperature!.toolTip = "GPU temperature" - self.utilization = HalfCircleGraphView(frame: NSRect( - x: (view.frame.width/2) + (((view.frame.width/2) - circleSize)/2) - 10, - y: 5, - width: circleSize, - height: circleSize - )) - self.utilization!.toolTip = "GPU utilization" + let circleWidth: CGFloat = 70 + let circleSize: CGFloat = 44 - view.addSubview(self.temperature!) - view.addSubview(self.utilization!) + let chartView: NSView = NSView(frame: NSRect( + x: 0, + y: 0, + width: view.frame.width - circleWidth, + height: view.frame.height + )) + chartView.wantsLayer = true + chartView.layer?.backgroundColor = NSColor.lightGray.withAlphaComponent(0.1).cgColor + chartView.layer?.cornerRadius = 3 + self.temperatureChart = LineChartView(frame: NSRect(x: 0, y: 0, width: chartView.frame.width, height: chartView.frame.height), num: 120) + chartView.addSubview(self.temperatureChart!) + + self.temperatureCirle = HalfCircleGraphView(frame: NSRect( + x: (view.frame.width - circleWidth) + (circleWidth - circleSize)/2, + y: (view.frame.height - circleSize)/2 - 3, + width: circleSize, + height: circleSize + )) + self.temperatureCirle!.toolTip = "GPU temperature" + + view.addSubview(chartView) + view.addSubview(self.temperatureCirle!) + + self.temperatureCirle?.setValue(Double(self.value.temperature)) + let formatter = MeasurementFormatter() + formatter.numberFormatter.maximumFractionDigits = 0 + let measurement = Measurement(value: Double(self.value.temperature), unit: UnitTemperature.celsius) + self.temperatureCirle?.setText(formatter.string(from: measurement)) + self.temperatureChart?.addValue(Double(self.value.temperature) / 100) self.addSubview(view) } - private func initChart() { - let view: NSView = NSView(frame: NSRect(x: self.margin, y: self.margin, width: self.frame.width - (self.margin*2), height: self.height)) - view.wantsLayer = true - view.layer?.backgroundColor = NSColor.lightGray.withAlphaComponent(0.1).cgColor - view.layer?.cornerRadius = 3 + private func initUtilization() { + let view: NSView = NSView(frame: NSRect( + x: self.margin, + y: self.margin, + width: self.frame.width - (self.margin*2), + height: self.height + )) - self.chart = LineChartView(frame: NSRect(x: 1, y: 0, width: view.frame.width, height: view.frame.height), num: 120) + let circleWidth: CGFloat = 70 + let circleSize: CGFloat = 44 + + let chartView: NSView = NSView(frame: NSRect( + x: 0, + y: 0, + width: view.frame.width - circleWidth, + height: view.frame.height + )) + chartView.wantsLayer = true + chartView.layer?.backgroundColor = NSColor.lightGray.withAlphaComponent(0.1).cgColor + chartView.layer?.cornerRadius = 3 + self.utilizationChart = LineChartView(frame: NSRect(x: 0, y: 0, width: chartView.frame.width, height: chartView.frame.height), num: 120) + chartView.addSubview(self.utilizationChart!) + + self.utilizationCircle = HalfCircleGraphView(frame: NSRect( + x: (view.frame.width - circleWidth) + (circleWidth - circleSize)/2, + y: (view.frame.height - circleSize)/2 - 3, + width: circleSize, + height: circleSize + )) + self.utilizationCircle!.toolTip = "GPU utilization" + + view.addSubview(chartView) + view.addSubview(self.utilizationCircle!) + + self.utilizationCircle?.setValue(self.value.utilization) + self.utilizationCircle?.setText("\(Int(self.value.utilization*100))%") + self.utilizationChart?.addValue(self.value.utilization) - view.addSubview(self.chart!) self.addSubview(view) } @@ -161,17 +203,18 @@ private class GPUView: NSView { self.stateView?.layer?.backgroundColor = (gpu.state ? NSColor.systemGreen : NSColor.systemRed).cgColor self.stateView?.toolTip = "GPU \(gpu.state ? "enabled" : "disabled")" - self.utilization?.setValue(gpu.utilization) - self.utilization?.setText("\(Int(gpu.utilization*100))%") - self.temperature?.setValue(Double(gpu.temperature)) - + self.temperatureCirle?.setValue(Double(gpu.temperature)) let formatter = MeasurementFormatter() formatter.numberFormatter.maximumFractionDigits = 0 let measurement = Measurement(value: Double(gpu.temperature), unit: UnitTemperature.celsius) - self.temperature?.setText(formatter.string(from: measurement)) + self.temperatureCirle?.setText(formatter.string(from: measurement)) - self.chart?.addValue(gpu.utilization) + self.utilizationCircle?.setValue(gpu.utilization) + self.utilizationCircle?.setText("\(Int(gpu.utilization*100))%") } + + self.temperatureChart?.addValue(Double(gpu.temperature) / 100) + self.utilizationChart?.addValue(gpu.utilization) }) } } diff --git a/Modules/GPU/reader.swift b/Modules/GPU/reader.swift index f7612259..035f74c3 100644 --- a/Modules/GPU/reader.swift +++ b/Modules/GPU/reader.swift @@ -16,20 +16,25 @@ import os.log internal class InfoReader: Reader { internal var smc: UnsafePointer? = nil - private var gpus: GPUs = GPUs() - public override func read() { + private var gpus: GPUs = GPUs() + private var devices: [NSDictionary]? = nil + + public override func setup() { guard let devices = fetchIOService("IOPCIDevice") else { return } - let gpus = devices.filter{ $0.object(forKey: "IOName") as? String == "display" } + self.devices = devices.filter{ $0.object(forKey: "IOName") as? String == "display" } + } + + public override func read() { guard let acceletators = fetchIOService(kIOAcceleratorClassName) else { return } acceletators.forEach { (accelerator: NSDictionary) in - guard let matchedGPU = gpus.first(where: { (gpu: NSDictionary) -> Bool in + guard let matchedGPU = self.devices?.first(where: { (gpu: NSDictionary) -> Bool in guard let deviceID = gpu["device-id"] as? Data, let vendorID = gpu["vendor-id"] as? Data else { return false } @@ -86,7 +91,8 @@ internal class InfoReader: Reader { self.gpus.list[idx].utilization = utilization == 0 ? 0 : Double(utilization)/100 self.gpus.list[idx].temperature = temperature } - + + self.gpus.list.sort{ !$0.state && $1.state } self.callback(self.gpus) } } diff --git a/Modules/Memory/popup.swift b/Modules/Memory/popup.swift index 1ee80c64..fe124610 100644 --- a/Modules/Memory/popup.swift +++ b/Modules/Memory/popup.swift @@ -32,6 +32,7 @@ internal class Popup: NSView { private var circle: CircleGraphView? = nil private var level: PressureView? = nil private var initialized: Bool = false + private var processesInitialized: Bool = false private var processes: [ProcessView] = [] @@ -157,11 +158,11 @@ internal class Popup: NSView { self.inactiveField?.stringValue = Units(bytes: Int64(value.inactive)).getReadableMemory() self.wiredField?.stringValue = Units(bytes: Int64(value.wired)).getReadableMemory() self.compressedField?.stringValue = Units(bytes: Int64(value.compressed)).getReadableMemory() - + self.totalField?.stringValue = Units(bytes: Int64(value.total)).getReadableMemory() self.usedField?.stringValue = Units(bytes: Int64(value.used)).getReadableMemory() self.freeField?.stringValue = Units(bytes: Int64(value.free)).getReadableMemory() - + self.circle?.setValue(value.usage) self.circle?.setSegments([ circle_segment(value: value.active/value.total, color: NSColor.systemBlue), @@ -178,7 +179,7 @@ internal class Popup: NSView { public func processCallback(_ list: [TopProcess]) { DispatchQueue.main.async(execute: { - if (self.window?.isVisible ?? false) { + if (self.window?.isVisible ?? false) || !self.processesInitialized { for i in 0..