CPU und GPU zwischen Auslastung und Temperatur umschaltbar
Die M-Serie-Konvention gilt auch auf dem M5: Tp* sind die CPU-Kerne, Tg* die GPU-Cluster. Gezeigt wird der wärmste Punkt, nicht der Durchschnitt — gedrosselt wird nach dem heißesten Kern. Gemessen: CPU 76 °C, GPU 70 °C. Die Fühlerliste wird zur Laufzeit gesucht statt fest verdrahtet: welche es gibt, hängt am Modell. Auf diesem Mac sind es 285 Temperaturfühler. Zwei Leistungsprobleme, gefunden bevor sie jemandem auffallen konnten: Die Suche geht alle 3486 Keys durch und braucht knapp eine Sekunde. Auf dem Hauptthread wäre das eine spürbare Startverzögerung — sie läuft jetzt im Hintergrund, bis dahin zeigt die Oberfläche "—" statt einer erfundenen Zahl. Fünfzig Fühler zu lesen kostete 34 ms je Abtastung, bei 1 Hz also 3,4 % CPU. Zwei Maßnahmen: Typ und Größe je Key werden zwischengespeichert (sie stehen in der Firmware fest, sie bei jedem Lesen zu erfragen verdoppelte die IOKit-Aufrufe), und Temperaturen werden höchstens alle zwei Sekunden neu gelesen — sie bewegen sich in einer Sekunde ohnehin kaum. Ergebnis: 6 ms Grundlast, 18 ms alle zwei Sekunden, im Mittel gut 1 %. Dabei noch eine Eigenheit gefunden: die Byte-Reihenfolge im SMC ist nicht durchgängig. Messwerte wie B0AV kommen little-endian aus dem Akku-Baustein, die Metadaten des SMC selbst dagegen big-endian. #KEY little-endian gelesen ergibt 2 651 652 096 statt 3486 — die Fühlersuche brach damit still ab und alle Temperaturen blieben leer. Lüfterdrehzahlen stehen jetzt im Sensor-Popover, mit Balken zwischen Minimum und Maximum der Firmware: "2321 U/min" allein sagt niemandem, ob das viel ist. Die Steuerung selbst kommt mit Phase 6 — Schreibzugriff auf den SMC verlangt root — und der Popover sagt das auch. 145 Tests grün.
This commit is contained in:
@@ -4,6 +4,7 @@ import OnyxNotch
|
||||
import OnyxWidgetKit
|
||||
import CalendarProvider
|
||||
import OnyxMenuBar
|
||||
import MetricsProvider
|
||||
import WeatherProvider
|
||||
|
||||
enum SettingsTab: Hashable {
|
||||
@@ -14,6 +15,7 @@ struct SettingsView: View {
|
||||
@Bindable var model: AppModel
|
||||
let calendarModel: CalendarModel
|
||||
let weatherModel: WeatherModel
|
||||
let metricsModel: MetricsModel?
|
||||
@Binding var selectedTab: SettingsTab
|
||||
|
||||
var body: some View {
|
||||
@@ -24,7 +26,7 @@ struct SettingsView: View {
|
||||
DisplaySettings(model: model)
|
||||
.tabItem { Label("settings.tab.display", systemImage: "macbook") }
|
||||
.tag(SettingsTab.display)
|
||||
MenuBarSettings(model: model)
|
||||
MenuBarSettings(model: model, metricsModel: metricsModel)
|
||||
.tabItem { Label("settings.tab.menubar", systemImage: "menubar.rectangle") }
|
||||
.tag(SettingsTab.menubar)
|
||||
PermissionSettings(calendarModel: calendarModel, weatherModel: weatherModel)
|
||||
@@ -160,6 +162,7 @@ private struct PermissionRow: View {
|
||||
/// blockiert die Kamera zusätzlich die Mitte.
|
||||
private struct MenuBarSettings: View {
|
||||
@Bindable var model: AppModel
|
||||
let metricsModel: MetricsModel?
|
||||
|
||||
private static let modules: [(id: String, label: LocalizedStringKey, symbol: String)] = [
|
||||
("metric.cpu", "metric.cpu", "cpu"),
|
||||
@@ -180,7 +183,8 @@ private struct MenuBarSettings: View {
|
||||
List {
|
||||
ForEach(Self.modules, id: \.id) { module in
|
||||
ModuleRow(model: model, id: module.id,
|
||||
label: module.label, symbol: module.symbol)
|
||||
label: module.label, symbol: module.symbol,
|
||||
metricsModel: metricsModel)
|
||||
}
|
||||
}
|
||||
.listStyle(.inset)
|
||||
@@ -197,6 +201,13 @@ private struct ModuleRow: View {
|
||||
let id: String
|
||||
let label: LocalizedStringKey
|
||||
let symbol: String
|
||||
let metricsModel: MetricsModel?
|
||||
|
||||
/// Zu welcher Hardwaregröße dieses Modul gehört — `nil` beim Netzwerk.
|
||||
private var metricKind: MetricKind? {
|
||||
guard id.hasPrefix("metric.") else { return nil }
|
||||
return MetricKind(rawValue: String(id.dropFirst("metric.".count)))
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
let settings = model.menuBarSettings(for: id)
|
||||
@@ -214,6 +225,21 @@ private struct ModuleRow: View {
|
||||
|
||||
Spacer()
|
||||
|
||||
// Bei CPU und GPU zusätzlich wählbar, was gezeigt wird. Beide Werte
|
||||
// stammen aus derselben Messung — ein zweites Element in der
|
||||
// ohnehin knappen Menüleiste wäre Verschwendung.
|
||||
if let metric = metricKind, metric.supportsDisplayModes, let metricsModel {
|
||||
Picker("", selection: Binding(
|
||||
get: { metricsModel.displayMode(for: metric) },
|
||||
set: { metricsModel.setDisplayMode($0, for: metric) })) {
|
||||
Text("metric.mode.usage").tag(MetricDisplayMode.usage)
|
||||
Text("metric.mode.temperature").tag(MetricDisplayMode.temperature)
|
||||
}
|
||||
.labelsHidden()
|
||||
.frame(width: 110)
|
||||
.disabled(!settings.isEnabled)
|
||||
}
|
||||
|
||||
Picker("", selection: Binding(
|
||||
get: { settings.presentation },
|
||||
set: { presentation in
|
||||
|
||||
Reference in New Issue
Block a user