feat: added macOS widget for GPU module

This commit is contained in:
Serhiy Mytrovtsiy
2024-07-17 19:34:51 +02:00
parent 71207f29fc
commit 80d7789ef7
8 changed files with 179 additions and 14 deletions

View File

@@ -77,13 +77,10 @@ open class Module {
config.name
}
public var combinedPosition: Int {
get {
Store.shared.int(key: "\(self.name)_position", defaultValue: 0)
}
set {
Store.shared.set(key: "\(self.name)_position", value: newValue)
}
get { Store.shared.int(key: "\(self.name)_position", defaultValue: 0) }
set { Store.shared.set(key: "\(self.name)_position", value: newValue) }
}
public var userDefaults: UserDefaults? = UserDefaults(suiteName: "eu.exelban.Stats.widgets")
private var settingsView: Settings_v? = nil
private var popup: PopupWindow? = nil

View File

@@ -83,8 +83,6 @@ public class CPU: Module {
return color.additional as! NSColor
}
private var userDefaults: UserDefaults? = UserDefaults(suiteName: "eu.exelban.Stats.widgets")
public init() {
self.settingsView = Settings(.CPU)
self.popupView = Popup(.CPU)

View File

@@ -191,7 +191,6 @@ public class Disk: Module {
private var processReader: ProcessReader?
private var selectedDisk: String = ""
private var userDefaults: UserDefaults? = UserDefaults(suiteName: "eu.exelban.Stats.widgets")
public init() {
super.init(

View File

@@ -11,6 +11,7 @@
import Cocoa
import Kit
import WidgetKit
public typealias GPU_type = String
public enum GPU_types: GPU_type {
@@ -40,13 +41,16 @@ public struct GPU_Info: Codable {
public var renderUtilization: Double? = nil
public var tilerUtilization: Double? = nil
init(id: String, type: GPU_type, IOClass: String, vendor: String? = nil, model: String, cores: Int?) {
init(id: String, type: GPU_type, IOClass: String, vendor: String? = nil, model: String, cores: Int?, utilization: Double? = nil, render: Double? = nil, tiler: Double? = nil) {
self.id = id
self.type = type
self.IOClass = IOClass
self.vendor = vendor
self.model = model
self.cores = cores
self.utilization = utilization
self.renderUtilization = render
self.tilerUtilization = tiler
}
}
@@ -143,5 +147,11 @@ public class GPU: Module {
default: break
}
}
if #available(macOS 11.0, *) {
guard let blobData = try? JSONEncoder().encode(selectedGPU) else { return }
self.userDefaults?.set(blobData, forKey: "GPU@InfoReader")
WidgetCenter.shared.reloadTimelines(ofKind: GPU_entry.kind)
}
}
}

View File

@@ -2,11 +2,108 @@
// widget.swift
// GPU
//
// Created by Serhiy Mytrovtsiy on 16/07/2024
// Created by Serhiy Mytrovtsiy on 17/07/2024
// Using Swift 5.0
// Running on macOS 14.5
//
// Copyright © 2024 Serhiy Mytrovtsiy. All rights reserved.
//
import Foundation
import SwiftUI
import WidgetKit
import Charts
import Kit
public struct GPU_entry: TimelineEntry {
public static let kind = "GPUWidget"
public static var snapshot: GPU_entry = GPU_entry(value: GPU_Info(id: "", type: "", IOClass: "", model: "", cores: nil, utilization: 0.11, render: 0.11, tiler: 0.11))
public var date: Date {
Calendar.current.date(byAdding: .second, value: 5, to: Date())!
}
public var value: GPU_Info? = nil
}
@available(macOS 11.0, *)
public struct Provider: TimelineProvider {
public typealias Entry = GPU_entry
private let userDefaults: UserDefaults? = UserDefaults(suiteName: "eu.exelban.Stats.widgets")
public func placeholder(in context: Context) -> GPU_entry {
GPU_entry()
}
public func getSnapshot(in context: Context, completion: @escaping (GPU_entry) -> Void) {
completion(GPU_entry.snapshot)
}
public func getTimeline(in context: Context, completion: @escaping (Timeline<GPU_entry>) -> Void) {
var entry = GPU_entry()
if let raw = userDefaults?.data(forKey: "GPU@InfoReader"), let load = try? JSONDecoder().decode(GPU_Info.self, from: raw) {
entry.value = load
}
let entries: [GPU_entry] = [entry]
completion(Timeline(entries: entries, policy: .atEnd))
}
}
@available(macOS 14.0, *)
public struct GPUWidget: Widget {
var usedColor: Color = Color(nsColor: NSColor.systemBlue)
var freeColor: Color = Color(nsColor: NSColor.lightGray)
public init() {}
public var body: some WidgetConfiguration {
StaticConfiguration(kind: GPU_entry.kind, provider: Provider()) { entry in
VStack(spacing: 10) {
if let value = entry.value {
HStack {
Chart {
SectorMark(angle: .value(localizedString("Used"), value.utilization ?? 0), innerRadius: .ratio(0.8)).foregroundStyle(self.usedColor)
SectorMark(angle: .value(localizedString("Free"), 1-(value.utilization ?? 0)), innerRadius: .ratio(0.8)).foregroundStyle(self.freeColor)
}
.frame(maxWidth: .infinity, maxHeight: 84)
.chartLegend(.hidden)
.chartBackground { chartProxy in
GeometryReader { geometry in
if let anchor = chartProxy.plotFrame {
let frame = geometry[anchor]
Text("\(Int((value.utilization ?? 0)*100))%")
.font(.system(size: 16, weight: .regular))
.position(x: frame.midX, y: frame.midY)
}
}
}
}
VStack(spacing: 3) {
HStack {
Text(localizedString("Usage")).font(.system(size: 12, weight: .regular)).foregroundColor(.secondary)
Spacer()
Text("\(Int((value.utilization ?? 0)*100))%")
}
HStack {
Text(localizedString("Render")).font(.system(size: 12, weight: .regular)).foregroundColor(.secondary)
Spacer()
Text("\(Int((value.renderUtilization ?? 0)*100))%")
}
HStack {
Text(localizedString("Tiler")).font(.system(size: 12, weight: .regular)).foregroundColor(.secondary)
Spacer()
Text("\(Int((value.tilerUtilization ?? 0)*100))%")
}
}
} else {
Text("No data")
}
}
.containerBackground(for: .widget) {
Color.clear
}
}
.configurationDisplayName("GPU widget")
.description("Displays GPU stats")
.supportedFamilies([.systemSmall])
}
}

View File

@@ -82,8 +82,6 @@ public class RAM: Module {
return color.additional as! NSColor
}
private var userDefaults: UserDefaults? = UserDefaults(suiteName: "eu.exelban.Stats.widgets")
public init() {
self.settingsView = Settings(.RAM)
self.popupView = Popup(.RAM)

View File

@@ -10,6 +10,11 @@
5C044F7A2B3DE6F3005F6951 /* portal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C044F792B3DE6F3005F6951 /* portal.swift */; };
5C0A2A8A292A5B4D009B4C1F /* SMJobBlessUtil.py in Resources */ = {isa = PBXBuildFile; fileRef = 5C0A2A89292A5B4D009B4C1F /* SMJobBlessUtil.py */; };
5C0A9CA22C467AA300EE6A89 /* widget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C0A9CA12C467AA300EE6A89 /* widget.swift */; };
5C0A9CA42C467F7A00EE6A89 /* widget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C0A9CA32C467F7A00EE6A89 /* widget.swift */; };
5C0A9CA52C46838300EE6A89 /* CPU.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A97CECA2537331B00742D8F /* CPU.framework */; };
5C0A9CAA2C46838A00EE6A89 /* GPU.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A90E18924EAD2BB00471E9A /* GPU.framework */; };
5C0A9CAF2C46838F00EE6A89 /* RAM.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A81C7562449A41400825D92 /* RAM.framework */; };
5C0A9CB42C46839500EE6A89 /* Disk.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9AF9EE0224648751005D2270 /* Disk.framework */; };
5C21D80B296C7B81005BA16D /* CombinedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C21D80A296C7B81005BA16D /* CombinedView.swift */; };
5C2229A329CCB3C400F00E69 /* Clock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C22299D29CCB3C400F00E69 /* Clock.framework */; };
5C2229A429CCB3C400F00E69 /* Clock.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 5C22299D29CCB3C400F00E69 /* Clock.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@@ -191,6 +196,34 @@
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
5C0A9CA72C46838300EE6A89 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 9A1410ED229E721100D29793 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 9A97CEC92537331B00742D8F;
remoteInfo = CPU;
};
5C0A9CAC2C46838A00EE6A89 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 9A1410ED229E721100D29793 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 9A90E18824EAD2BB00471E9A;
remoteInfo = GPU;
};
5C0A9CB12C46838F00EE6A89 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 9A1410ED229E721100D29793 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 9A81C7552449A41400825D92;
remoteInfo = RAM;
};
5C0A9CB62C46839500EE6A89 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 9A1410ED229E721100D29793 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 9AF9EE0124648751005D2270;
remoteInfo = Disk;
};
5C2229A129CCB3C400F00E69 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 9A1410ED229E721100D29793 /* Project object */;
@@ -437,6 +470,7 @@
5C044F792B3DE6F3005F6951 /* portal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = portal.swift; sourceTree = "<group>"; };
5C0A2A89292A5B4D009B4C1F /* SMJobBlessUtil.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; path = SMJobBlessUtil.py; sourceTree = "<group>"; };
5C0A9CA12C467AA300EE6A89 /* widget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = widget.swift; sourceTree = "<group>"; };
5C0A9CA32C467F7A00EE6A89 /* widget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = widget.swift; sourceTree = "<group>"; };
5C0E550A2B5D545A00FFF1FB /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = "<group>"; };
5C21D80A296C7B81005BA16D /* CombinedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CombinedView.swift; sourceTree = "<group>"; };
5C22299D29CCB3C400F00E69 /* Clock.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Clock.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -661,7 +695,11 @@
buildActionMask = 2147483647;
files = (
5CE7E78E2C318512006BC92C /* SwiftUI.framework in Frameworks */,
5C0A9CAA2C46838A00EE6A89 /* GPU.framework in Frameworks */,
5CE7E78C2C318512006BC92C /* WidgetKit.framework in Frameworks */,
5C0A9CB42C46839500EE6A89 /* Disk.framework in Frameworks */,
5C0A9CA52C46838300EE6A89 /* CPU.framework in Frameworks */,
5C0A9CAF2C46838F00EE6A89 /* RAM.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1070,6 +1108,7 @@
5C23BC0929A0EDA300DBA990 /* portal.swift */,
9A53EBF824EAFA5200648841 /* settings.swift */,
5CF221142B1F4792006C583F /* notifications.swift */,
5C0A9CA32C467F7A00EE6A89 /* widget.swift */,
9A90E18C24EAD2BB00471E9A /* Info.plist */,
9A90E19724EAD3B000471E9A /* config.plist */,
);
@@ -1329,6 +1368,10 @@
buildRules = (
);
dependencies = (
5C0A9CA82C46838300EE6A89 /* PBXTargetDependency */,
5C0A9CAD2C46838A00EE6A89 /* PBXTargetDependency */,
5C0A9CB22C46838F00EE6A89 /* PBXTargetDependency */,
5C0A9CB72C46839500EE6A89 /* PBXTargetDependency */,
);
name = WidgetsExtension;
productName = WidgetsExtension;
@@ -2061,6 +2104,7 @@
9A46C06B266D8602001A1117 /* smc.swift in Sources */,
9A90E1A324EAD66600471E9A /* reader.swift in Sources */,
9A90E19624EAD35F00471E9A /* main.swift in Sources */,
5C0A9CA42C467F7A00EE6A89 /* widget.swift in Sources */,
5C23BC0A29A0EDA300DBA990 /* portal.swift in Sources */,
9A53EBFB24EB041E00648841 /* popup.swift in Sources */,
9A53EBF924EAFA5200648841 /* settings.swift in Sources */,
@@ -2145,6 +2189,26 @@
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
5C0A9CA82C46838300EE6A89 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 9A97CEC92537331B00742D8F /* CPU */;
targetProxy = 5C0A9CA72C46838300EE6A89 /* PBXContainerItemProxy */;
};
5C0A9CAD2C46838A00EE6A89 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 9A90E18824EAD2BB00471E9A /* GPU */;
targetProxy = 5C0A9CAC2C46838A00EE6A89 /* PBXContainerItemProxy */;
};
5C0A9CB22C46838F00EE6A89 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 9A81C7552449A41400825D92 /* RAM */;
targetProxy = 5C0A9CB12C46838F00EE6A89 /* PBXContainerItemProxy */;
};
5C0A9CB72C46839500EE6A89 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 9AF9EE0124648751005D2270 /* Disk */;
targetProxy = 5C0A9CB62C46839500EE6A89 /* PBXContainerItemProxy */;
};
5C2229A229CCB3C400F00E69 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 5C22299C29CCB3C400F00E69 /* Clock */;

View File

@@ -12,6 +12,7 @@
import SwiftUI
import CPU
import GPU
import RAM
import Disk
@@ -19,6 +20,7 @@ import Disk
struct WidgetsBundle: WidgetBundle {
var body: some Widget {
CPUWidget()
GPUWidget()
RAMWidget()
DiskWidget()
}