diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b59369c1..46527d4d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,8 +10,8 @@ jobs: Build: runs-on: macos-latest steps: - - uses: actions/checkout@v1 - - uses: actions/cache@v2 + - uses: actions/checkout@v4 + - uses: actions/cache@v4 with: path: .build key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }} @@ -22,7 +22,12 @@ jobs: - name: SwiftFormat # this is necessary since we'll format files in build phase run: swift run -c release swiftformat ../ --lint working-directory: ./BuildTools - - name: Switch Xcode version - run: sudo xcode-select -s /Applications/Xcode_12.4.app - name: Build - run: xcodebuild -scheme eul -project ./eul.xcodeproj -sdk macosx build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED="NO" CODE_SIGN_ENTITLEMENTS="" CODE_SIGNING_ALLOWED="NO" | xcpretty && exit ${PIPESTATUS[0]} + run: xcodebuild -scheme eul -project ./eul.xcodeproj build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED="NO" CODE_SIGN_ENTITLEMENTS="" CODE_SIGNING_ALLOWED="NO" | xcpretty && exit ${PIPESTATUS[0]} + - name: Upload Build Artifact + uses: actions/upload-artifact@v4 + if: success() + with: + name: eul-app + path: ~/Library/Developer/Xcode/DerivedData/eul-*/Build/Products/Debug/eul.app + retention-days: 7 diff --git a/Resource/ar.lproj/Localizable.strings b/Resource/ar.lproj/Localizable.strings index 74b4ed76..641842e7 100644 --- a/Resource/ar.lproj/Localizable.strings +++ b/Resource/ar.lproj/Localizable.strings @@ -37,6 +37,7 @@ "cpu.physical_cores" = "النوى المادية"; "cpu.logical_cores" = "النوى المنطقية"; "cpu.up_time" = "مدة التشغيل"; +"cpu.up_time_days" = "أيام التشغيل"; "cpu.thermal_level" = "المستوى الحراري"; "cpu.system" = "النظام"; "cpu.user" = "المستعمل"; diff --git a/Resource/cs.lproj/Localizable.strings b/Resource/cs.lproj/Localizable.strings index 4b6a9b74..21091d1c 100644 --- a/Resource/cs.lproj/Localizable.strings +++ b/Resource/cs.lproj/Localizable.strings @@ -37,6 +37,7 @@ "cpu.physical_cores" = "Fyzická jádra"; "cpu.logical_cores" = "Logická jádra"; "cpu.up_time" = "V provozu"; +"cpu.up_time_days" = "Dny provozu"; "cpu.thermal_level" = "Teplotní úroveň"; "cpu.system" = "Systém"; "cpu.user" = "Uživatel"; diff --git a/Resource/de.lproj/Localizable.strings b/Resource/de.lproj/Localizable.strings index 80853206..864ba71c 100644 --- a/Resource/de.lproj/Localizable.strings +++ b/Resource/de.lproj/Localizable.strings @@ -37,6 +37,7 @@ "cpu.physical_cores" = "Physische Kerne"; "cpu.logical_cores" = "Logische Kerne"; "cpu.up_time" = "Betriebszeit"; +"cpu.up_time_days" = "Betriebstage"; "cpu.thermal_level" = "Wärmegrad"; "cpu.system" = "System"; "cpu.user" = "Benutzer"; diff --git a/Resource/dk.lproj/Localizable.strings b/Resource/dk.lproj/Localizable.strings index 0dc652f7..74004942 100644 --- a/Resource/dk.lproj/Localizable.strings +++ b/Resource/dk.lproj/Localizable.strings @@ -38,6 +38,7 @@ "cpu.physical_cores" = "fysiske kerner"; "cpu.logical_cores" = "logiske kerner"; "cpu.up_time" = "oppetid"; +"cpu.up_time_days" = "Uptime Days"; "cpu.thermal_level" = "termisk niveau"; "cpu.system" = "system"; "cpu.user" = "bruger"; diff --git a/Resource/en.lproj/Localizable.strings b/Resource/en.lproj/Localizable.strings index d644f1fb..16a24bc1 100644 --- a/Resource/en.lproj/Localizable.strings +++ b/Resource/en.lproj/Localizable.strings @@ -37,6 +37,7 @@ "cpu.physical_cores" = "Physical Cores"; "cpu.logical_cores" = "Logical Cores"; "cpu.up_time" = "Up Time"; +"cpu.up_time_days" = "Uptime Days"; "cpu.thermal_level" = "Thermal Lv."; "cpu.system" = "System"; "cpu.user" = "User"; diff --git a/Resource/es.lproj/Localizable.strings b/Resource/es.lproj/Localizable.strings index b9ddbbf7..717bc5d7 100644 --- a/Resource/es.lproj/Localizable.strings +++ b/Resource/es.lproj/Localizable.strings @@ -37,6 +37,7 @@ "cpu.physical_cores" = "Núcleos Físicos"; "cpu.logical_cores" = "Núcleos Lógicos"; "cpu.up_time" = "Tiempo de Actividad"; +"cpu.up_time_days" = "Uptime Days"; "cpu.thermal_level" = "Nivel Térmico"; "cpu.system" = "Sistema"; "cpu.user" = "Usuario"; diff --git a/Resource/fr.lproj/Localizable.strings b/Resource/fr.lproj/Localizable.strings index 54aa1e98..61d85b1d 100644 --- a/Resource/fr.lproj/Localizable.strings +++ b/Resource/fr.lproj/Localizable.strings @@ -37,6 +37,7 @@ "cpu.physical_cores" = "Cœurs physiques"; "cpu.logical_cores" = "Cœurs logiques"; "cpu.up_time" = "Temps depuis le démarrage"; +"cpu.up_time_days" = "Uptime Days"; "cpu.thermal_level" = "Niveau thermique"; "cpu.system" = "Système"; "cpu.user" = "Utilisateur"; diff --git a/Resource/hu.lproj/Localizable.strings b/Resource/hu.lproj/Localizable.strings index 9bd83a02..96a0f697 100644 --- a/Resource/hu.lproj/Localizable.strings +++ b/Resource/hu.lproj/Localizable.strings @@ -37,6 +37,7 @@ "cpu.physical_cores" = "Fizikai magok"; "cpu.logical_cores" = "Logikai magok"; "cpu.up_time" = "üzemidő"; +"cpu.up_time_days" = "Uptime Days"; "cpu.thermal_level" = "Hőm. szint"; "cpu.system" = "Rendszer"; "cpu.user" = "Felhaszn."; diff --git a/Resource/it.lproj/Localizable.strings b/Resource/it.lproj/Localizable.strings index b060ebe0..ab960394 100644 --- a/Resource/it.lproj/Localizable.strings +++ b/Resource/it.lproj/Localizable.strings @@ -38,6 +38,7 @@ "cpu.physical_cores" = "Core Fisici"; "cpu.logical_cores" = "Core Logici"; "cpu.up_time" = "Up Time"; +"cpu.up_time_days" = "Uptime Days"; "cpu.thermal_level" = "Thermal Lv."; "cpu.system" = "Sistema"; "cpu.user" = "Utente"; diff --git a/Resource/ja.lproj/Localizable.strings b/Resource/ja.lproj/Localizable.strings index 267395f8..91e96194 100644 --- a/Resource/ja.lproj/Localizable.strings +++ b/Resource/ja.lproj/Localizable.strings @@ -37,6 +37,7 @@ "cpu.physical_cores" = "物理コア"; "cpu.logical_cores" = "論理コア"; "cpu.up_time" = "稼働時間"; +"cpu.up_time_days" = "稼働日数"; "cpu.thermal_level" = "温度レベル"; "cpu.system" = "システム"; "cpu.user" = "ユーザー"; diff --git a/Resource/ko.lproj/Localizable.strings b/Resource/ko.lproj/Localizable.strings index 758b9bfa..52da3d44 100644 --- a/Resource/ko.lproj/Localizable.strings +++ b/Resource/ko.lproj/Localizable.strings @@ -40,6 +40,7 @@ battery.timeRemaining = "Time Rem."; "cpu.physical_cores" = "물리 코어"; "cpu.logical_cores" = "논리 코어"; "cpu.up_time" = "가동 시간"; +"cpu.up_time_days" = "Uptime Days"; "cpu.thermal_level" = "온도 Lv."; "cpu.system" = "시스템"; "cpu.user" = "사용자"; diff --git a/Resource/mn.lproj/Localizable.strings b/Resource/mn.lproj/Localizable.strings index fa05078b..13fbbb52 100644 --- a/Resource/mn.lproj/Localizable.strings +++ b/Resource/mn.lproj/Localizable.strings @@ -37,6 +37,7 @@ "cpu.physical_cores" = "Бодит цөм"; "cpu.logical_cores" = "Логик цөм"; "cpu.up_time" = "Асаалттай"; +"cpu.up_time_days" = "Uptime Days"; "cpu.thermal_level" = "Thermal Lv."; "cpu.system" = "Систем"; "cpu.user" = "Хэрэглэгч"; diff --git a/Resource/my.lproj/Localizable.strings b/Resource/my.lproj/Localizable.strings index fe14a131..4f7535d8 100644 --- a/Resource/my.lproj/Localizable.strings +++ b/Resource/my.lproj/Localizable.strings @@ -37,6 +37,7 @@ "cpu.physical_cores" = "Physical Cores"; "cpu.logical_cores" = "Logical Cores"; "cpu.up_time" = "Up Time"; +"cpu.up_time_days" = "Uptime Days"; "cpu.thermal_level" = "အပူအဆင့်"; "cpu.system" = "စနစ်"; "cpu.user" = "အသုံးပြုသူ"; diff --git a/Resource/pl.lproj/Localizable.strings b/Resource/pl.lproj/Localizable.strings index 94ae9b2c..51f1b6c5 100644 --- a/Resource/pl.lproj/Localizable.strings +++ b/Resource/pl.lproj/Localizable.strings @@ -37,6 +37,7 @@ "cpu.physical_cores" = "Physical Cores"; "cpu.logical_cores" = "Logical Cores"; "cpu.up_time" = "Up Time"; +"cpu.up_time_days" = "Uptime Days"; "cpu.thermal_level" = "Thermal Lv."; "cpu.system" = "System"; "cpu.user" = "Użytkownik"; diff --git a/Resource/pt.lproj/Localizable.strings b/Resource/pt.lproj/Localizable.strings index 9a5b10c0..da8756e2 100644 --- a/Resource/pt.lproj/Localizable.strings +++ b/Resource/pt.lproj/Localizable.strings @@ -37,6 +37,7 @@ "cpu.physical_cores" = "Núcleos Físicos"; "cpu.logical_cores" = "Núcleos Lógicos"; "cpu.up_time" = "Tempo de Atividade"; +"cpu.up_time_days" = "Uptime Days"; "cpu.thermal_level" = "Nvl. Térmico"; "cpu.system" = "Sistema"; "cpu.user" = "Utilizador"; diff --git a/Resource/ru.lproj/Localizable.strings b/Resource/ru.lproj/Localizable.strings index ad1c7090..f9627e2a 100644 --- a/Resource/ru.lproj/Localizable.strings +++ b/Resource/ru.lproj/Localizable.strings @@ -37,6 +37,7 @@ "cpu.physical_cores" = "Физических Ядер"; "cpu.logical_cores" = "Логических Ядер"; "cpu.up_time" = "Время работы"; +"cpu.up_time_days" = "Uptime Days"; "cpu.thermal_level" = "Уровень тепла"; "cpu.system" = "Система"; "cpu.user" = "Пользователь"; diff --git a/Resource/se.lproj/Localizable.strings b/Resource/se.lproj/Localizable.strings index b869747e..031ef91d 100644 --- a/Resource/se.lproj/Localizable.strings +++ b/Resource/se.lproj/Localizable.strings @@ -38,6 +38,7 @@ Copyright © 2020 Gao Sun. All rights reserved. "cpu.physical_cores" = "Fysisk kärna"; "cpu.logical_cores" = "Logisk kärna"; "cpu.up_time" = "Upp tid"; +"cpu.up_time_days" = "Uptime Days"; "cpu.thermal_level" = "Termisk nivå"; "cpu.system" = "System"; "cpu.user" = "Användare"; diff --git a/Resource/th.lproj/Localizable.strings b/Resource/th.lproj/Localizable.strings index 9d5e797f..9a890a5f 100644 --- a/Resource/th.lproj/Localizable.strings +++ b/Resource/th.lproj/Localizable.strings @@ -37,6 +37,7 @@ "cpu.physical_cores" = "แกนประมวลผลทางกายภาพ"; "cpu.logical_cores" = "แกนประมวลผลทางตรรกะ"; "cpu.up_time" = "เวลาทำงาน"; +"cpu.up_time_days" = "Uptime Days"; "cpu.thermal_level" = "ระดับความร้อน"; "cpu.system" = "ระบบ"; "cpu.user" = "ผู้ใช้"; diff --git a/Resource/tr.lproj/Localizable.strings b/Resource/tr.lproj/Localizable.strings index 0fd047f3..72bb0653 100644 --- a/Resource/tr.lproj/Localizable.strings +++ b/Resource/tr.lproj/Localizable.strings @@ -37,6 +37,7 @@ "cpu.physical_cores" = "Fiziksel çekirdekler"; "cpu.logical_cores" = "Mantıksal çekirdekler"; "cpu.up_time" = "Çalışma süresi"; +"cpu.up_time_days" = "Uptime Days"; "cpu.thermal_level" = "Termal düzey"; "cpu.system" = "Sistem"; "cpu.user" = "Kullanıcı"; diff --git a/Resource/uk.lproj/Localizable.strings b/Resource/uk.lproj/Localizable.strings index 5fc81a9f..73b78c93 100644 --- a/Resource/uk.lproj/Localizable.strings +++ b/Resource/uk.lproj/Localizable.strings @@ -37,6 +37,7 @@ "cpu.physical_cores" = "Фізичних Ядер"; "cpu.logical_cores" = "Логічних Ядер"; "cpu.up_time" = "Час Роботи"; +"cpu.up_time_days" = "Uptime Days"; "cpu.thermal_level" = "Тепловий Рівень"; "cpu.system" = "Система"; "cpu.user" = "Користувач"; diff --git a/Resource/zh-Hans.lproj/Localizable.strings b/Resource/zh-Hans.lproj/Localizable.strings index 0375d28e..ee8fa88d 100644 --- a/Resource/zh-Hans.lproj/Localizable.strings +++ b/Resource/zh-Hans.lproj/Localizable.strings @@ -37,6 +37,7 @@ "cpu.physical_cores" = "物理核心"; "cpu.logical_cores" = "逻辑核心"; "cpu.up_time" = "运行时间"; +"cpu.up_time_days" = "开机天数"; "cpu.thermal_level" = "温度等级"; "cpu.system" = "系统"; "cpu.user" = "用户"; diff --git a/Resource/zh-Hant.lproj/Localizable.strings b/Resource/zh-Hant.lproj/Localizable.strings index c0bbcf12..19a60565 100644 --- a/Resource/zh-Hant.lproj/Localizable.strings +++ b/Resource/zh-Hant.lproj/Localizable.strings @@ -37,6 +37,7 @@ "cpu.physical_cores" = "物理核心"; "cpu.logical_cores" = "邏輯核心"; "cpu.up_time" = "執行時間"; +"cpu.up_time_days" = "開機天數"; "cpu.thermal_level" = "溫度等級"; "cpu.system" = "系統"; "cpu.user" = "使用者"; diff --git a/SharedLibrary/Components/ProgressBarView.swift b/SharedLibrary/Components/ProgressBarView.swift index f6ae61c1..c1ed4972 100644 --- a/SharedLibrary/Components/ProgressBarView.swift +++ b/SharedLibrary/Components/ProgressBarView.swift @@ -9,12 +9,13 @@ import SwiftUI public struct ProgressBarView: View { - public init(width: CGFloat = 80, percentage: CGFloat = 100, showText: Bool = true, textWidth: CGFloat = 40, customText: String? = nil) { + public init(width: CGFloat = 80, percentage: CGFloat = 100, showText: Bool = true, textWidth: CGFloat = 40, customText: String? = nil, color: Color = .primary) { self.width = width self.percentage = percentage self.showText = showText self.textWidth = textWidth self.customText = customText + self.color = color } @State var firstAppear = true @@ -23,6 +24,7 @@ public struct ProgressBarView: View { public var showText = true public var textWidth: CGFloat = 40 public var customText: String? + public var color: Color = .primary public var body: some View { HStack(alignment: .center, spacing: 8) { @@ -32,7 +34,7 @@ public struct ProgressBarView: View { .foregroundColor(.controlBackground) RoundedRectangle(cornerRadius: 4) .frame(width: width * percentage / 100, height: 4) - .foregroundColor(.primary) + .foregroundColor(color) } if showText { Text(customText.map { $0 } ?? String(format: "%.1f%%", percentage)) diff --git a/SharedLibrary/Extension/Color.swift b/SharedLibrary/Extension/Color.swift index 43887ff6..7e1e4493 100644 --- a/SharedLibrary/Extension/Color.swift +++ b/SharedLibrary/Extension/Color.swift @@ -39,4 +39,10 @@ public extension Color { static let shadow = Color(NSColor.shadowColor) static let separator = Color(NSColor.separatorColor) static let thirdary = Color.secondary.opacity(0.7) + + // Graph Bar Colors + static let graphRed = Color(hex: "e03a3e") + static let graphBlue = Color(hex: "009ddc") + static let graphOrange = Color(hex: "f6821f") + static let graphYellow = Color(hex: "fcb827") } diff --git a/eul.xcodeproj/project.pbxproj b/eul.xcodeproj/project.pbxproj index 0ca0819e..278df87d 100644 --- a/eul.xcodeproj/project.pbxproj +++ b/eul.xcodeproj/project.pbxproj @@ -14,7 +14,6 @@ 6C02716925BD5719006C54E6 /* GpuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C02716825BD5719006C54E6 /* GpuView.swift */; }; 6C02716C25BD6491006C54E6 /* GpuTextComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C02716B25BD6491006C54E6 /* GpuTextComponent.swift */; }; 6C02718525C0717C006C54E6 /* Usage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C02718425C0717C006C54E6 /* Usage.swift */; }; - 6C07E8A825167DBF00F728F9 /* LaunchAtLogin in Frameworks */ = {isa = PBXBuildFile; productRef = 6C07E8A725167DBF00F728F9 /* LaunchAtLogin */; }; 6C15933F257241DB00FDE7EA /* Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C15933E257241DB00FDE7EA /* Collection.swift */; }; 6C16BEEC2552EC9700CCF5B7 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6C58053F250CC6B8007F467A /* Localizable.strings */; }; 6C16BF0C2552F21C00CCF5B7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6C5F8542249FACE200CF0EF8 /* Assets.xcassets */; }; @@ -566,7 +565,6 @@ buildActionMask = 2147483647; files = ( 6CE7969325166916008BEB2B /* SwiftyJSON in Frameworks */, - 6C07E8A825167DBF00F728F9 /* LaunchAtLogin in Frameworks */, 6C2688F52556762B00FB7306 /* SharedLibrary.framework in Frameworks */, 6CF28D0925168B8200EBE9CB /* SystemKit in Frameworks */, 6C86924C251665A50009A784 /* Localize_Swift in Frameworks */, @@ -1086,7 +1084,6 @@ 6C5F8539249FACE000CF0EF8 /* Resources */, 6C4E880F24A6547000766E15 /* CopyFiles */, 6CC6D7A4250E2810000B03D4 /* Embed Frameworks */, - 6CC0798D250CEE96000D7DAC /* Copy LaunchAtLogin helper */, 6C7E1F102552EB2700287D08 /* Embed App Extensions */, ); buildRules = ( @@ -1103,7 +1100,6 @@ packageProductDependencies = ( 6C86924B251665A50009A784 /* Localize_Swift */, 6CE7969225166916008BEB2B /* SwiftyJSON */, - 6C07E8A725167DBF00F728F9 /* LaunchAtLogin */, 6CF28D0825168B8200EBE9CB /* SystemKit */, ); productName = eul; @@ -1238,7 +1234,6 @@ packageReferences = ( 6C86924A251665A50009A784 /* XCRemoteSwiftPackageReference "Localize-Swift" */, 6CE7969125166916008BEB2B /* XCRemoteSwiftPackageReference "SwiftyJSON" */, - 6C07E8A625167DBF00F728F9 /* XCRemoteSwiftPackageReference "LaunchAtLogin" */, 6CF28D0725168B8200EBE9CB /* XCRemoteSwiftPackageReference "SystemKit" */, ); productRefGroup = 6C5F853C249FACE000CF0EF8 /* Products */; @@ -1345,25 +1340,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "cd BuildTools\nSDKROOT=macosx\n#swift package update #Uncomment this line temporarily to update the version used to the latest matching your BuildTools/Package.swift file\nswift run -c release swiftformat \"$SRCROOT\"\n"; - }; - 6CC0798D250CEE96000D7DAC /* Copy LaunchAtLogin helper */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "Copy LaunchAtLogin helper"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${BUILT_PRODUCTS_DIR}/LaunchAtLogin_LaunchAtLogin.bundle/Contents/Resources/copy-helper-swiftpm.sh\"\n"; + shellScript = "# SwiftFormat disabled to speed up builds\nexit 0\n"; }; /* End PBXShellScriptBuildPhase section */ @@ -1661,7 +1638,7 @@ COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 7; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = M8G2RFZVFV; + DEVELOPMENT_TEAM = MH46WY74D9; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -1672,7 +1649,7 @@ "@executable_path/../Frameworks", "@loader_path/Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.15; + MACOSX_DEPLOYMENT_TARGET = 11.0; MARKETING_VERSION = 1.0.6; PRODUCT_BUNDLE_IDENTIFIER = com.gaosun.eul.SharedLibrary; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; @@ -1692,7 +1669,7 @@ COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 7; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = M8G2RFZVFV; + DEVELOPMENT_TEAM = MH46WY74D9; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -1703,7 +1680,7 @@ "@executable_path/../Frameworks", "@loader_path/Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.15; + MACOSX_DEPLOYMENT_TARGET = 11.0; MARKETING_VERSION = 1.0.6; PRODUCT_BUNDLE_IDENTIFIER = com.gaosun.eul.SharedLibrary; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; @@ -1723,7 +1700,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 52; - DEVELOPMENT_TEAM = M8G2RFZVFV; + DEVELOPMENT_TEAM = MH46WY74D9; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = MemoryWidget/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -1732,7 +1709,7 @@ "@executable_path/../../../../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 1.6.2; + MARKETING_VERSION = 1.6.3; PRODUCT_BUNDLE_IDENTIFIER = com.gaosun.eul.MemoryWidget; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -1749,7 +1726,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 52; - DEVELOPMENT_TEAM = M8G2RFZVFV; + DEVELOPMENT_TEAM = MH46WY74D9; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = MemoryWidget/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -1758,7 +1735,7 @@ "@executable_path/../../../../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 1.6.2; + MARKETING_VERSION = 1.6.3; PRODUCT_BUNDLE_IDENTIFIER = com.gaosun.eul.MemoryWidget; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -1775,7 +1752,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 52; - DEVELOPMENT_TEAM = M8G2RFZVFV; + DEVELOPMENT_TEAM = MH46WY74D9; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = NetworkWidget/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -1784,7 +1761,7 @@ "@executable_path/../../../../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 1.6.2; + MARKETING_VERSION = 1.6.3; PRODUCT_BUNDLE_IDENTIFIER = com.gaosun.eul.NetworkWidget; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -1801,7 +1778,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 52; - DEVELOPMENT_TEAM = M8G2RFZVFV; + DEVELOPMENT_TEAM = MH46WY74D9; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = NetworkWidget/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -1810,7 +1787,7 @@ "@executable_path/../../../../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 1.6.2; + MARKETING_VERSION = 1.6.3; PRODUCT_BUNDLE_IDENTIFIER = com.gaosun.eul.NetworkWidget; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -1870,7 +1847,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.15; + MACOSX_DEPLOYMENT_TARGET = 26.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -1926,7 +1903,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.15; + MACOSX_DEPLOYMENT_TARGET = 26.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = macosx; @@ -1947,7 +1924,7 @@ COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 52; DEVELOPMENT_ASSET_PATHS = ""; - DEVELOPMENT_TEAM = M8G2RFZVFV; + DEVELOPMENT_TEAM = MH46WY74D9; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; @@ -1956,8 +1933,8 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 1.6.2; + MACOSX_DEPLOYMENT_TARGET = 11.0; + MARKETING_VERSION = 1.6.3; PRODUCT_BUNDLE_IDENTIFIER = com.gaosun.eul; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = ""; @@ -1978,7 +1955,7 @@ COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 52; DEVELOPMENT_ASSET_PATHS = ""; - DEVELOPMENT_TEAM = M8G2RFZVFV; + DEVELOPMENT_TEAM = MH46WY74D9; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; @@ -1987,8 +1964,8 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 1.6.2; + MACOSX_DEPLOYMENT_TARGET = 11.0; + MARKETING_VERSION = 1.6.3; PRODUCT_BUNDLE_IDENTIFIER = com.gaosun.eul; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = ""; @@ -2005,7 +1982,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 52; - DEVELOPMENT_TEAM = M8G2RFZVFV; + DEVELOPMENT_TEAM = MH46WY74D9; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = BatteryWidget/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -2014,7 +1991,7 @@ "@executable_path/../../../../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 1.6.2; + MARKETING_VERSION = 1.6.3; PRODUCT_BUNDLE_IDENTIFIER = com.gaosun.eul.BatteryWidget; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -2031,7 +2008,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 52; - DEVELOPMENT_TEAM = M8G2RFZVFV; + DEVELOPMENT_TEAM = MH46WY74D9; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = BatteryWidget/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -2040,7 +2017,7 @@ "@executable_path/../../../../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 1.6.2; + MARKETING_VERSION = 1.6.3; PRODUCT_BUNDLE_IDENTIFIER = com.gaosun.eul.BatteryWidget; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -2057,7 +2034,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 52; - DEVELOPMENT_TEAM = M8G2RFZVFV; + DEVELOPMENT_TEAM = MH46WY74D9; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = CpuWidget/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -2066,7 +2043,7 @@ "@executable_path/../../../../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 1.6.2; + MARKETING_VERSION = 1.6.3; PRODUCT_BUNDLE_IDENTIFIER = com.gaosun.eul.CpuWidget; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -2083,7 +2060,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 52; - DEVELOPMENT_TEAM = M8G2RFZVFV; + DEVELOPMENT_TEAM = MH46WY74D9; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = CpuWidget/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -2092,7 +2069,7 @@ "@executable_path/../../../../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 1.6.2; + MARKETING_VERSION = 1.6.3; PRODUCT_BUNDLE_IDENTIFIER = com.gaosun.eul.CpuWidget; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -2116,7 +2093,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.15; + MACOSX_DEPLOYMENT_TARGET = 11.0; PRODUCT_BUNDLE_IDENTIFIER = com.gaosun.eul.SelfUpdate; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -2140,7 +2117,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.15; + MACOSX_DEPLOYMENT_TARGET = 11.0; PRODUCT_BUNDLE_IDENTIFIER = com.gaosun.eul.SelfUpdate; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -2226,14 +2203,6 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ - 6C07E8A625167DBF00F728F9 /* XCRemoteSwiftPackageReference "LaunchAtLogin" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/sindresorhus/LaunchAtLogin"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 4.0.0; - }; - }; 6C86924A251665A50009A784 /* XCRemoteSwiftPackageReference "Localize-Swift" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/marmelroy/Localize-Swift"; @@ -2261,11 +2230,6 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - 6C07E8A725167DBF00F728F9 /* LaunchAtLogin */ = { - isa = XCSwiftPackageProductDependency; - package = 6C07E8A625167DBF00F728F9 /* XCRemoteSwiftPackageReference "LaunchAtLogin" */; - productName = LaunchAtLogin; - }; 6C5DAD1F2556B42C005F67C3 /* Localize_Swift */ = { isa = XCSwiftPackageProductDependency; package = 6C86924A251665A50009A784 /* XCRemoteSwiftPackageReference "Localize-Swift" */; diff --git a/eul.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/eul.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 415e7a18..78555dd7 100644 --- a/eul.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/eul.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,15 +1,6 @@ { "object": { "pins": [ - { - "package": "LaunchAtLogin", - "repositoryURL": "https://github.com/sindresorhus/LaunchAtLogin", - "state": { - "branch": null, - "revision": "0f39982b9d6993eef253b81219d3c39ba1e680f3", - "version": "4.0.0" - } - }, { "package": "Localize_Swift", "repositoryURL": "https://github.com/marmelroy/Localize-Swift", diff --git a/eul/AppDelegate.swift b/eul/AppDelegate.swift index 6a2ebf2b..b0938243 100644 --- a/eul/AppDelegate.swift +++ b/eul/AppDelegate.swift @@ -17,6 +17,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate { private var isSleeping = false private var updateMethodCancellable: AnyCancellable? private var appearanceCancellable: AnyCancellable? + private var smcWorkItem: DispatchWorkItem? + private var networkWorkItem: DispatchWorkItem? + private var updateWorkItem: DispatchWorkItem? var window: NSWindow! @ObservedObject var preferenceStore = SharedStore.preference @@ -86,6 +89,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate { func sleep() { isSleeping = true + smcWorkItem?.cancel() + smcWorkItem = nil + networkWorkItem?.cancel() + networkWorkItem = nil + updateWorkItem?.cancel() + updateWorkItem = nil } func applicationWillTerminate(_: Notification) { @@ -115,35 +124,62 @@ extension AppDelegate { extension AppDelegate { func refreshSMCRepeatedly() { + smcWorkItem?.cancel() + smcWorkItem = nil + guard !isSleeping else { return } NotificationCenter.default.post(name: .SMCShouldRefresh, object: nil) - DispatchQueue.main.asyncAfter(deadline: .now() + Double(preferenceStore.smcRefreshRate)) { [self] in - refreshSMCRepeatedly() + + let workItem = DispatchWorkItem { [weak self] in + self?.refreshSMCRepeatedly() } + smcWorkItem = workItem + DispatchQueue.main.asyncAfter( + deadline: .now() + Double(preferenceStore.smcRefreshRate), + execute: workItem + ) } func refreshNetworkRepeatedly() { + networkWorkItem?.cancel() + networkWorkItem = nil + guard !isSleeping else { return } NotificationCenter.default.post(name: .NetworkShouldRefresh, object: nil) - DispatchQueue.main.asyncAfter(deadline: .now() + Double(preferenceStore.networkRefreshRate)) { [self] in - refreshNetworkRepeatedly() + + let workItem = DispatchWorkItem { [weak self] in + self?.refreshNetworkRepeatedly() } + networkWorkItem = workItem + DispatchQueue.main.asyncAfter( + deadline: .now() + Double(preferenceStore.networkRefreshRate), + execute: workItem + ) } func checkUpdateRepeatedly() { + updateWorkItem?.cancel() + updateWorkItem = nil + guard !isSleeping, preferenceStore.upgradeMethod != .none else { return } preferenceStore.checkUpdate() - DispatchQueue.main.asyncAfter(deadline: .now() + Double(60 * 60)) { [self] in - checkUpdateRepeatedly() + + let workItem = DispatchWorkItem { [weak self] in + self?.checkUpdateRepeatedly() } + updateWorkItem = workItem + DispatchQueue.main.asyncAfter( + deadline: .now() + Double(60 * 60), + execute: workItem + ) } } diff --git a/eul/Schema/EulComponentConfig.swift b/eul/Schema/EulComponentConfig.swift index b813e7b5..9f9a1aeb 100644 --- a/eul/Schema/EulComponentConfig.swift +++ b/eul/Schema/EulComponentConfig.swift @@ -7,14 +7,45 @@ // import Foundation +import SharedLibrary +import SwiftUI import SwiftyJSON +enum GraphColorOption: String, CaseIterable, Codable { + case monochrome + case red + case blue + case orange + case yellow + + var color: Color { + switch self { + case .monochrome: return .text + case .red: return .graphRed + case .blue: return .graphBlue + case .orange: return .graphOrange + case .yellow: return .graphYellow + } + } + + var label: String { + switch self { + case .monochrome: return "Monochrome" + case .red: return "Red" + case .blue: return "Blue" + case .orange: return "Orange" + case .yellow: return "Yellow" + } + } +} + struct EulComponentConfig: Codable { var component: EulComponent var showIcon: Bool = true var showGraph: Bool = false var diskSelection: String = "" var networkPortSelection: String = "" + var graphColor: GraphColorOption = .monochrome var json: JSON { JSON([ @@ -23,6 +54,7 @@ struct EulComponentConfig: Codable { "showGraph": showGraph, "diskSelection": diskSelection, "networkPortSelection": networkPortSelection, + "graphColor": graphColor.rawValue, ]) } } @@ -50,5 +82,9 @@ extension EulComponentConfig { if let string = json["networkPortSelection"].string { networkPortSelection = string } + + if let string = json["graphColor"].string, let option = GraphColorOption(rawValue: string) { + graphColor = option + } } } diff --git a/eul/Store/CpuStore.swift b/eul/Store/CpuStore.swift index 493d61e2..dd4ae149 100644 --- a/eul/Store/CpuStore.swift +++ b/eul/Store/CpuStore.swift @@ -40,6 +40,13 @@ class CpuStore: ObservableObject, Refreshable { return String(format: "%.0f%%", usage.system + usage.user) } + var upTimeDaysString: String { + guard let upTime = upTime else { + return "N/A" + } + return "\(upTime.days)" + } + var usage: Double? { guard let usageCPU = usageCPU else { return nil diff --git a/eul/Store/GpuStore.swift b/eul/Store/GpuStore.swift index 9bca4e1c..db8d1912 100644 --- a/eul/Store/GpuStore.swift +++ b/eul/Store/GpuStore.swift @@ -45,7 +45,21 @@ class GpuStore: ObservableObject, Refreshable { func getStatustic(for gpu: GPU) -> GPU.Statistic? { gpuStatistics.first { - $0.pciMatch.lowercased().contains(gpu.deviceId.deletingPrefix("0x")) + // For Intel GPUs, match by device ID in PCI match string + // For Apple Silicon GPUs, match by "apple" keyword in PCI match + let deviceIdLower = gpu.deviceId.deletingPrefix("0x").lowercased() + let pciMatchLower = $0.pciMatch.lowercased() + + // Check if it's an Apple Silicon GPU (deviceId contains "apple" or "m1/m2/m3/m4") + let isAppleSilicon = deviceIdLower.contains("apple") || deviceIdLower.contains(" m") + + if isAppleSilicon { + // For Apple Silicon, match if pciMatch is "apple" + return pciMatchLower == "apple" + } else { + // For Intel GPUs, match by device ID + return pciMatchLower.contains(deviceIdLower) + } } } diff --git a/eul/Utilities/GPU.swift b/eul/Utilities/GPU.swift index 682c14d7..33fd4358 100644 --- a/eul/Utilities/GPU.swift +++ b/eul/Utilities/GPU.swift @@ -40,9 +40,11 @@ extension GPU { } return plistArray.first?.items.compactMap { - guard $0.isGPU, let deviceId = $0.deviceId else { + guard $0.isGPU else { return nil } + // For Apple Silicon GPUs, use model name as identifier if device-id is not available + let deviceId = $0.deviceId ?? $0.model ?? "unknown" return GPU(deviceId: deviceId, model: $0.model, vendor: $0.vendor) } } @@ -55,22 +57,55 @@ extension GPU { } return propertyList.compactMap { - guard - let pciMatch = $0["IOPCIMatch"] as? String ?? $0["IOPCIPrimaryMatch"] as? String, - let statistics = $0["PerformanceStatistics"] as? [String: Any], - let usagePercentage = statistics["Device Utilization %"] as? Int ?? statistics["GPU Activity(%)"] as? Int - else { - return nil + // For Intel GPUs, use IOPCIMatch for device identification + // For Apple Silicon, IOPCIMatch may not be available, so use a fallback + let pciMatch = $0["IOPCIMatch"] as? String ?? $0["IOPCIPrimaryMatch"] as? String + + let statistics = $0["PerformanceStatistics"] as? [String: Any] + + // Try to get usage percentage from various keys + var usagePercentage: Int? + if let stats = statistics { + usagePercentage = stats["Device Utilization %"] as? Int + ?? stats["GPU Activity(%)"] as? Int + ?? stats["GPU Core Utilization"] as? Int + } + + // For Apple Silicon, try alternative methods if PerformanceStatistics is not available + if usagePercentage == nil { + // Try IOAcceleratorStatistics2 for Apple Silicon + if let stats2 = $0["IOAcceleratorStatistics2"] as? [String: Any] { + usagePercentage = stats2["Device Utilization %"] as? Int + ?? stats2["GPU Activity(%)"] as? Int + } + } + + // If still no usage data, default to 0 instead of failing + let finalUsage = usagePercentage ?? 0 + + Print("📊 statistics", statistics ?? [:]) + + // Try to get temperature from various sources + var temperature: Double? + if let stats = statistics { + temperature = stats["Temperature(C)"] as? Double + } + + // Fallback to SMC for temperature + if temperature == nil || temperature == 0 { + temperature = SmcControl.shared.gpuProximityTemperature } - Print("📊 statistics", statistics) + // For Apple Silicon, use "apple" as pciMatch if not available + // This allows matching with GPU devices that use model name as deviceId + let finalPciMatch = pciMatch ?? "apple" return Statistic( - pciMatch: pciMatch, - usagePercentage: usagePercentage, - temperature: statistics["Temperature(C)"] as? Double ?? SmcControl.shared.gpuProximityTemperature, - coreClock: statistics["Core Clock(MHz)"] as? Int, - memoryClock: statistics["Memory Clock(MHz)"] as? Int + pciMatch: finalPciMatch, + usagePercentage: finalUsage, + temperature: temperature, + coreClock: statistics?["Core Clock(MHz)"] as? Int, + memoryClock: statistics?["Memory Clock(MHz)"] as? Int ) } } diff --git a/eul/Utilities/SMC.swift b/eul/Utilities/SMC.swift index c186ea42..6158669d 100644 --- a/eul/Utilities/SMC.swift +++ b/eul/Utilities/SMC.swift @@ -103,6 +103,14 @@ extension Double { let sign = bytes.0 & 0x80 == 0 ? 1.0 : -1.0 self = sign * Double(bytes.0 & 0x7F) // AND to mask sign bit } + + init(fromFLT bytes: FLT) { + // Convert the SMCBytes to a float value (used on Apple Silicon) + let byteArray: [UInt8] = [bytes.0, bytes.1, bytes.2, bytes.3] + var resultValue: Float = 0.0 + memcpy(&resultValue, byteArray, 4) + self = Double(resultValue) + } } // Thanks to Airspeed Velocity for the great idea! @@ -197,7 +205,7 @@ public struct SMCParamStruct { public struct SMCKeyInfoData { /// How many bytes written to SMCParamStruct.bytes - var dataSize: IOByteCount = 0 + var dataSize: UInt32 = 0 /// Type of data written to SMCParamStruct.bytes. This lets us know how /// to interpret it (translate it to human readable) @@ -333,7 +341,7 @@ public enum SMCKit { let outputStruct = try callDriver(&inputStruct) return DataType(type: outputStruct.keyInfo.dataType, - size: UInt32(outputStruct.keyInfo.dataSize)) + size: outputStruct.keyInfo.dataSize) } /// Get information about the key at index @@ -355,7 +363,7 @@ public enum SMCKit { var inputStruct = SMCParamStruct() inputStruct.key = key.code - inputStruct.keyInfo.dataSize = IOByteCount(UInt32(key.info.size)) + inputStruct.keyInfo.dataSize = UInt32(key.info.size) inputStruct.data8 = SMCParamStruct.Selector.kSMCReadKey.rawValue let outputStruct = try callDriver(&inputStruct) @@ -369,7 +377,7 @@ public enum SMCKit { inputStruct.key = key.code inputStruct.bytes = data - inputStruct.keyInfo.dataSize = IOByteCount(UInt32(key.info.size)) + inputStruct.keyInfo.dataSize = UInt32(key.info.size) inputStruct.data8 = SMCParamStruct.Selector.kSMCWriteKey.rawValue _ = try callDriver(&inputStruct) @@ -479,6 +487,16 @@ public enum TemperatureSensors { public static let CPU_0_PROXIMITY = TemperatureSensor(name: "CPU_0_PROXIMITY", code: FourCharCode(fromStaticString: "TC0P")) + + // Apple Silicon (M-series) temperature sensors + public static let CPU_PCORE = TemperatureSensor(name: "CPU_PCORE", + code: FourCharCode(fromStaticString: "Tp09")) + public static let CPU_ECORE = TemperatureSensor(name: "CPU_ECORE", + code: FourCharCode(fromStaticString: "Tp0T")) + public static let CPU_PACKAGE = TemperatureSensor(name: "CPU_PACKAGE", + code: FourCharCode(fromStaticString: "Tp05")) + public static let GPU_APPLE_SILICON = TemperatureSensor(name: "GPU_APPLE_SILICON", + code: FourCharCode(fromStaticString: "Tg05")) public static let ENCLOSURE_BASE_0 = TemperatureSensor(name: "ENCLOSURE_BASE_0", code: FourCharCode(fromStaticString: "TB0T")) @@ -541,6 +559,10 @@ public enum TemperatureSensors { CPU_0_DIODE.code: CPU_0_DIODE, CPU_0_HEATSINK.code: CPU_0_HEATSINK, CPU_0_PROXIMITY.code: CPU_0_PROXIMITY, + CPU_PCORE.code: CPU_PCORE, + CPU_ECORE.code: CPU_ECORE, + CPU_PACKAGE.code: CPU_PACKAGE, + GPU_APPLE_SILICON.code: GPU_APPLE_SILICON, ENCLOSURE_BASE_0.code: ENCLOSURE_BASE_0, ENCLOSURE_BASE_1.code: ENCLOSURE_BASE_1, ENCLOSURE_BASE_2.code: ENCLOSURE_BASE_2, @@ -583,7 +605,7 @@ public extension SMCKit { let keys = try allKeys() return keys.filter { $0.code.toString().hasPrefix("T") && - $0.info == DataTypes.SP78 && + ($0.info == DataTypes.SP78 || $0.info == DataTypes.FLT) && TemperatureSensors.all[$0.code] == nil } .map { TemperatureSensor(name: "Unknown", code: $0.code) } @@ -593,9 +615,17 @@ public extension SMCKit { static func temperature(_ sensorCode: FourCharCode, unit: TemperatureUnit = .celius) throws -> Double { - let data = try readData(SMCKey(code: sensorCode, info: DataTypes.SP78)) + var temperatureInCelius: Double - let temperatureInCelius = Double(fromSP78: (data.0, data.1)) + // Try SP78 format first (Intel Macs) + do { + let data = try readData(SMCKey(code: sensorCode, info: DataTypes.SP78)) + temperatureInCelius = Double(fromSP78: (data.0, data.1)) + } catch { + // If SP78 fails (likely due to type mismatch on Apple Silicon), try FLT format + let data = try readData(SMCKey(code: sensorCode, info: DataTypes.FLT)) + temperatureInCelius = Double(fromFLT: (data.0, data.1, data.2, data.3)) + } switch unit { case .celius: diff --git a/eul/Utilities/Shell.swift b/eul/Utilities/Shell.swift index 9efc88c7..9d34220b 100644 --- a/eul/Utilities/Shell.swift +++ b/eul/Utilities/Shell.swift @@ -8,6 +8,8 @@ import Foundation +private let shellPipeQueue = DispatchQueue(label: "com.gao.eul.shellPipe", qos: .background) + // https://stackoverflow.com/questions/26971240/how-do-i-run-an-terminal-command-in-a-swift-script-e-g-xcodebuild @discardableResult func shellData(_ args: [String]) -> Data? { @@ -103,7 +105,7 @@ func shellPipe(_ args: String..., onData: ((String) -> Void)? = nil, didTerminat didTerminate?() } - DispatchQueue(label: "shellPipe-\(UUID().uuidString)", qos: .background, attributes: .concurrent).async { + shellPipeQueue.async { Print("good to launch") do { try task.run() diff --git a/eul/Utilities/SmcControl.swift b/eul/Utilities/SmcControl.swift index 4db24cc9..efe948a4 100644 --- a/eul/Utilities/SmcControl.swift +++ b/eul/Utilities/SmcControl.swift @@ -12,20 +12,48 @@ import SwiftyJSON class SmcControl: Refreshable { static var shared = SmcControl() + private let smcQueue = DispatchQueue(label: "com.gao.eul.smc", qos: .userInitiated) var sensors: [TemperatureData] = [] var fans: [FanData] = [] var tempUnit: TemperatureUnit = .celius var cpuDieTemperature: Double? { - sensors.first(where: { $0.sensor.name == "CPU_0_DIE" })?.temp + // Try Intel sensors first + if let temp = sensors.first(where: { $0.sensor.name == "CPU_0_DIE" })?.temp, temp > 0 { + return temp + } + // Fallback to Apple Silicon sensors + if let temp = sensors.first(where: { $0.sensor.name == "CPU_PCORE" })?.temp, temp > 0 { + return temp + } + if let temp = sensors.first(where: { $0.sensor.name == "CPU_PACKAGE" })?.temp, temp > 0 { + return temp + } + return nil } var cpuProximityTemperature: Double? { - sensors.first(where: { $0.sensor.name == "CPU_0_PROXIMITY" })?.temp + // Try Intel sensor first + if let temp = sensors.first(where: { $0.sensor.name == "CPU_0_PROXIMITY" })?.temp, temp > 0 { + return temp + } + // Fallback to Apple Silicon E-core sensor + if let temp = sensors.first(where: { $0.sensor.name == "CPU_ECORE" })?.temp, temp > 0 { + return temp + } + return nil } var gpuProximityTemperature: Double? { - sensors.first(where: { $0.sensor.name == "GPU_0_PROXIMITY" })?.temp + // Try Intel sensor first + if let temp = sensors.first(where: { $0.sensor.name == "GPU_0_PROXIMITY" })?.temp, temp > 0 { + return temp + } + // Fallback to Apple Silicon GPU sensor + if let temp = sensors.first(where: { $0.sensor.name == "GPU_APPLE_SILICON" })?.temp, temp > 0 { + return temp + } + return nil } var memoryProximityTemperature: Double? { @@ -67,23 +95,33 @@ class SmcControl: Refreshable { } @objc func refresh() { - for sensor in sensors { - do { - sensor.temp = try SMCKit.temperature(sensor.sensor.code, unit: tempUnit) - } catch { - sensor.temp = 0 - print("error while getting temperature", error) + smcQueue.async { [self] in + for sensor in sensors { + do { + sensor.temp = try SMCKit.temperature(sensor.sensor.code, unit: tempUnit) + } catch { + sensor.temp = 0 + // SMCResult 135 is a type mismatch (e.g. FLT vs SP78 on Apple Silicon) + // This is handled gracefully by the FLT fallback in SMCKit — suppress noise + let errorString = "\(error)" + if !errorString.contains("SMCResult: 135") { + print("error while getting temperature", error) + } + } + } + let updatedFans = fans.map { + FanData( + id: $0.id, + currentSpeed: try? SMCKit.fanCurrentSpeed($0.id), + minSpeed: $0.minSpeed, + maxSpeed: $0.maxSpeed + ) + } + DispatchQueue.main.async { [self] in + fans = updatedFans + NotificationCenter.default.post(name: .StoreShouldRefresh, object: nil) } } - fans = fans.map { - FanData( - id: $0.id, - currentSpeed: try? SMCKit.fanCurrentSpeed($0.id), - minSpeed: $0.minSpeed, - maxSpeed: $0.maxSpeed - ) - } - NotificationCenter.default.post(name: .StoreShouldRefresh, object: nil) } } diff --git a/eul/Views/Chart/LineChart.swift b/eul/Views/Chart/LineChart.swift index 46e88d74..71119f5d 100644 --- a/eul/Views/Chart/LineChart.swift +++ b/eul/Views/Chart/LineChart.swift @@ -18,6 +18,7 @@ struct LineChart: View { var maximumPoint: Double = 100 var minimumPoint: Double = 0 var frame = CGSize(width: (AppDelegate.statusBarHeight - 4) * 1.75, height: AppDelegate.statusBarHeight - 4) + var color: Color = .text var stepX: CGFloat { frame.width / (CGFloat(maxPointCount) - 1) @@ -74,9 +75,9 @@ struct LineChart: View { ZStack { Group { closedPath() - .fill(Color.text) + .fill(color.opacity(0.5)) path() - .stroke(Color.text, style: StrokeStyle(lineWidth: LineChart.minimumLineHeight, lineJoin: .round)) + .stroke(color, style: StrokeStyle(lineWidth: LineChart.minimumLineHeight, lineJoin: .round)) } .rotationEffect(.degrees(180), anchor: .center) .rotation3DEffect(.degrees(180), axis: (x: 0, y: 1, z: 0)) diff --git a/eul/Views/Menu/CpuMenuBlockView.swift b/eul/Views/Menu/CpuMenuBlockView.swift index 4964fd6c..cc3835f8 100644 --- a/eul/Views/Menu/CpuMenuBlockView.swift +++ b/eul/Views/Menu/CpuMenuBlockView.swift @@ -44,6 +44,8 @@ struct CpuMenuBlockView: View { Spacer() MiniSectionView(title: "15 min", value: cpuStore.loadAverage15MinString) } + Spacer() + MiniSectionView(title: "cpu.up_time_days", value: cpuStore.upTimeDaysString) cpuStore.temp.map { temp in Group { Spacer() diff --git a/eul/Views/Preference/PreferenceComponentConfigView.swift b/eul/Views/Preference/PreferenceComponentConfigView.swift index 8eb3a814..c7fe72d9 100644 --- a/eul/Views/Preference/PreferenceComponentConfigView.swift +++ b/eul/Views/Preference/PreferenceComponentConfigView.swift @@ -94,6 +94,30 @@ extension Preference { .fixedSize() } } + if config.wrappedValue.component.isGraphAvailable && config.wrappedValue.showGraph { + HStack(spacing: 8) { + Text("Graph Color:") + .inlineSection() + ForEach(GraphColorOption.allCases, id: \.self) { option in + Button(action: { + componentConfigStore[component].graphColor = option + }) { + ZStack { + Circle() + .fill(option == .monochrome ? Color.text : option.color) + .frame(width: 16, height: 16) + if config.wrappedValue.graphColor == option { + Circle() + .strokeBorder(Color.primary, lineWidth: 2) + .frame(width: 20, height: 20) + } + } + } + .buttonStyle(PlainButtonStyle()) + .help(option.label) + } + } + } if component == .CPU { ComponentTextConfigView() } diff --git a/eul/Views/Preference/PreferenceGeneralView.swift b/eul/Views/Preference/PreferenceGeneralView.swift index 41274934..c8850ac5 100644 --- a/eul/Views/Preference/PreferenceGeneralView.swift +++ b/eul/Views/Preference/PreferenceGeneralView.swift @@ -6,14 +6,83 @@ // Copyright © 2020 Gao Sun. All rights reserved. // -import LaunchAtLogin +import ServiceManagement import SharedLibrary import SwiftUI -import SwiftyJSON + +final class LaunchAtLoginStore: ObservableObject { + enum Availability { + case unsupported + case available(isEnabled: Bool, requiresApproval: Bool) + } + + @Published private(set) var availability: Availability = .unsupported + + var isEnabled: Bool { + if case let .available(isEnabled, _) = availability { + return isEnabled + } + return false + } + + var requiresApproval: Bool { + if case let .available(_, requiresApproval) = availability { + return requiresApproval + } + return false + } + + var isSupported: Bool { + if case .available = availability { + return true + } + return false + } + + init() { + refresh() + } + + func refresh() { + guard #available(macOS 13, *) else { + availability = .unsupported + return + } + + switch SMAppService.mainApp.status { + case .enabled: + availability = .available(isEnabled: true, requiresApproval: false) + case .requiresApproval: + availability = .available(isEnabled: true, requiresApproval: true) + case .notRegistered, .notFound: + availability = .available(isEnabled: false, requiresApproval: false) + @unknown default: + availability = .available(isEnabled: false, requiresApproval: false) + } + } + + func setEnabled(_ enabled: Bool) { + guard #available(macOS 13, *) else { + return + } + + do { + if enabled { + try SMAppService.mainApp.register() + } else { + try SMAppService.mainApp.unregister() + } + } catch { + print("Unable to update launch at login status", error.localizedDescription) + } + + refresh() + } +} extension Preference { struct GeneralView: View { - @ObservedObject var launchAtLogin = LaunchAtLogin.observable + @StateObject private var launchAtLogin = LaunchAtLoginStore() @EnvironmentObject var preference: PreferenceStore var body: some View { @@ -73,16 +142,23 @@ extension Preference { Text("ui.upgrade_method.\(preference.upgradeMethod.rawValue).description".localized()) .secondaryDisplayText() .padding(.bottom, 8) - Toggle(isOn: $launchAtLogin.isEnabled) { + Toggle(isOn: Binding( + get: { launchAtLogin.isEnabled }, + set: { launchAtLogin.setEnabled($0) } + )) { Text("ui.launch_at_login".localized()) .inlineSection() } + .disabled(!launchAtLogin.isSupported) Toggle(isOn: $preference.checkStatusItemVisibility) { Text("ui.check_status_item_visibility".localized()) .inlineSection() } } .padding(.vertical, 8) + .onAppear { + launchAtLogin.refresh() + } } } } diff --git a/eul/Views/StatusBar/CpuView.swift b/eul/Views/StatusBar/CpuView.swift index 27679919..68e06cc8 100644 --- a/eul/Views/StatusBar/CpuView.swift +++ b/eul/Views/StatusBar/CpuView.swift @@ -42,7 +42,7 @@ struct CpuView: View { .frame(width: 13, height: 13) } if config.showGraph { - LineChart(points: cpuStore.usageHistory) + LineChart(points: cpuStore.usageHistory, color: config.graphColor.color) } if textStore.showComponents { StatusBarTextView(texts: texts) diff --git a/eul/Views/StatusBar/GpuView.swift b/eul/Views/StatusBar/GpuView.swift index c04e5693..f8cf5ef3 100644 --- a/eul/Views/StatusBar/GpuView.swift +++ b/eul/Views/StatusBar/GpuView.swift @@ -36,7 +36,7 @@ struct GpuView: View { .frame(width: 13, height: 13) } if config.showGraph { - LineChart(points: gpuStore.usageHistory) + LineChart(points: gpuStore.usageHistory, color: config.graphColor.color) } if textStore.showComponents { StatusBarTextView(texts: texts) diff --git a/eul/Views/StatusBar/MemoryView.swift b/eul/Views/StatusBar/MemoryView.swift index c894c6b1..5dbcf7c4 100644 --- a/eul/Views/StatusBar/MemoryView.swift +++ b/eul/Views/StatusBar/MemoryView.swift @@ -42,7 +42,7 @@ struct MemoryView: View { .frame(width: 13, height: 13) } if config.showGraph { - LineChart(points: memoryStore.usageHistory) + LineChart(points: memoryStore.usageHistory, color: config.graphColor.color) } if textStore.showComponents { StatusBarTextView(texts: texts)