mirror of
https://github.com/Toxblh/MTMR.git
synced 2026-01-09 16:48:38 +00:00
Add new "cpu" widget (#415)
This commit is contained in:
parent
352bf4887c
commit
d199bbd852
@ -21,6 +21,8 @@
|
||||
36FEF872235A1CFC00A0ABCE /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36FEF871235A1CFC00A0ABCE /* AppSettings.swift */; };
|
||||
4CC9FEDC22FDEA65001512EB /* AMR_ANSIEscapeHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CC9FEDB22FDEA65001512EB /* AMR_ANSIEscapeHelper.m */; };
|
||||
4CDC6E5022FCA93F0069ADD4 /* ShellScriptTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CDC6E4F22FCA93F0069ADD4 /* ShellScriptTouchBarItem.swift */; };
|
||||
4CF222EB26CC00470025BDA7 /* CPUBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF222EA26CC00470025BDA7 /* CPUBarItem.swift */; };
|
||||
4CF222ED26CC43D50025BDA7 /* CPU.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF222EC26CC43D40025BDA7 /* CPU.swift */; };
|
||||
4CFF5E5C22E623DD00BFB1EE /* YandexWeatherBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF5E5B22E623DD00BFB1EE /* YandexWeatherBarItem.swift */; };
|
||||
5DC6CA02241F92CB005CD5E8 /* Music.nowPlaying.scpt in Resources */ = {isa = PBXBuildFile; fileRef = 5DC6CA00241F92CB005CD5E8 /* Music.nowPlaying.scpt */; };
|
||||
5DC6CA03241F92CB005CD5E8 /* Music.next.scpt in Resources */ = {isa = PBXBuildFile; fileRef = 5DC6CA01241F92CB005CD5E8 /* Music.next.scpt */; };
|
||||
@ -104,6 +106,8 @@
|
||||
4CC9FEDA22FDEA65001512EB /* AMR_ANSIEscapeHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AMR_ANSIEscapeHelper.h; sourceTree = "<group>"; };
|
||||
4CC9FEDB22FDEA65001512EB /* AMR_ANSIEscapeHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AMR_ANSIEscapeHelper.m; sourceTree = "<group>"; };
|
||||
4CDC6E4F22FCA93F0069ADD4 /* ShellScriptTouchBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShellScriptTouchBarItem.swift; sourceTree = "<group>"; };
|
||||
4CF222EA26CC00470025BDA7 /* CPUBarItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CPUBarItem.swift; sourceTree = "<group>"; };
|
||||
4CF222EC26CC43D40025BDA7 /* CPU.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CPU.swift; sourceTree = "<group>"; };
|
||||
4CFF5E5B22E623DD00BFB1EE /* YandexWeatherBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YandexWeatherBarItem.swift; sourceTree = "<group>"; };
|
||||
5DC6CA00241F92CB005CD5E8 /* Music.nowPlaying.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Music.nowPlaying.scpt; sourceTree = "<group>"; };
|
||||
5DC6CA01241F92CB005CD5E8 /* Music.next.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Music.next.scpt; sourceTree = "<group>"; };
|
||||
@ -239,6 +243,7 @@
|
||||
B0A7E9A9205D6AA400EEF070 /* KeyPress.swift */,
|
||||
36FEF871235A1CFC00A0ABCE /* AppSettings.swift */,
|
||||
B059D623205E04F3006E6B86 /* CustomButtonTouchBarItem.swift */,
|
||||
4CF222EC26CC43D40025BDA7 /* CPU.swift */,
|
||||
36C2ECD8207B74B4003CDA33 /* AppleScriptTouchBarItem.swift */,
|
||||
BAF5AB5824317CAF00B04904 /* SwipeItem.swift */,
|
||||
4CDC6E4F22FCA93F0069ADD4 /* ShellScriptTouchBarItem.swift */,
|
||||
@ -310,6 +315,7 @@
|
||||
6042B6A62083E03A00C525C8 /* AppScrubberTouchBarItem.swift */,
|
||||
B04B7BB62087398C00C835D0 /* BatteryBarItem.swift */,
|
||||
6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */,
|
||||
4CF222EA26CC00470025BDA7 /* CPUBarItem.swift */,
|
||||
607EEA4C2087A8DA009DA5F0 /* CurrencyBarItem.swift */,
|
||||
B081732B213739FE005D4908 /* DnDBarItem.swift */,
|
||||
60669B4220AD8FA80074E817 /* GroupBarItem.swift */,
|
||||
@ -478,12 +484,14 @@
|
||||
B0846A752220C968000288A7 /* NetworkBarItem.swift in Sources */,
|
||||
B0F8771A207AC1EA00D6E430 /* TouchBarSupport.m in Sources */,
|
||||
B08126F1217BE19000A98970 /* WidgetProtocol.swift in Sources */,
|
||||
4CF222EB26CC00470025BDA7 /* CPUBarItem.swift in Sources */,
|
||||
B0008E552080286C003AD4DD /* SupportHelpers.swift in Sources */,
|
||||
6042B6A72083E03A00C525C8 /* AppScrubberTouchBarItem.swift in Sources */,
|
||||
BAF5AB5924317CAF00B04904 /* SwipeItem.swift in Sources */,
|
||||
B082B253205C7D8000BC04DC /* AppDelegate.swift in Sources */,
|
||||
B0F54A7A2295AC7D00B4C509 /* DarkModeBarItem.swift in Sources */,
|
||||
B08126EF217BD0B900A98970 /* PomodoroBarItem.swift in Sources */,
|
||||
4CF222ED26CC43D50025BDA7 /* CPU.swift in Sources */,
|
||||
B081732C213739FE005D4908 /* DnDBarItem.swift in Sources */,
|
||||
60C44AFD20A373A100C0EC91 /* MusicBarItem.swift in Sources */,
|
||||
B059D624205E04F3006E6B86 /* CustomButtonTouchBarItem.swift in Sources */,
|
||||
|
||||
21
MTMR/Assets.xcassets/cpu.imageset/Contents.json
vendored
Normal file
21
MTMR/Assets.xcassets/cpu.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "cpu.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
BIN
MTMR/Assets.xcassets/cpu.imageset/cpu.png
vendored
Normal file
BIN
MTMR/Assets.xcassets/cpu.imageset/cpu.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
191
MTMR/CPU.swift
Normal file
191
MTMR/CPU.swift
Normal file
@ -0,0 +1,191 @@
|
||||
//
|
||||
// CPU.swift
|
||||
// Pods
|
||||
//
|
||||
// Created by zixun on 2016/12/5.
|
||||
// https://github.com/zixun/SystemEye
|
||||
// MIT License
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
private let HOST_CPU_LOAD_INFO_COUNT : mach_msg_type_number_t =
|
||||
UInt32(MemoryLayout<host_cpu_load_info_data_t>.size / MemoryLayout<integer_t>.size)
|
||||
|
||||
/// CPU Class
|
||||
public class CPU: NSObject {
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// MARK: OPEN PROPERTY
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// /// Number of physical cores on this machine.
|
||||
// public static var physicalCores: Int {
|
||||
// get {
|
||||
// return Int(System.hostBasicInfo.physical_cpu)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /// Number of logical cores on this machine. Will be equal to physicalCores
|
||||
// /// unless it has hyper-threading, in which case it will be double.
|
||||
// public static var logicalCores: Int {
|
||||
// get {
|
||||
// return Int(System.hostBasicInfo.logical_cpu)
|
||||
// }
|
||||
// }
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// MARK: OPEN FUNCTIONS
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/// Get CPU usage of hole system (system, user, idle, nice). Determined by the delta between
|
||||
/// the current and last call.
|
||||
public static func systemUsage() -> (system: Double,
|
||||
user: Double,
|
||||
idle: Double,
|
||||
nice: Double) {
|
||||
let load = self.hostCPULoadInfo
|
||||
|
||||
let userDiff = Double(load.cpu_ticks.0 - loadPrevious.cpu_ticks.0)
|
||||
let sysDiff = Double(load.cpu_ticks.1 - loadPrevious.cpu_ticks.1)
|
||||
let idleDiff = Double(load.cpu_ticks.2 - loadPrevious.cpu_ticks.2)
|
||||
let niceDiff = Double(load.cpu_ticks.3 - loadPrevious.cpu_ticks.3)
|
||||
|
||||
let totalTicks = sysDiff + userDiff + niceDiff + idleDiff
|
||||
|
||||
let sys = sysDiff / totalTicks * 100.0
|
||||
let user = userDiff / totalTicks * 100.0
|
||||
let idle = idleDiff / totalTicks * 100.0
|
||||
let nice = niceDiff / totalTicks * 100.0
|
||||
|
||||
loadPrevious = load
|
||||
|
||||
return (sys, user, idle, nice)
|
||||
}
|
||||
|
||||
|
||||
/// Get CPU usage of application,get from all thread
|
||||
open class func applicationUsage() -> Double {
|
||||
let threads = self.threadBasicInfos()
|
||||
var result : Double = 0.0
|
||||
threads.forEach { (thread:thread_basic_info) in
|
||||
if self.flag(thread) {
|
||||
result += Double.init(thread.cpu_usage) / Double.init(TH_USAGE_SCALE);
|
||||
}
|
||||
}
|
||||
return result * 100
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// MARK: PRIVATE PROPERTY
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/// previous load of cpu
|
||||
private static var loadPrevious = host_cpu_load_info()
|
||||
|
||||
static var hostCPULoadInfo: host_cpu_load_info {
|
||||
get {
|
||||
var size = HOST_CPU_LOAD_INFO_COUNT
|
||||
var hostInfo = host_cpu_load_info()
|
||||
let result = withUnsafeMutablePointer(to: &hostInfo) {
|
||||
$0.withMemoryRebound(to: integer_t.self, capacity: Int(size)) {
|
||||
host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, $0, &size)
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
if result != KERN_SUCCESS {
|
||||
fatalError("ERROR - \(#file):\(#function) - kern_result_t = "
|
||||
+ "\(result)")
|
||||
}
|
||||
#endif
|
||||
|
||||
return hostInfo
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// MARK: PRIVATE FUNCTION
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
private class func flag(_ thread:thread_basic_info) -> Bool {
|
||||
let foo = thread.flags & TH_FLAGS_IDLE
|
||||
let number = NSNumber.init(value: foo)
|
||||
return !Bool.init(truncating: number)
|
||||
}
|
||||
|
||||
private class func threadActPointers() -> [thread_act_t] {
|
||||
var threads_act = [thread_act_t]()
|
||||
|
||||
var threads_array: thread_act_array_t? = nil
|
||||
var count = mach_msg_type_number_t()
|
||||
|
||||
let result = task_threads(mach_task_self_, &(threads_array), &count)
|
||||
|
||||
guard result == KERN_SUCCESS else {
|
||||
return threads_act
|
||||
}
|
||||
|
||||
guard let array = threads_array else {
|
||||
return threads_act
|
||||
}
|
||||
|
||||
for i in 0..<count {
|
||||
threads_act.append(array[Int(i)])
|
||||
}
|
||||
|
||||
let krsize = count * UInt32.init(MemoryLayout<thread_t>.size)
|
||||
_ = vm_deallocate(mach_task_self_, vm_address_t(array.pointee), vm_size_t(krsize));
|
||||
return threads_act
|
||||
}
|
||||
|
||||
private class func threadBasicInfos() -> [thread_basic_info] {
|
||||
var result = [thread_basic_info]()
|
||||
|
||||
let thinfo : thread_info_t = thread_info_t.allocate(capacity: Int(THREAD_INFO_MAX))
|
||||
let thread_info_count = UnsafeMutablePointer<mach_msg_type_number_t>.allocate(capacity: 128)
|
||||
var basic_info_th: thread_basic_info_t? = nil
|
||||
|
||||
for act_t in self.threadActPointers() {
|
||||
thread_info_count.pointee = UInt32(THREAD_INFO_MAX);
|
||||
let kr = thread_info(act_t ,thread_flavor_t(THREAD_BASIC_INFO),thinfo, thread_info_count);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
return [thread_basic_info]();
|
||||
}
|
||||
basic_info_th = withUnsafePointer(to: &thinfo.pointee, { (ptr) -> thread_basic_info_t in
|
||||
let int8Ptr = unsafeBitCast(ptr, to: thread_basic_info_t.self)
|
||||
return int8Ptr
|
||||
})
|
||||
if basic_info_th != nil {
|
||||
result.append(basic_info_th!.pointee)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
//TODO: this function is used for get cpu usage of all thread,and this is in developing
|
||||
private class func threadIdentifierInfos() -> [thread_identifier_info] {
|
||||
var result = [thread_identifier_info]()
|
||||
let thinfo : thread_info_t = thread_info_t.allocate(capacity: Int(THREAD_INFO_MAX))
|
||||
let thread_info_count = UnsafeMutablePointer<mach_msg_type_number_t>.allocate(capacity: 128)
|
||||
var identifier_info_th: thread_identifier_info_t? = nil
|
||||
|
||||
for act_t in self.threadActPointers() {
|
||||
thread_info_count.pointee = UInt32(THREAD_INFO_MAX);
|
||||
let kr = thread_info(act_t ,thread_flavor_t(THREAD_IDENTIFIER_INFO),thinfo, thread_info_count);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
return [thread_identifier_info]();
|
||||
}
|
||||
identifier_info_th = withUnsafePointer(to: &thinfo.pointee, { (ptr) -> thread_identifier_info_t in
|
||||
let int8Ptr = unsafeBitCast(ptr, to: thread_identifier_info_t.self)
|
||||
return int8Ptr
|
||||
})
|
||||
if identifier_info_th != nil {
|
||||
result.append(identifier_info_th!.pointee)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
@ -261,6 +261,7 @@ enum ItemType: Decodable {
|
||||
case shellScriptTitledButton(source: SourceProtocol, refreshInterval: Double)
|
||||
case timeButton(formatTemplate: String, timeZone: String?, locale: String?)
|
||||
case battery
|
||||
case cpu(refreshInterval: Double)
|
||||
case dock(autoResize: Bool, filter: String?)
|
||||
case volume
|
||||
case brightness(refreshInterval: Double)
|
||||
@ -317,6 +318,7 @@ enum ItemType: Decodable {
|
||||
case shellScriptTitledButton
|
||||
case timeButton
|
||||
case battery
|
||||
case cpu
|
||||
case dock
|
||||
case volume
|
||||
case brightness
|
||||
@ -363,6 +365,10 @@ enum ItemType: Decodable {
|
||||
case .battery:
|
||||
self = .battery
|
||||
|
||||
case .cpu:
|
||||
let refreshInterval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 5.0
|
||||
self = .cpu(refreshInterval: refreshInterval)
|
||||
|
||||
case .dock:
|
||||
let autoResize = try container.decodeIfPresent(Bool.self, forKey: .autoResize) ?? false
|
||||
let filterRegexString = try container.decodeIfPresent(String.self, forKey: .filter)
|
||||
|
||||
@ -29,6 +29,8 @@ extension ItemType {
|
||||
return "com.toxblh.mtmr.timeButton."
|
||||
case .battery:
|
||||
return "com.toxblh.mtmr.battery."
|
||||
case .cpu(refreshInterval: _):
|
||||
return "com.toxblh.mtmr.cpu."
|
||||
case .dock(autoResize: _, filter: _):
|
||||
return "com.toxblh.mtmr.dock"
|
||||
case .volume:
|
||||
@ -272,6 +274,8 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
|
||||
barItem = TimeTouchBarItem(identifier: identifier, formatTemplate: template, timeZone: timeZone, locale: locale)
|
||||
case .battery:
|
||||
barItem = BatteryBarItem(identifier: identifier)
|
||||
case let .cpu(refreshInterval: refreshInterval):
|
||||
barItem = CPUBarItem(identifier: identifier, refreshInterval: refreshInterval)
|
||||
case let .dock(autoResize: autoResize, filter: regexString):
|
||||
if let regexString = regexString {
|
||||
guard let regex = try? NSRegularExpression(pattern: regexString, options: []) else {
|
||||
|
||||
82
MTMR/Widgets/CPUBarItem.swift
Normal file
82
MTMR/Widgets/CPUBarItem.swift
Normal file
@ -0,0 +1,82 @@
|
||||
//
|
||||
// CPUBarItem.swift
|
||||
// MTMR
|
||||
//
|
||||
// Created by bobrosoft on 17/08/2021.
|
||||
// Copyright © 2018 Anton Palgunov. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class CPUBarItem: CustomButtonTouchBarItem {
|
||||
private let refreshInterval: TimeInterval
|
||||
private var refreshQueue: DispatchQueue? = DispatchQueue(label: "mtmr.cpu")
|
||||
private let defaultSingleTapScript: NSAppleScript! = "activate application \"Activity Monitor\"\rtell application \"System Events\"\r\ttell process \"Activity Monitor\"\r\t\ttell radio button \"CPU\" of radio group 1 of group 2 of toolbar 1 of window 1 to perform action \"AXPress\"\r\tend tell\rend tell".appleScript
|
||||
|
||||
init(identifier: NSTouchBarItem.Identifier, refreshInterval: TimeInterval) {
|
||||
self.refreshInterval = refreshInterval
|
||||
super.init(identifier: identifier, title: "⏳")
|
||||
|
||||
// Set default image
|
||||
if self.image == nil {
|
||||
self.image = #imageLiteral(resourceName: "cpu").resize(maxSize: NSSize(width: 24, height: 24));
|
||||
}
|
||||
|
||||
// Set default action
|
||||
if actions.filter({ $0.trigger == .singleTap }).isEmpty {
|
||||
actions.append(ItemAction(
|
||||
trigger: .singleTap,
|
||||
defaultTapAction
|
||||
))
|
||||
}
|
||||
|
||||
refreshAndSchedule()
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func refreshAndSchedule() {
|
||||
DispatchQueue.main.async {
|
||||
// Get CPU load
|
||||
let usage = 100 - CPU.systemUsage().idle
|
||||
guard usage.isFinite else {
|
||||
return
|
||||
}
|
||||
|
||||
// Choose color based on CPU load
|
||||
var color: NSColor? = nil
|
||||
var bgColor: NSColor? = nil
|
||||
if usage > 70 {
|
||||
color = .black
|
||||
bgColor = .yellow
|
||||
} else if usage > 30 {
|
||||
color = .yellow
|
||||
}
|
||||
|
||||
// Update layout
|
||||
let attrTitle = NSMutableAttributedString.init(attributedString: String(format: "%.1f%%", usage).defaultTouchbarAttributedString)
|
||||
if let color = color {
|
||||
attrTitle.addAttributes([.foregroundColor: color], range: NSRange(location: 0, length: attrTitle.length))
|
||||
}
|
||||
self.attributedTitle = attrTitle
|
||||
self.backgroundColor = bgColor
|
||||
}
|
||||
|
||||
refreshQueue?.asyncAfter(deadline: .now() + refreshInterval) { [weak self] in
|
||||
self?.refreshAndSchedule()
|
||||
}
|
||||
}
|
||||
|
||||
func defaultTapAction() {
|
||||
refreshQueue?.async { [weak self] in
|
||||
self?.defaultSingleTapScript.executeAndReturnError(nil)
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
refreshQueue?.suspend()
|
||||
refreshQueue = nil
|
||||
}
|
||||
}
|
||||
16
README.md
16
README.md
@ -73,6 +73,7 @@ The pre-installed configuration contains less or more than you'll probably want,
|
||||
|
||||
- timeButton
|
||||
- battery
|
||||
- cpu
|
||||
- currency
|
||||
- weather
|
||||
- yandexWeather
|
||||
@ -189,7 +190,7 @@ Example:
|
||||
> Note: script may return also colors using escape sequences (read more here https://misc.flogisoft.com/bash/tip_colors_and_formatting)
|
||||
> Only "16 Colors" mode supported atm. If background color returned, button will pick it up as own background color.
|
||||
|
||||
Example of "CPU load" button which also changes color based on load value.
|
||||
Example of "CPU load" button which also changes color based on load value (Note: you can use native `cpu` plugin for that purpose which runs better):
|
||||
```js
|
||||
{
|
||||
"type": "shellScriptTitledButton",
|
||||
@ -246,6 +247,19 @@ To close a group, use the button:
|
||||
|
||||
## Native plugins
|
||||
|
||||
#### `cpu`
|
||||
|
||||
> Shows current CPU load in percents, changes color based on load value.
|
||||
> Has lower power consumption and more stable in comparison to shell-based solution.
|
||||
|
||||
```js
|
||||
{
|
||||
"type": "cpu",
|
||||
"refreshInterval": 3,
|
||||
"width": 80
|
||||
}
|
||||
```
|
||||
|
||||
#### `timeButton`
|
||||
|
||||
> Attention! Works not all: https://en.wikipedia.org/wiki/List_of_time_zone_abbreviations
|
||||
|
||||
Loading…
Reference in New Issue
Block a user