From 77119a1a13c5c72455d23a37595cfd9cce80f036 Mon Sep 17 00:00:00 2001 From: David Wernhart Date: Sun, 14 Feb 2021 22:01:54 +0100 Subject: [PATCH] Apple Silicon Support --- AlDente.xcodeproj/project.pbxproj | 567 +++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/swiftpm/Package.resolved | 16 + .../UserInterfaceState.xcuserstate | Bin 0 -> 73057 bytes .../xcdebugger/Breakpoints_v2.xcbkptlist | 24 + .../xcschemes/xcschememanagement.plist | 39 + AlDente/AppDelegate.swift | 133 +++ .../AppIcon.appiconset/Contents.json | 59 ++ .../AppIcon.appiconset/spaghetti.png | Bin 0 -> 74592 bytes AlDente/Assets.xcassets/Contents.json | 6 + .../menubaricon.imageset/Contents.json | 54 ++ .../aldentedarkicon1x.png | Bin 0 -> 2202 bytes .../aldentedarkicon2x.png | Bin 0 -> 3737 bytes .../aldentelighticon1x.png | Bin 0 -> 2073 bytes .../aldentelighticon2x.png | Bin 0 -> 3353 bytes AlDente/Base.lproj/Main.storyboard | 88 ++ AlDente/ContentView.swift | 259 ++++++ AlDente/Helper.swift | 293 +++++++ AlDente/Info.plist | 45 + AlDente/PersistanceManager.swift | 29 + .../Preview Assets.xcassets/Contents.json | 6 + Common/HelperToolProtocol.swift | 27 + README.md | 85 ++ SMJobBlessUtil.py | 437 ++++++++++ com.davidwernhart.Helper/Helper-Info.plist | 18 + com.davidwernhart.Helper/Helper-Launchd.plist | 13 + com.davidwernhart.Helper/HelperTool.swift | 126 +++ com.davidwernhart.Helper/SMC.swift | 795 ++++++++++++++++++ com.davidwernhart.Helper/main.swift | 44 + 30 files changed, 3178 insertions(+) create mode 100644 AlDente.xcodeproj/project.pbxproj create mode 100644 AlDente.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 AlDente.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 AlDente.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 AlDente.xcodeproj/project.xcworkspace/xcuserdata/davidwernhart.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 AlDente.xcodeproj/xcuserdata/davidwernhart.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist create mode 100644 AlDente.xcodeproj/xcuserdata/davidwernhart.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 AlDente/AppDelegate.swift create mode 100644 AlDente/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 AlDente/Assets.xcassets/AppIcon.appiconset/spaghetti.png create mode 100644 AlDente/Assets.xcassets/Contents.json create mode 100644 AlDente/Assets.xcassets/menubaricon.imageset/Contents.json create mode 100644 AlDente/Assets.xcassets/menubaricon.imageset/aldentedarkicon1x.png create mode 100644 AlDente/Assets.xcassets/menubaricon.imageset/aldentedarkicon2x.png create mode 100644 AlDente/Assets.xcassets/menubaricon.imageset/aldentelighticon1x.png create mode 100644 AlDente/Assets.xcassets/menubaricon.imageset/aldentelighticon2x.png create mode 100644 AlDente/Base.lproj/Main.storyboard create mode 100644 AlDente/ContentView.swift create mode 100644 AlDente/Helper.swift create mode 100644 AlDente/Info.plist create mode 100644 AlDente/PersistanceManager.swift create mode 100644 AlDente/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 Common/HelperToolProtocol.swift create mode 100644 README.md create mode 100755 SMJobBlessUtil.py create mode 100644 com.davidwernhart.Helper/Helper-Info.plist create mode 100644 com.davidwernhart.Helper/Helper-Launchd.plist create mode 100644 com.davidwernhart.Helper/HelperTool.swift create mode 100644 com.davidwernhart.Helper/SMC.swift create mode 100644 com.davidwernhart.Helper/main.swift diff --git a/AlDente.xcodeproj/project.pbxproj b/AlDente.xcodeproj/project.pbxproj new file mode 100644 index 0000000..7de3ba6 --- /dev/null +++ b/AlDente.xcodeproj/project.pbxproj @@ -0,0 +1,567 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 505E96C92586211600DAA405 /* LaunchAtLogin in Frameworks */ = {isa = PBXBuildFile; productRef = 505E96C82586211600DAA405 /* LaunchAtLogin */; }; + 9224A08723F1F70600961AC4 /* com.davidwernhart.Helper in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9283741823F1F34400B8AE7A /* com.davidwernhart.Helper */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 927DBFC923F5478E00F8BF0D /* HelperToolProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927DBFC823F5478E00F8BF0D /* HelperToolProtocol.swift */; }; + 927DBFCA23F5478E00F8BF0D /* HelperToolProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927DBFC823F5478E00F8BF0D /* HelperToolProtocol.swift */; }; + 9283741B23F1F34400B8AE7A /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9283741A23F1F34400B8AE7A /* main.swift */; }; + 928674C025D07BA300EC79C1 /* PersistanceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 928674BF25D07BA300EC79C1 /* PersistanceManager.swift */; }; + 92981EDB23F08D9B00C05424 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92981EDA23F08D9B00C05424 /* AppDelegate.swift */; }; + 92981EDD23F08D9B00C05424 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92981EDC23F08D9B00C05424 /* ContentView.swift */; }; + 92981EDF23F08D9C00C05424 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 92981EDE23F08D9C00C05424 /* Assets.xcassets */; }; + 92981EE223F08D9C00C05424 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 92981EE123F08D9C00C05424 /* Preview Assets.xcassets */; }; + 92981EE523F08D9C00C05424 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 92981EE323F08D9C00C05424 /* Main.storyboard */; }; + 92ACA12B23F5F822003512DC /* HelperTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927DBFCB23F54C2C00F8BF0D /* HelperTool.swift */; }; + 92ACA12E23F5F861003512DC /* SMC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92ACA12D23F5F861003512DC /* SMC.swift */; }; + 92E24B4123F6DC0D00BE41ED /* Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92E24B4023F6DC0D00BE41ED /* Helper.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9224A08623F1F6E300961AC4 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 12; + dstPath = Contents/Library/LaunchServices; + dstSubfolderSpec = 1; + files = ( + 9224A08723F1F70600961AC4 /* com.davidwernhart.Helper in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 927DBFC623F533FB00F8BF0D /* Helper-Launchd.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Helper-Launchd.plist"; sourceTree = ""; }; + 927DBFC823F5478E00F8BF0D /* HelperToolProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelperToolProtocol.swift; sourceTree = ""; }; + 927DBFCB23F54C2C00F8BF0D /* HelperTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelperTool.swift; sourceTree = ""; }; + 9283741823F1F34400B8AE7A /* com.davidwernhart.Helper */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = com.davidwernhart.Helper; sourceTree = BUILT_PRODUCTS_DIR; }; + 9283741A23F1F34400B8AE7A /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; + 9283741F23F1F38100B8AE7A /* Helper-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Helper-Info.plist"; sourceTree = ""; }; + 928674BF25D07BA300EC79C1 /* PersistanceManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersistanceManager.swift; sourceTree = ""; }; + 92981ED723F08D9B00C05424 /* AlDente.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AlDente.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 92981EDA23F08D9B00C05424 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 92981EDC23F08D9B00C05424 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 92981EDE23F08D9C00C05424 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 92981EE123F08D9C00C05424 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 92981EE423F08D9C00C05424 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 92981EE623F08D9C00C05424 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 92ACA12D23F5F861003512DC /* SMC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SMC.swift; sourceTree = ""; }; + 92E24B4023F6DC0D00BE41ED /* Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Helper.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 92981ED423F08D9B00C05424 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 505E96C92586211600DAA405 /* LaunchAtLogin in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 927DBFC723F545AF00F8BF0D /* Common */ = { + isa = PBXGroup; + children = ( + 927DBFC823F5478E00F8BF0D /* HelperToolProtocol.swift */, + ); + path = Common; + sourceTree = ""; + }; + 9283741923F1F34400B8AE7A /* com.davidwernhart.Helper */ = { + isa = PBXGroup; + children = ( + 9283741A23F1F34400B8AE7A /* main.swift */, + 9283741F23F1F38100B8AE7A /* Helper-Info.plist */, + 927DBFC623F533FB00F8BF0D /* Helper-Launchd.plist */, + 927DBFCB23F54C2C00F8BF0D /* HelperTool.swift */, + 92ACA12D23F5F861003512DC /* SMC.swift */, + ); + path = com.davidwernhart.Helper; + sourceTree = ""; + }; + 92981ECE23F08D9B00C05424 = { + isa = PBXGroup; + children = ( + 927DBFC723F545AF00F8BF0D /* Common */, + 92981ED923F08D9B00C05424 /* AlDente */, + 9283741923F1F34400B8AE7A /* com.davidwernhart.Helper */, + 92981ED823F08D9B00C05424 /* Products */, + ); + sourceTree = ""; + }; + 92981ED823F08D9B00C05424 /* Products */ = { + isa = PBXGroup; + children = ( + 92981ED723F08D9B00C05424 /* AlDente.app */, + 9283741823F1F34400B8AE7A /* com.davidwernhart.Helper */, + ); + name = Products; + sourceTree = ""; + }; + 92981ED923F08D9B00C05424 /* AlDente */ = { + isa = PBXGroup; + children = ( + 92E24B4023F6DC0D00BE41ED /* Helper.swift */, + 92981EDA23F08D9B00C05424 /* AppDelegate.swift */, + 92981EDC23F08D9B00C05424 /* ContentView.swift */, + 92981EDE23F08D9C00C05424 /* Assets.xcassets */, + 92981EE323F08D9C00C05424 /* Main.storyboard */, + 92981EE623F08D9C00C05424 /* Info.plist */, + 92981EE023F08D9C00C05424 /* Preview Content */, + 928674BF25D07BA300EC79C1 /* PersistanceManager.swift */, + ); + path = AlDente; + sourceTree = ""; + }; + 92981EE023F08D9C00C05424 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 92981EE123F08D9C00C05424 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 9283741723F1F34400B8AE7A /* com.davidwernhart.Helper */ = { + isa = PBXNativeTarget; + buildConfigurationList = 9283741C23F1F34400B8AE7A /* Build configuration list for PBXNativeTarget "com.davidwernhart.Helper" */; + buildPhases = ( + 9283741423F1F34400B8AE7A /* Sources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = com.davidwernhart.Helper; + productName = com.davidwernhart.Helper; + productReference = 9283741823F1F34400B8AE7A /* com.davidwernhart.Helper */; + productType = "com.apple.product-type.tool"; + }; + 92981ED623F08D9B00C05424 /* AlDente */ = { + isa = PBXNativeTarget; + buildConfigurationList = 92981EEA23F08D9C00C05424 /* Build configuration list for PBXNativeTarget "AlDente" */; + buildPhases = ( + 92981ED323F08D9B00C05424 /* Sources */, + 92981ED423F08D9B00C05424 /* Frameworks */, + 92981ED523F08D9B00C05424 /* Resources */, + 9224A08623F1F6E300961AC4 /* CopyFiles */, + 92E24B4423F847B500BE41ED /* LaunchAtLogin */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = AlDente; + packageProductDependencies = ( + 505E96C82586211600DAA405 /* LaunchAtLogin */, + ); + productName = AlDente; + productReference = 92981ED723F08D9B00C05424 /* AlDente.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 92981ECF23F08D9B00C05424 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1130; + LastUpgradeCheck = 1220; + ORGANIZATIONNAME = "David Wernhart"; + TargetAttributes = { + 9283741723F1F34400B8AE7A = { + CreatedOnToolsVersion = 11.3.1; + }; + 92981ED623F08D9B00C05424 = { + CreatedOnToolsVersion = 11.3.1; + }; + }; + }; + buildConfigurationList = 92981ED223F08D9B00C05424 /* Build configuration list for PBXProject "AlDente" */; + compatibilityVersion = "Xcode 12.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 92981ECE23F08D9B00C05424; + packageReferences = ( + 505E96C72586211600DAA405 /* XCRemoteSwiftPackageReference "LaunchAtLogin" */, + ); + productRefGroup = 92981ED823F08D9B00C05424 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 92981ED623F08D9B00C05424 /* AlDente */, + 9283741723F1F34400B8AE7A /* com.davidwernhart.Helper */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 92981ED523F08D9B00C05424 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 92981EE523F08D9C00C05424 /* Main.storyboard in Resources */, + 92981EE223F08D9C00C05424 /* Preview Assets.xcassets in Resources */, + 92981EDF23F08D9C00C05424 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 92E24B4423F847B500BE41ED /* LaunchAtLogin */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = LaunchAtLogin; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${BUILT_PRODUCTS_DIR}/LaunchAtLogin_LaunchAtLogin.bundle/Contents/Resources/copy-helper-swiftpm.sh\"\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 9283741423F1F34400B8AE7A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9283741B23F1F34400B8AE7A /* main.swift in Sources */, + 92ACA12B23F5F822003512DC /* HelperTool.swift in Sources */, + 927DBFCA23F5478E00F8BF0D /* HelperToolProtocol.swift in Sources */, + 92ACA12E23F5F861003512DC /* SMC.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 92981ED323F08D9B00C05424 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 928674C025D07BA300EC79C1 /* PersistanceManager.swift in Sources */, + 927DBFC923F5478E00F8BF0D /* HelperToolProtocol.swift in Sources */, + 92981EDD23F08D9B00C05424 /* ContentView.swift in Sources */, + 92981EDB23F08D9B00C05424 /* AppDelegate.swift in Sources */, + 92E24B4123F6DC0D00BE41ED /* Helper.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 92981EE323F08D9C00C05424 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 92981EE423F08D9C00C05424 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 9283741D23F1F34400B8AE7A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 10; + DEVELOPMENT_TEAM = 56C2L92EKW; + ENABLE_HARDENED_RUNTIME = YES; + EXCLUDED_ARCHS = ""; + "EXCLUDED_ARCHS[sdk=*]" = ""; + INFOPLIST_FILE = "com.davidwernhart.Helper/Helper-Info.plist"; + MACOSX_DEPLOYMENT_TARGET = 11.1; + MARKETING_VERSION = 4.3; + OTHER_LDFLAGS = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(SRCROOT)/com.davidwernhart.Helper/Helper-Info.plist", + "-sectcreate", + __TEXT, + __launchd_plist, + "$(SRCROOT)/com.davidwernhart.Helper/Helper-Launchd.plist", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.davidwernhart.Helper; + PRODUCT_MODULE_NAME = AlDenteHelper; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 9283741E23F1F34400B8AE7A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 10; + DEVELOPMENT_TEAM = 56C2L92EKW; + ENABLE_HARDENED_RUNTIME = YES; + EXCLUDED_ARCHS = ""; + INFOPLIST_FILE = "com.davidwernhart.Helper/Helper-Info.plist"; + MACOSX_DEPLOYMENT_TARGET = 11.1; + MARKETING_VERSION = 4.3; + OTHER_LDFLAGS = ( + "-sectcreate", + __TEXT, + __info_plist, + "$(SRCROOT)/com.davidwernhart.Helper/Helper-Info.plist", + "-sectcreate", + __TEXT, + __launchd_plist, + "$(SRCROOT)/com.davidwernhart.Helper/Helper-Launchd.plist", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.davidwernhart.Helper; + PRODUCT_MODULE_NAME = AlDenteHelper; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 92981EE823F08D9C00C05424 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 11.1; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 92981EE923F08D9C00C05424 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 11.1; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 92981EEB23F08D9C00C05424 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 6; + DEVELOPMENT_ASSET_PATHS = "\"AlDente/Preview Content\""; + DEVELOPMENT_TEAM = 56C2L92EKW; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = AlDente/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 11.1; + MARKETING_VERSION = "2.0 Alpha"; + PRODUCT_BUNDLE_IDENTIFIER = com.davidwernhart.AlDente; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 92981EEC23F08D9C00C05424 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 6; + DEVELOPMENT_ASSET_PATHS = "\"AlDente/Preview Content\""; + DEVELOPMENT_TEAM = 56C2L92EKW; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = AlDente/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 11.1; + MARKETING_VERSION = "2.0 Alpha"; + PRODUCT_BUNDLE_IDENTIFIER = com.davidwernhart.AlDente; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 9283741C23F1F34400B8AE7A /* Build configuration list for PBXNativeTarget "com.davidwernhart.Helper" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9283741D23F1F34400B8AE7A /* Debug */, + 9283741E23F1F34400B8AE7A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 92981ED223F08D9B00C05424 /* Build configuration list for PBXProject "AlDente" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 92981EE823F08D9C00C05424 /* Debug */, + 92981EE923F08D9C00C05424 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 92981EEA23F08D9C00C05424 /* Build configuration list for PBXNativeTarget "AlDente" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 92981EEB23F08D9C00C05424 /* Debug */, + 92981EEC23F08D9C00C05424 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 505E96C72586211600DAA405 /* XCRemoteSwiftPackageReference "LaunchAtLogin" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/sindresorhus/LaunchAtLogin"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 4.0.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 505E96C82586211600DAA405 /* LaunchAtLogin */ = { + isa = XCSwiftPackageProductDependency; + package = 505E96C72586211600DAA405 /* XCRemoteSwiftPackageReference "LaunchAtLogin" */; + productName = LaunchAtLogin; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 92981ECF23F08D9B00C05424 /* Project object */; +} diff --git a/AlDente.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/AlDente.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/AlDente.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/AlDente.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/AlDente.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/AlDente.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/AlDente.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/AlDente.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..c88b7a8 --- /dev/null +++ b/AlDente.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,16 @@ +{ + "object": { + "pins": [ + { + "package": "LaunchAtLogin", + "repositoryURL": "https://github.com/sindresorhus/LaunchAtLogin", + "state": { + "branch": null, + "revision": "0f39982b9d6993eef253b81219d3c39ba1e680f3", + "version": "4.0.0" + } + } + ] + }, + "version": 1 +} diff --git a/AlDente.xcodeproj/project.xcworkspace/xcuserdata/davidwernhart.xcuserdatad/UserInterfaceState.xcuserstate b/AlDente.xcodeproj/project.xcworkspace/xcuserdata/davidwernhart.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..a165139eca7ef9c9c2c4d9847210240cbaf60ee5 GIT binary patch literal 73057 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

C3yx%#*`Gj+& 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 J{{NEv{{Y_VZ|VR5 literal 0 HcmV?d00001 diff --git a/AlDente.xcodeproj/xcuserdata/davidwernhart.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/AlDente.xcodeproj/xcuserdata/davidwernhart.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..db78504 --- /dev/null +++ b/AlDente.xcodeproj/xcuserdata/davidwernhart.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,24 @@ + + + + + + + + + diff --git a/AlDente.xcodeproj/xcuserdata/davidwernhart.xcuserdatad/xcschemes/xcschememanagement.plist b/AlDente.xcodeproj/xcuserdata/davidwernhart.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..fd047e3 --- /dev/null +++ b/AlDente.xcodeproj/xcuserdata/davidwernhart.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,39 @@ + + + + + SchemeUserState + + AlDente Pro.xcscheme_^#shared#^_ + + orderHint + 2 + + AlDente copy.xcscheme_^#shared#^_ + + orderHint + 2 + + AlDente.xcscheme_^#shared#^_ + + orderHint + 1 + + Hall.xcscheme_^#shared#^_ + + orderHint + 0 + + Helper.xcscheme_^#shared#^_ + + orderHint + 2 + + com.davidwernhart.Helper.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/AlDente/AppDelegate.swift b/AlDente/AppDelegate.swift new file mode 100644 index 0000000..0e141ae --- /dev/null +++ b/AlDente/AppDelegate.swift @@ -0,0 +1,133 @@ +// +// AppDelegate.swift +// AlDente +// +// Created by David Wernhart on 09.02.20. +// Copyright © 2020 David Wernhart. All rights reserved. +// + +import AppKit +import SwiftUI +import LaunchAtLogin +import Foundation +import IOKit.ps +import IOKit.pwr_mgt + +extension ProcessInfo { + /// Returns a `String` representing the machine hardware name or nil if there was an error invoking `uname(_:)` or decoding the response. + /// + /// Return value is the equivalent to running `$ uname -m` in shell. + var machineHardwareName: String? { + var sysinfo = utsname() + let result = uname(&sysinfo) + guard result == EXIT_SUCCESS else { return nil } + let data = Data(bytes: &sysinfo.machine, count: Int(_SYS_NAMELEN)) + guard let identifier = String(bytes: data, encoding: .ascii) else { return nil } + return identifier.trimmingCharacters(in: .controlCharacters) + } +} + +@NSApplicationMain +final class AppDelegate: NSObject, NSApplicationDelegate { + + //var window: NSWindow! + var statusBarItem: NSStatusItem! + var popover: NSPopover! + + func applicationWillTerminate(_ aNotification: Notification) { + Helper.instance.enableSleep() + Helper.instance.enableCharging() + } + + func applicationDidFinishLaunching(_ aNotification: Notification) { + + let contentView = ContentView() + + // Create the popover + let popover = NSPopover() + popover.contentSize = NSSize(width: 400, height: 600) + popover.behavior = .transient + popover.contentViewController = NSHostingController(rootView: contentView) + self.popover = popover + + let statusBar = NSStatusBar.system + statusBarItem = statusBar.statusItem(withLength: NSStatusItem.squareLength) + statusBarItem.button?.image = NSImage(named: "menubaricon")! + statusBarItem.button?.action = #selector(togglePopover(_:)) + + Helper.instance.setPlatformKey() + + Helper.instance.checkHelperVersion{(foundHelper) in + if(foundHelper){ + print("helper found!") + } + else{ + Helper.instance.installHelper() + } + } + + LaunchAtLogin.isEnabled = true + SMCPresenter.shared.loadValue() + + Helper.instance.checkCharging() + + var actionMsg:String? + + Timer.scheduledTimer(withTimeInterval: 5, repeats: true) { timer in + if(Helper.instance.isInitialized){ + Helper.instance.getChargingInfo { (Name, Capacity, IsCharging, MaxCapacity) in + + + if(!PersistanceManager.instance.oldKey){ + if(Capacity < SMCPresenter.shared.value){ + actionMsg = "NEED TO CHARGE" + if(Helper.instance.chargeInhibited){ + Helper.instance.enableCharging() + } + Helper.instance.disableSleep() + + } + else{ + actionMsg = "IS PERFECT" + if(!Helper.instance.chargeInhibited){ + Helper.instance.disableCharging() + } + Helper.instance.enableSleep() + + } + print("TARGET: ",SMCPresenter.shared.value, + " CURRENT: ",String(Capacity), + " ISCHARGING: ",String(IsCharging), + " CHARGE INHIBITED: ",String(Helper.instance.chargeInhibited), + " ACTION: ",actionMsg!) + } + else{ + print("BCLM MODE ENABLED") + } + + + + } + DispatchQueue.main.async { + Helper.instance.setStatusString() + } + + } + } + + } + + @objc func togglePopover(_ sender: AnyObject?) { + popover.contentViewController?.view.window?.becomeKey() + + Helper.instance.setStatusString() + if let button = self.statusBarItem.button { + if popover.isShown { + popover.performClose(sender) + } else { + popover.show(relativeTo: button.bounds, of: button, preferredEdge: .minY) + } + } + } + +} diff --git a/AlDente/Assets.xcassets/AppIcon.appiconset/Contents.json b/AlDente/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..5666a15 --- /dev/null +++ b/AlDente/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,59 @@ +{ + "images" : [ + { + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "filename" : "spaghetti.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/AlDente/Assets.xcassets/AppIcon.appiconset/spaghetti.png b/AlDente/Assets.xcassets/AppIcon.appiconset/spaghetti.png new file mode 100644 index 0000000000000000000000000000000000000000..ed07e6082d010ce9997018c19e38fa7e3867b4ad GIT binary patch literal 74592 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 PKzQ`Dj5Mq6J4OEwXPHD? literal 0 HcmV?d00001 diff --git a/AlDente/Assets.xcassets/Contents.json b/AlDente/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/AlDente/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/AlDente/Assets.xcassets/menubaricon.imageset/Contents.json b/AlDente/Assets.xcassets/menubaricon.imageset/Contents.json new file mode 100644 index 0000000..401a362 --- /dev/null +++ b/AlDente/Assets.xcassets/menubaricon.imageset/Contents.json @@ -0,0 +1,54 @@ +{ + "images" : [ + { + "filename" : "aldentelighticon1x.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "aldentedarkicon1x.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "aldentelighticon2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "aldentedarkicon2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/AlDente/Assets.xcassets/menubaricon.imageset/aldentedarkicon1x.png b/AlDente/Assets.xcassets/menubaricon.imageset/aldentedarkicon1x.png new file mode 100644 index 0000000000000000000000000000000000000000..a2845396be518f705a1b881aa1152bdc43f87280 GIT binary patch literal 2202 zcmZ`)4LDTk9zQcv(&Rge**3>Ci+2z6 z$;#fP6`{0OE~%-ubjjVK#m$y(tR^m!rnWtU%J$qn=Xu}vcmD71_y2g`|MQ$2w!4!$ z8iNJ^K;6Zec@SQ8ltWboo_+4-`vL%>h|gfKT^J0IEr7!LQ9J-JEle)h=Zf;wl6Y7S ztjjSy%H_6j+76Jf$?oACRGF$4>7JVw3%BA<^Aa-9rRh?~?yF9Z8=G71TS|TaO>;q? z-;qb8na5({zwjJLyM4TJ-<||HqJw4c?b-ygebmhL@@9nPiDPdw-8&xj=qIgr@$J18 zhSWW>^taCcTBqEh)NhOj&qBWIFrtTpE0I~-8!G}%_>LXS<>kll18wuDL>+CNCfK8i~0|8W2 z>MM18FF%nV%Z(ZeMdP_)PzVn%j*eBT0GgNz!)Ts}3yPzoVuVz&4R)D9g|X61z=F#Z z(U&$@KR87OB;bL2@nk#^OGkqsNE3vGQx7utugc*s8|+b$D3(efBqSu@6ZYUCK?K2q zLZJ|dBm#+K4l~S!i7_Is*gQtKW5vmPKTMu5RKSlF@u3({>6aS<9TVALvC2Rno|QaB z{P2&FVuY(@!37eOI|K_nk?{XyJTd<-WXhetl)-28p{zdz6LVu%M9YV^(C-*+D!tupHH764F87pA?JwYK!oKiogF*R5(VyPoQYvc#)q zKS~wjKqCii2==;TML!$F)RmlZxuMBE`&CIgGRIK0j$x@j1X5IYrGZRwW|r0#k69Je z%cLumh&q-QpLB0}dW5og$7XR%``5AN-M8cZg?p_~ylau>=O^`!&-}tRov#!xbQRZ{ z`WH}c$YfrJ9|TspfA{nK`!Dr+%lg7m_53cG8ZZYm?kc|3xxp!@B_|fpxQ;P2)ZC({ zmz+9UTwHtz)$DxspsihPh;hr2?ypA2#$@}b)Rckht`k^{fdTiG@y+erB*X5Xo@@o}z+`OO>O4G#}X#I)aw6blOrx#i^sRqtMMaPuvTB#fsh8P~~?rJ=D?v0CvfhV?5EXgp{xYpBSAD)sjmbT5* z`GU2r?N*H}MzcCQvazGXIxsM>5e!(EpZDqrYT_g(zpWD-KYp$^akEv@#Y>k4U0q!R zf`h-AQM^sJ`@0UPRbc#WtyEzL7O0e;1oA!o|Cw9uJ}+vt1MRCvoQ+8#ga4E#XQ%OsuSY zeFxfwZo3)yojbLqQmMd(Mk@(xv$)*hJ%bG~&_?fI+k5)>$XY4o)9vl;5Mbjt3B|eM zyU{(k8n>d7($b&INWLA{ko2ENMy4MQ4ZRxc@9#HboUW*ta*)J#1``a8HgRg^RVoXN z2RpmEER6=%QL3}Y=ax$9>x~~wO-;GubS8@)K97-4P7Z{&3~3#xrcx*^1#Z7ReJV>2 z`|a7Yn^zqL6Wd?lCAYHz0s=;GMp-4YYPme7SeBNSmRnI#VRbZZt8Z2Kp)-OxL&K2CP{s+=PNVrYrdbyJ6951gM|WnE11J3tFm*?k literal 0 HcmV?d00001 diff --git a/AlDente/Assets.xcassets/menubaricon.imageset/aldentedarkicon2x.png b/AlDente/Assets.xcassets/menubaricon.imageset/aldentedarkicon2x.png new file mode 100644 index 0000000000000000000000000000000000000000..22701c938aac5e2a4619b1e9f227141a74752510 GIT binary patch literal 3737 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 literal 0 HcmV?d00001 diff --git a/AlDente/Assets.xcassets/menubaricon.imageset/aldentelighticon2x.png b/AlDente/Assets.xcassets/menubaricon.imageset/aldentelighticon2x.png new file mode 100644 index 0000000000000000000000000000000000000000..266e770cce088856864aceee10e1d6858a4df990 GIT binary patch literal 3353 zcmZ`*2{@Ep8-B-5M1!=+3`5qLFg((4`oHsD=bZa}&V4`UdCqyR_j;quO!Ni#cJToKAb>Z}vtZ6J zR_Ebjj_-iXOaK5SlW;gQJPrpjqf*>SWFi2_CIuxn89gu)X>Bfjwf*vesgV((8ccn| z%SYV}x^h;s^ej-Qr@NLCzcM6ZWUL}5%Q^%a+_$S0dy+PEZT9Wmn zy*G0uhP5UK{Ix^+!B+L7e4uMCp40Tyf@d8|A3bOfI0eIa_%2o*wzv$Fy}Q)OxEUV) zJ_pEIUb*Y~*)Y(7V~^%yak&@QB6x8jx(GD{+1uqQH|o%OD5i7Aq+*r2H)?vvqLMNq zog<17*M~?~**Vg8rT}JC>~FbUA=W6?-{;OFwRD9e-9stsT{$-H9=2L}(1%GaDi$M2 zI8mb2F~~tX=pp^=kCzSyzdH83mUzNgLClCt0vh@0jPAC!Z)XmO$P8xT;;!23M)(Cy4ix<<6cyBiw=@=VMFaD%`AI}?GJ4*=Q)aDK-C-~!tH3wwcNe{$HE z9A$tx0s_oV1);uU5hf0aBmo>hwW-XR)eM-O>Gj8~k2f}hpy0|#xUvcWppYme2C0TY zsz8tjFe<7TWh4Nw6|(*E0V`zx2eX{rkNh&qoOt~V?EC>>i!iH$fb85IEFHQDIECtgP)4KC2qX%D zLMbsBO8$YqG-tY!ufNm}Cx83VBl^2iNq#gE#TUZz>+C`~LDQ0yWNq}T{fLuBa{p&1 zU;m$JF%v|vRuIZ?B;xSSN+L3| zWaWZY*8CpTzij{H^n-6f^rw(du;@p8Ni;0#r{oXwZz@*67#xL6p<4PmyAoNO{Av1) z&CCE}OmQQ*2kJS~h*(yEDIwLAkSbPx7nmmE56&NI9F^!yqfo6V6f*YrYW<=6ef6hM z6T!N}-|qBB!F;zeD+bHQRQ_FkSiY8ozGVR5et_50v9jf@&$Pdg_e!*_c`0vyh8q>C z1TBf)Nh7qvV8y%wUg73eX4|$%yXY0K%3`DIyRdaUKBS@uECY6Lp9I?xp%>V4!tId= z-Z8?c+XdVnWyfsg2)ui8R(s>;m45%;*C}J;u~^!@{Q8x@ysJ0QR^Mq}5AwJ+yNQs7 zJU#tob{gY1aq}3Ge`BFDu*UVoIv7&>%otp zW8#?Wyt-=|kob9ubj{71nV^{&ZC%0sse*G4Jv?tr%TMdcCcUY3ZF|tp5S?>3NX0W9 z3AO}H(XdmKWF3s9jnJ%A;#x?bVE3gAgj0TQc6N4KaGK(c=XpHS-sHhs%WZ>$g9%9t za~`FTHOm$8T57AJZu#zo>RN40ZEfw76YW_$+20oPhn{Og`}rb*@AayJ&jVRz5s{Y}|6-cIBxwc zucoic3)$%t6bYjlH9_GUsr%LW-UPrMZhhXn(h*2D#4Nm)Y}iw@2Ng)@YYLbTf(0gr zpGIU1U@B6S98`}U*3xo3wUZ+~F{c+QqDUHyEG(TT*k z44B?W?x{fVyc&8XMsHVQxn&+|43luT#n4bznMZA*tkt%3RiRa$p}%9(ola+zk69Di zIE!S)Dkx*iA6`qyOhc{5a-#{+uJpn-}*+Oz79e{h2r4FptUbPVeLDB$i94eUN51g+`ikQ!cl!-szn!}~rVLm{VEV%cqso)-=Qv_XacMLmWG^~jN1?=2#|COsG&i|%YTw6%w!9EaeCS4K zU{Y7DaxyuN*TQ+r#dx39w3;%|*}5iA7)bZq@m0>Ef~ipA$7ufaSYdLo8>b(@VXILy zCS#`4nme)7&8=Im`*>1mo*u<6Uj0p#?VW5OL!>QzSWkv`WMZ66aaL!j)<5Sf7`>Jp zwd8h1Eh6~Tsp+cYv*}5fl#N5?P+|t$Ji}dyxY*y4Yu6ygXS0nL4|nBmb>}ac)bsgnk?c zQCRjW2irh#*u#oB?hs4`Dfe7zFS=vDw%eJ!)JA&Q%^eh(D>FF`*gtXs~klIJ;^+hyg5%qeTt;H=Gw#Lk@bI(kSRMw#^9F9>R7=d# zwv?K(s)_;t!eVRp}lJ`ycnpoW=kE literal 0 HcmV?d00001 diff --git a/AlDente/Base.lproj/Main.storyboard b/AlDente/Base.lproj/Main.storyboard new file mode 100644 index 0000000..a6ad7ba --- /dev/null +++ b/AlDente/Base.lproj/Main.storyboard @@ -0,0 +1,88 @@ + + + + + + + + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AlDente/ContentView.swift b/AlDente/ContentView.swift new file mode 100644 index 0000000..c75a2a0 --- /dev/null +++ b/AlDente/ContentView.swift @@ -0,0 +1,259 @@ +// +// ContentView.swift +// AlDente +// +// Created by David Wernhart on 09.02.20. +// Copyright © 2020 David Wernhart. All rights reserved. +// + +import LaunchAtLogin +import SwiftUI + +private struct BlueButtonStyle: ButtonStyle { + func makeBody(configuration: Self.Configuration) -> some View { + configuration.label + .foregroundColor(configuration.isPressed ? Color.blue : Color.white) + .background(configuration.isPressed ? Color.white : Color.blue) + .cornerRadius(6.0) + .padding() + } +} + +private struct RedButtonStyle: ButtonStyle { + func makeBody(configuration: Self.Configuration) -> some View { + configuration.label + .foregroundColor(configuration.isPressed ? Color.red : Color.white) + .background(configuration.isPressed ? Color.white : Color.red) + .cornerRadius(6.0) + .padding() + } +} + +private var allowDischarge = true + +private struct Settings: View { + @State private var launchAtLogin = LaunchAtLogin.isEnabled + @State private var oldKey = PersistanceManager.instance.oldKey + @ObservedObject private var presenter = SMCPresenter.shared + + var body: some View { + VStack { + Spacer().frame(height: 15) + Text(presenter.status) + HStack { + VStack(alignment: .leading) { + Toggle(isOn: Binding( + get: { launchAtLogin }, + + set: { newValue in + launchAtLogin = newValue + print("Launch at login turned \(newValue ? "on" : "off")!") + LaunchAtLogin.isEnabled = newValue + } + )) { + Text("Launch at login") + } + + if(!Helper.instance.appleSilicon!){ + Toggle(isOn: Binding( + get: { oldKey }, + + set: { newValue in + oldKey = newValue + PersistanceManager.instance.oldKey = oldKey + PersistanceManager.instance.save() + Helper.instance.setStatusString() + if(newValue){ + Helper.instance.enableCharging() + Helper.instance.enableSleep() + //presenter.writeValue() + } + else{ + presenter.setValue(value: 100) + } + } + )) { + Text("Use Classic SMC Key (Intel)") + } + } + }.padding() + + Spacer() + + Button(action: { + Helper.instance.installHelper() + }) { + Text("Reinstall Helper") + .frame(maxWidth: 120, maxHeight: 30) + }.buttonStyle(BlueButtonStyle()) + } + + HStack { + Spacer().frame(width: 15) + VStack(alignment: .leading) { + let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String + Text("AlDente \(version ?? "") 🍝").font(.headline) + + let address = "github.com/davidwernhart/AlDente" + Button(action: { + openURL("https://" + address) + }) { + Text(address).foregroundColor(Color(.linkColor)) + }.buttonStyle(PlainButtonStyle()) + + Text("Cooked up in 2021 by AppHouseKitchen") +// Text("AlDente 🍝").font(.title) +// Text("Keep your battery just right").font(.subheadline) + } + + Spacer() + + Button(action: { + openURL("https://apphousekitchen.com/aldente/") + }) { + Text("Get Pro 🍜") + .frame(maxWidth: 100, maxHeight: 50) + } + .buttonStyle(BlueButtonStyle()) + } + } + .background(Color(.unemphasizedSelectedContentBackgroundColor)) + .cornerRadius(5) + } + + private func openURL(_ string: String) { + let url = URL(string: string)! + if NSWorkspace.shared.open(url) { + print("default browser was successfully opened") + } + } +} + +struct ContentView: View { + @State private var adaptableHeight = CGFloat(100) + @State private var showSettings = false + + @ObservedObject private var presenter = SMCPresenter.shared + + init() { + Helper.instance.delegate = presenter + } + + var body: some View { + VStack(alignment: .leading) { + + HStack { + Text("Max. Battery Charge:").padding(.leading) + TextField("Number", value: Binding( + get: { + Float(presenter.value) + }, + set: { newValue in + if newValue >= 20 && newValue <= 100 { + presenter.setValue(value: newValue) + } + } + ), formatter: NumberFormatter()) + .multilineTextAlignment(.center) + .frame(maxWidth: 50) + .textFieldStyle(RoundedBorderTextFieldStyle()) + + Spacer() + + Button(action: { + showSettings.toggle() + adaptableHeight = showSettings ? 275 : 100 + }) { + Text("Settings") + .frame(maxWidth: 70, maxHeight: 30) + }.buttonStyle(BlueButtonStyle()).padding(.leading, -30) + + Button(action: { + NSApplication.shared.terminate(self) + }) { + Text("Quit") + .frame(maxWidth: 50, maxHeight: 30) + }.buttonStyle(RedButtonStyle()).padding(.leading, -30) + } + + Slider(value: Binding( + get: { + Float(presenter.value) + }, + set: { newValue in + if newValue >= 20 && newValue <= 100 { + presenter.setValue(value: newValue) + } + } + ), in: 20...100).padding(.horizontal).padding(.top, -20) + + Spacer() + + if showSettings { + Settings() + } + + }.frame(width: 400, height: adaptableHeight) + + } +} + +public final class SMCPresenter: ObservableObject, HelperDelegate { + + static let shared = SMCPresenter() + + @Published var value: UInt8 = 0 + @Published var status: String = "" + private var timer: Timer? + private var accuracyTimer: Timer? + + func OnMaxBatRead(value: UInt8) { + if(PersistanceManager.instance.oldKey){ + DispatchQueue.main.async { + self.value = value + } + } + } + + func updateStatus(status:String){ + DispatchQueue.main.async { + self.status = status + } + } + + public func loadValue(){ + PersistanceManager.instance.load() + self.value = UInt8(PersistanceManager.instance.chargeVal!) + if(self.value == 0){ + self.value = 50 + } + print("loaded max charge val: ",self.value," old key:",PersistanceManager.instance.oldKey) +// if(!Helper.instance.appleSilicon!){ +// Helper.instance.getSMCCharge(withReply: { (smcval) in +// self.value = UInt8(smcval) +// }) +// } + if(PersistanceManager.instance.oldKey){ + writeValue() + } + } + + func setValue(value: Float) { + DispatchQueue.main.async { + self.value = UInt8(value) + PersistanceManager.instance.chargeVal = Int(value) + PersistanceManager.instance.save() + self.writeValue() + } + timer?.invalidate() + accuracyTimer?.invalidate() + } + + func writeValue(){ + if(PersistanceManager.instance.oldKey){ + print("should write bclm value: ", self.value) + Helper.instance.writeMaxBatteryCharge(setVal: self.value) + } + } + +} diff --git a/AlDente/Helper.swift b/AlDente/Helper.swift new file mode 100644 index 0000000..ec9751e --- /dev/null +++ b/AlDente/Helper.swift @@ -0,0 +1,293 @@ +// +// Helper.swift +// AlDente +// +// Created by David Wernhart on 14.02.20. +// Copyright © 2020 David Wernhart. All rights reserved. +// + +import Foundation +import ServiceManagement +import IOKit.pwr_mgt + +protocol HelperDelegate { + func OnMaxBatRead(value: UInt8) + func updateStatus(status:String) +} + +final class Helper { + + static let instance = Helper() + + public var delegate: HelperDelegate? + + private var key: String? + + private var preventSleepID: IOPMAssertionID? + + public var appleSilicon:Bool? + public var chargeInhibited: Bool = false + public var isInitialized:Bool = false + + public var statusString:String = "" + + + lazy var helperToolConnection: NSXPCConnection = { + let connection = NSXPCConnection(machServiceName: "com.davidwernhart.Helper.mach", options: .privileged) + connection.remoteObjectInterface = NSXPCInterface(with: HelperToolProtocol.self) + + connection.resume() + return connection + }() + + func setPlatformKey() { + let s:String! = ProcessInfo.init().machineHardwareName + if(s != nil){ + if(s.elementsEqual("x86_64")){ + print("intel cpu!") + appleSilicon = false; + } + else if(s.elementsEqual("arm64")){ + print("arm cpu!") + appleSilicon = true; + } + + } + } + func setStatusString(){ + checkCharging() + var sleepDisabled:Bool = !(preventSleepID == nil) + statusString = "" + if(PersistanceManager.instance.oldKey){ + statusString = "BCLM Key Mode. Final charge value can differ by up to 5%" + } + else{ + statusString = "Charge Inhibit: "+String(chargeInhibited)+" | Prevent Sleep: "+String(sleepDisabled)+" | Helper v"+String(helperVersion)+": \(self.isInitialized ? "found" : "not found")" + } + + + self.delegate?.updateStatus(status: statusString) + } + + + func enableSleep(){ + if(self.preventSleepID != nil){ + print("RELEASING PREVENT SLEEP ASSERTION WITH ID: ",preventSleepID!) + releaseAssertion(assertionId: self.preventSleepID!) + self.preventSleepID = nil + } + } + + func disableSleep(){ + createAssertion(assertion: kIOPMAssertionTypePreventSystemSleep){ id in + if(self.preventSleepID == nil){ + print("PREVENT SLEEP ASSERTION CREATED! ID: ",id) + self.preventSleepID = id + } + } + } + + func enableCharging(){ + if(appleSilicon!){ + SMCWriteByte(key: "CH0B", value: 00) + } + SMCWriteByte(key: "CH0B", value: 00) + self.chargeInhibited = false + + } + + func disableCharging(){ + if(appleSilicon!){ + SMCWriteByte(key: "CH0B", value: 02) + } + SMCWriteByte(key: "CH0B", value: 02) + self.chargeInhibited = true + + } + + func checkCharging(){ + Helper.instance.SMCReadUInt32(key: "CH0B") { value in + self.chargeInhibited = !(value == 00) + print("CHARGE INHIBITED: "+String(self.chargeInhibited)) + } + if(PersistanceManager.instance.oldKey){ + Helper.instance.readMaxBatteryCharge() + } + + } + + func getChargingInfo(withReply reply: (String,Int,Bool,Int) -> Void){ + let snapshot = IOPSCopyPowerSourcesInfo().takeRetainedValue() + let sources = IOPSCopyPowerSourcesList(snapshot).takeRetainedValue() as Array + let info = IOPSGetPowerSourceDescription(snapshot, sources[0]).takeUnretainedValue() as! [String: AnyObject] + + if let name = info[kIOPSNameKey] as? String, + let capacity = info[kIOPSCurrentCapacityKey] as? Int, + let isCharging = info[kIOPSIsChargingKey] as? Bool, + let max = info[kIOPSMaxCapacityKey] as? Int { + reply(name,capacity,isCharging,max) + } + } + + func getSMCCharge(withReply reply: @escaping (Float)->Void){ + Helper.instance.SMCReadUInt32(key: "BRSC") { value in + let smcval = Float(value >> 16) + reply(smcval) + } + } + + @objc func createAssertion(assertion: String, withReply reply: @escaping (IOPMAssertionID) -> Void){ + let helper = helperToolConnection.remoteObjectProxyWithErrorHandler { + let e = $0 as NSError + print("Remote proxy error \(e.code): \(e.localizedDescription) \(e.localizedRecoverySuggestion ?? "---")") + } as? HelperToolProtocol + + helper?.createAssertion(assertion: assertion, withReply: { id in + reply(id) + }) + } + + @objc func releaseAssertion(assertionId: IOPMAssertionID){ + let helper = helperToolConnection.remoteObjectProxyWithErrorHandler { + let e = $0 as NSError + print("Remote proxy error \(e.code): \(e.localizedDescription) \(e.localizedRecoverySuggestion ?? "---")") + } as? HelperToolProtocol + + helper?.releaseAssertion(assertionID: assertionId) + } + + @objc func installHelper() { + print("trying to install helper!") + var status = noErr + let helperID = "com.davidwernhart.Helper" as CFString // Prefs.helperID as CFString + + var authItem = kSMRightBlessPrivilegedHelper.withCString { + AuthorizationItem(name: $0, valueLength: 0, value: nil, flags: 0) + } + var authRights = withUnsafeMutablePointer(to: &authItem) { + AuthorizationRights(count: 1, items: $0) + } + let authFlags: AuthorizationFlags = [.interactionAllowed, .preAuthorize, .extendRights] + var authRef: AuthorizationRef? + status = AuthorizationCreate(&authRights, nil, authFlags, &authRef) + if status != errAuthorizationSuccess { + print(SecCopyErrorMessageString(status, nil) ?? "") + print("Error: \(status)") + } + + var error: Unmanaged? + SMJobBless(kSMDomainSystemLaunchd, helperID, authRef, &error) + if let e = error?.takeRetainedValue() { + print("Domain: ", CFErrorGetDomain(e) ?? "") + print("Code: ", CFErrorGetCode(e)) + print("UserInfo: ", CFErrorCopyUserInfo(e) ?? "") + print("Description: ", CFErrorCopyDescription(e) ?? "") + print("Reason: ", CFErrorCopyFailureReason(e) ?? "") + print("Suggestion: ", CFErrorCopyRecoverySuggestion(e) ?? "") + } + + if(error == nil){ + print("helper installed successfully!") + restart() + } + } + + func restart(){ + let url = URL(fileURLWithPath: Bundle.main.resourcePath!) + let path = url.deletingLastPathComponent().deletingLastPathComponent().absoluteString + let task = Process() + task.launchPath = "/usr/bin/open" + task.arguments = [path] + task.launch() + exit(0) + } + + @objc func setResetValues(){ + let helper = helperToolConnection.remoteObjectProxyWithErrorHandler { + let e = $0 as NSError + print("Remote proxy error \(e.code): \(e.localizedDescription) \(e.localizedRecoverySuggestion ?? "---")") + + } as? HelperToolProtocol + + helper?.setResetVal(key: "CH0B", value: 00) + } + + @objc func writeMaxBatteryCharge(setVal: UInt8) { + SMCWriteByte(key: "BCLM", value: setVal) + + } + + @objc func readMaxBatteryCharge() { + SMCReadByte(key: "BCLM") { value in + print("OLD KEY MAX CHARGE: "+String(value)) + self.delegate?.OnMaxBatRead(value: value) + } + } + + @objc func enableCharging(enabled: Bool) { + if(enabled){ + SMCWriteByte(key: "CH0B", value: 00) + } + else{ + SMCWriteByte(key: "CH0B", value: 02) + } + + } + + @objc func checkHelperVersion(withReply reply: @escaping (Bool) -> Void) { + print("checking helper version") + let helper = helperToolConnection.remoteObjectProxyWithErrorHandler { + let e = $0 as NSError + print("Remote proxy error \(e.code): \(e.localizedDescription) \(e.localizedRecoverySuggestion ?? "---")") + reply(false) + return() + + } as? HelperToolProtocol + + helper?.getVersion { version in + print("helperVersion:", helperVersion, " version from helper:", version) + if !helperVersion.elementsEqual(version) { + reply(false) + return() } + else{ + self.isInitialized = true + reply(true) + return() + } + } + } + + @objc func SMCReadByte(key: String, withReply reply: @escaping (UInt8) -> Void) { + let helper = helperToolConnection.remoteObjectProxyWithErrorHandler { + let e = $0 as NSError + print("Remote proxy error \(e.code): \(e.localizedDescription) \(e.localizedRecoverySuggestion ?? "---")") + + } as? HelperToolProtocol + + helper?.readSMCByte(key: key) { + reply($0) + } + } + + @objc func SMCReadUInt32(key: String, withReply reply: @escaping (UInt32) -> Void) { + let helper = helperToolConnection.remoteObjectProxyWithErrorHandler { + let e = $0 as NSError + print("Remote proxy error \(e.code): \(e.localizedDescription) \(e.localizedRecoverySuggestion ?? "---")") + + } as? HelperToolProtocol + + helper?.readSMCUInt32(key: key) { + reply($0) + } + } + + @objc func SMCWriteByte(key: String, value: UInt8) { + let helper = helperToolConnection.remoteObjectProxyWithErrorHandler { + let e = $0 as NSError + print("Remote proxy error \(e.code): \(e.localizedDescription) \(e.localizedRecoverySuggestion ?? "---")") + + } as? HelperToolProtocol + + helper?.setSMCByte(key: key, value: value) + } +} diff --git a/AlDente/Info.plist b/AlDente/Info.plist new file mode 100644 index 0000000..27baca2 --- /dev/null +++ b/AlDente/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + LSApplicationCategoryType + public.app-category.developer-tools + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + LSUIElement + + NSHumanReadableCopyright + Copyright © 2020 David Wernhart. All rights reserved. + NSMainStoryboardFile + Main + NSPrincipalClass + NSApplication + NSSupportsAutomaticTermination + + NSSupportsSuddenTermination + + SMPrivilegedExecutables + + com.davidwernhart.Helper + identifier "com.davidwernhart.Helper" and anchor apple generic and certificate leaf[subject.CN] = "Apple Development: david.wernhart96@gmail.com (GSDX9BQ584)" and certificate 1[field.1.2.840.113635.100.6.2.1] /* exists */ + + + diff --git a/AlDente/PersistanceManager.swift b/AlDente/PersistanceManager.swift new file mode 100644 index 0000000..135816a --- /dev/null +++ b/AlDente/PersistanceManager.swift @@ -0,0 +1,29 @@ +// +// PersistanceManager.swift +// AlDente +// +// Created by David Wernhart on 07.02.21. +// Copyright © 2021 David Wernhart. All rights reserved. +// + +import Foundation + +class PersistanceManager{ + static let instance = PersistanceManager() + + public var launchOnLogin: Bool? + public var chargeVal: Int? + public var oldKey: Bool = false + + public func load(){ + launchOnLogin = UserDefaults.standard.bool(forKey: "launchOnLogin") + oldKey = UserDefaults.standard.bool(forKey: "oldKey") + chargeVal = UserDefaults.standard.integer(forKey: "chargeVal") + } + + public func save(){ + UserDefaults.standard.set(launchOnLogin, forKey: "launchOnLogin") + UserDefaults.standard.set(chargeVal, forKey: "chargeVal") + UserDefaults.standard.set(oldKey, forKey: "oldKey") + } +} diff --git a/AlDente/Preview Content/Preview Assets.xcassets/Contents.json b/AlDente/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/AlDente/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Common/HelperToolProtocol.swift b/Common/HelperToolProtocol.swift new file mode 100644 index 0000000..258fb74 --- /dev/null +++ b/Common/HelperToolProtocol.swift @@ -0,0 +1,27 @@ +// +// HelperToolProtocol.swift +// AlDente +// +// Created by David Wernhart on 13.02.20. +// Copyright © 2020 David Wernhart. All rights reserved. +// + +import Foundation +import IOKit.pwr_mgt + +let helperVersion: String = "10" //for some reason the integrated version check does not work, so I use this one + +@objc(HelperToolProtocol) protocol HelperToolProtocol { +//protocol HelperToolProtocol { + func getVersion(withReply reply: @escaping (String) -> Void) + + //TODO: more functions for other data types, altough this is sufficient for battery max charge level + func setSMCByte(key: String, value: UInt8) + func readSMCByte(key: String, withReply reply: @escaping (UInt8) -> Void) + func readSMCUInt32(key: String, withReply reply: @escaping (UInt32) -> Void) + + func createAssertion(assertion:String, withReply reply: @escaping (IOPMAssertionID) -> Void) + func releaseAssertion(assertionID:IOPMAssertionID) + func setResetVal(key:String, value: UInt8) + +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..915d539 --- /dev/null +++ b/README.md @@ -0,0 +1,85 @@ +# AlDente 🍝 +_macOS menu bar tool to limit maximum charging percentage_ + +#### Don't overcook your battery! Keep it fresh and chewy with AlDente. + +## AlDente for Apple Silicon is coming soon! + +## Why do I need this? +Li-ion and polymer batteries (like the one in your MacBook) last the longest when operating between 30 and 80 percent. Keeping your battery at 100% at all times can shorten the lifespan of your notebook significantly. +More information can be found here: + + +## How does it work? +The tool writes the desired value to your MacBooks SMC (System Management Controller), which handles the rest. +For everyone that is curious, the modified SMC key is called "BCLM" (Presumably "Battery Charge Level Max") + +## AlDente is tested and working on pretty much every Intel MacBook from 2011-2020, including: +* 2020 MacBook Pro 13" with 4 TB3 ports +* 2020 Macbook Air 13" (Intel) +* 2019 MacBook Pro 16" +* 2019 MacBook Pro 13" +* 2017 Macbook Pro 13" without TouchBar +* 2017 MacBook Air 13" +* 2017 MacBook 12" +* 2016 MacBook Pro 15" +* 2016 MacBook 12" +* 2015 MacBook 12" +* 2015 MacBook Pro 13" +* 2014 MacBook Air 13" +* 2014 MacBook Peo 13" +* 2013 MacBook Pro 13" +* 2010 MacBook + +## Download: +AlDente is available as a homebrew cask. Install via +``` +brew install aldente +``` + +Alternatively, you can download the app from GitHub: + +## How to use: +Simply extract the latest .zip release and drag the App to your Applications Folder. + +If you get the annoying `"AlDente cannot be opened because the developer cannot be verified"` or `"AlDente.app" can’t be opened because Apple can’t check it for malicious software.` messages on Catalina, simply navigate to your Applications folder using Finder, right click on AlDente and select `Open`. Then you can proceed to start the tool normally. + +On the first start, the application is going to ask you to allow installing a helper tool. This is necessary, since writing SMC Keys requires root privileges. +Once finished, enter your desired max. charging percentage by clicking on the 🍝 icon on your +menu bar. + +For some reason, macOS will always try to squeeze in a few more percent than specified by the SMC. For example, if you set yours to 80% it will stop charging at around 83%, so be patient. Strangely, this is not the case using Windows with bootcamp, therefore I have chosen not to correct this inaccuracy in code for now. + +Usually, the operating system will take a minute or two registering the changes, so be patient. You can check if it's working by setting the max. percentage to e.g.: 70%. After a while, clicking on your battery icon will report "Battery is not charging" if you have more than ≈73% left, even tough your charger is connected. Notice that in this state, your MacBook is still powered by the charger, but the battery is not charging anymore. + +## FAQ +* IT DOES NOT WORK??? + +AlDente does only prevent your Mac from charging more than you specify, it does NOT automatically drain your battery to the specified percentage. +* STILL NOT WORKING :( + +Simply reboot your MacBook! This seems to do the trick for most people. +* IS MACOS BIG SUR SUPPORTED? + +Allthough AlDente was written and compiled in Catalina, it works just fine on MacOS Big Sur. +* HOW TO UNINSTALL? + +Make Sure to set your max. charging percentage back to 100 before moving AlDente to the Trash. If you really want to make sure, you can also reset your Mac's SMC. If you want to also remove the helper tool, take a look at this issue: . +* DO I HAVE TO UNCHECK THE BUILT IN BATTERY HEALTH CHECKBOX? + +Since this checkbox does not seem to do much for most users, many reported that it does not matter, although I would reccomend to uncheck it just to make sure. + +## Other tools I used in this project: +* +* +* + +## Disclaimer: +I do not take any responsibility for any sort of damage in result of using this tool! Alltough this had no negative side effects for me, AlDente still taps in some very low level system functions that are not ment to be tampered with. Use it at your own risk! + +Copyright(c) 2020 David Wernhart +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/SMJobBlessUtil.py b/SMJobBlessUtil.py new file mode 100755 index 0000000..c041363 --- /dev/null +++ b/SMJobBlessUtil.py @@ -0,0 +1,437 @@ +#! /usr/bin/python +# +# File: SMJobBlessUtil.py +# +# Contains: Tool for checking and correcting apps that use SMJobBless. +# +# Written by: DTS +# +# Copyright: Copyright (c) 2012 Apple Inc. All Rights Reserved. +# +# Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. +# ("Apple") in consideration of your agreement to the following +# terms, and your use, installation, modification or +# redistribution of this Apple software constitutes acceptance of +# these terms. If you do not agree with these terms, please do +# not use, install, modify or redistribute this Apple software. +# +# In consideration of your agreement to abide by the following +# terms, and subject to these terms, Apple grants you a personal, +# non-exclusive license, under Apple's copyrights in this +# original Apple software (the "Apple Software"), to use, +# reproduce, modify and redistribute the Apple Software, with or +# without modifications, in source and/or binary forms; provided +# that if you redistribute the Apple Software in its entirety and +# without modifications, you must retain this notice and the +# following text and disclaimers in all such redistributions of +# the Apple Software. Neither the name, trademarks, service marks +# or logos of Apple Inc. may be used to endorse or promote +# products derived from the Apple Software without specific prior +# written permission from Apple. Except as expressly stated in +# this notice, no other rights or licenses, express or implied, +# are granted by Apple herein, including but not limited to any +# patent rights that may be infringed by your derivative works or +# by other works in which the Apple Software may be incorporated. +# +# The Apple Software is provided by Apple on an "AS IS" basis. +# APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING +# WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING +# THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN +# COMBINATION WITH YOUR PRODUCTS. +# +# IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, +# INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY +# OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION +# OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY +# OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR +# OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +import sys +import os +import getopt +import subprocess +import plistlib +import operator + +class UsageException (Exception): + """ + Raised when the progam detects a usage issue; the top-level code catches this + and prints a usage message. + """ + pass + +class CheckException (Exception): + """ + Raised when the "check" subcommand detects a problem; the top-level code catches + this and prints a nice error message. + """ + def __init__(self, message, path=None): + self.message = message + self.path = path + +def checkCodeSignature(programPath, programType): + """Checks the code signature of the referenced program.""" + + # Use the codesign tool to check the signature. The second "-v" is required to enable + # verbose mode, which causes codesign to do more checking. By default it does the minimum + # amount of checking ("Is the program properly signed?"). If you enabled verbose mode it + # does other sanity checks, which we definitely want. The specific thing I'd like to + # detect is "Does the code satisfy its own designated requirement?" and I need to enable + # verbose mode to get that. + + args = [ + # "false", + "codesign", + "-v", + "-v", + programPath + ] + try: + subprocess.check_call(args, stderr=open("/dev/null")) + except subprocess.CalledProcessError, e: + raise CheckException("%s code signature invalid" % programType, programPath) + +def readDesignatedRequirement(programPath, programType): + """Returns the designated requirement of the program as a string.""" + args = [ + # "false", + "codesign", + "-d", + "-r", + "-", + programPath + ] + try: + req = subprocess.check_output(args, stderr=open("/dev/null")) + except subprocess.CalledProcessError, e: + raise CheckException("%s designated requirement unreadable" % programType, programPath) + + reqLines = req.splitlines() + if len(reqLines) != 1 or not req.startswith("designated => "): + raise CheckException("%s designated requirement malformed" % programType, programPath) + return reqLines[0][len("designated => "):] + +def readInfoPlistFromPath(infoPath): + """Reads an "Info.plist" file from the specified path.""" + try: + info = plistlib.readPlist(infoPath) + except: + raise CheckException("'Info.plist' not readable", infoPath) + if not isinstance(info, dict): + raise CheckException("'Info.plist' root must be a dictionary", infoPath) + return info + +def readPlistFromToolSection(toolPath, segmentName, sectionName): + """Reads a dictionary property list from the specified section within the specified executable.""" + + # Run otool -s to get a hex dump of the section. + + args = [ + # "false", + "otool", + "-s", + segmentName, + sectionName, + toolPath + ] + try: + plistDump = subprocess.check_output(args) + except subprocess.CalledProcessError, e: + raise CheckException("tool %s / %s section unreadable" % (segmentName, sectionName), toolPath) + + # Convert that hex dump to an property list. + + plistLines = plistDump.splitlines() + if len(plistLines) < 3 or plistLines[1] != ("Contents of (%s,%s) section" % (segmentName, sectionName)): + raise CheckException("tool %s / %s section dump malformed (1)" % (segmentName, sectionName), toolPath) + del plistLines[0:2] + + try: + bytes = [] + for line in plistLines: + # line looks like this: + # + # '0000000100000b80\t3c 3f 78 6d 6c 20 76 65 72 73 69 6f 6e 3d 22 31 ' + columns = line.split("\t") + assert len(columns) == 2 + for hexStr in columns[1].split(): + bytes.append(int(hexStr, 16)) + plist = plistlib.readPlistFromString(bytearray(bytes)) + except: + raise CheckException("tool %s / %s section dump malformed (2)" % (segmentName, sectionName), toolPath) + + # Check the root of the property list. + + if not isinstance(plist, dict): + raise CheckException("tool %s / %s property list root must be a dictionary" % (segmentName, sectionName), toolPath) + + return plist + +def checkStep1(appPath): + """Checks that the app and the tool are both correctly code signed.""" + + if not os.path.isdir(appPath): + raise CheckException("app not found", appPath) + + # Check the app's code signature. + + checkCodeSignature(appPath, "app") + + # Check the tool directory. + + toolDirPath = os.path.join(appPath, "Contents", "Library", "LaunchServices") + if not os.path.isdir(toolDirPath): + raise CheckException("tool directory not found", toolDirPath) + + # Check each tool's code signature. + + toolPathList = [] + for toolName in os.listdir(toolDirPath): + if toolName != ".DS_Store": + toolPath = os.path.join(toolDirPath, toolName) + if not os.path.isfile(toolPath): + raise CheckException("tool directory contains a directory", toolPath) + checkCodeSignature(toolPath, "tool") + toolPathList.append(toolPath) + + # Check that we have at least one tool. + + if len(toolPathList) == 0: + raise CheckException("no tools found", toolDirPath) + + return toolPathList + +def checkStep2(appPath, toolPathList): + """Checks the SMPrivilegedExecutables entry in the app's "Info.plist".""" + + # Create a map from the tool name (not path) to its designated requirement. + + toolNameToReqMap = dict() + for toolPath in toolPathList: + req = readDesignatedRequirement(toolPath, "tool") + toolNameToReqMap[os.path.basename(toolPath)] = req + + # Read the Info.plist for the app and extract the SMPrivilegedExecutables value. + + infoPath = os.path.join(appPath, "Contents", "Info.plist") + info = readInfoPlistFromPath(infoPath) + if not info.has_key("SMPrivilegedExecutables"): + raise CheckException("'SMPrivilegedExecutables' not found", infoPath) + infoToolDict = info["SMPrivilegedExecutables"] + if not isinstance(infoToolDict, dict): + raise CheckException("'SMPrivilegedExecutables' must be a dictionary", infoPath) + + # Check that the list of tools matches the list of SMPrivilegedExecutables entries. + + if sorted(infoToolDict.keys()) != sorted(toolNameToReqMap.keys()): + raise CheckException("'SMPrivilegedExecutables' and tools in 'Contents/Library/LaunchServices' don't match") + + # Check that all the requirements match. + + # This is an interesting policy choice. Technically the tool just needs to match + # the requirement listed in SMPrivilegedExecutables, and we can check that by + # putting the requirement into tmp.req and then running + # + # $ codesign -v -R tmp.req /path/to/tool + # + # However, for a Developer ID signed tool we really want to have the SMPrivilegedExecutables + # entry contain the tool's designated requirement because Xcode has built a + # more complex DR that does lots of useful and important checks. So, as a matter + # of policy we require that the value in SMPrivilegedExecutables match the tool's DR. + + for toolName in infoToolDict: + if infoToolDict[toolName] != toolNameToReqMap[toolName]: + raise CheckException("tool designated requirement (%s) doesn't match entry in 'SMPrivilegedExecutables' (%s)" % (toolNameToReqMap[toolName], infoToolDict[toolName])) + +def checkStep3(appPath, toolPathList): + """Checks the "Info.plist" embedded in each helper tool.""" + + # First get the app's designated requirement. + + appReq = readDesignatedRequirement(appPath, "app") + + # Then check that the tool's SMAuthorizedClients value matches it. + + for toolPath in toolPathList: + info = readPlistFromToolSection(toolPath, "__TEXT", "__info_plist") + + if not info.has_key("CFBundleInfoDictionaryVersion") or info["CFBundleInfoDictionaryVersion"] != "6.0": + raise CheckException("'CFBundleInfoDictionaryVersion' in tool __TEXT / __info_plist section must be '6.0'", toolPath) + + if not info.has_key("CFBundleIdentifier") or info["CFBundleIdentifier"] != os.path.basename(toolPath): + raise CheckException("'CFBundleIdentifier' in tool __TEXT / __info_plist section must match tool name", toolPath) + + if not info.has_key("SMAuthorizedClients"): + raise CheckException("'SMAuthorizedClients' in tool __TEXT / __info_plist section not found", toolPath) + infoClientList = info["SMAuthorizedClients"] + if not isinstance(infoClientList, list): + raise CheckException("'SMAuthorizedClients' in tool __TEXT / __info_plist section must be an array", toolPath) + if len(infoClientList) != 1: + raise CheckException("'SMAuthorizedClients' in tool __TEXT / __info_plist section must have one entry", toolPath) + + # Again, as a matter of policy we require that the SMAuthorizedClients entry must + # match exactly the designated requirement of the app. + + if infoClientList[0] != appReq: + raise CheckException("app designated requirement (%s) doesn't match entry in 'SMAuthorizedClients' (%s)" % (appReq, infoClientList[0]), toolPath) + +def checkStep4(appPath, toolPathList): + """Checks the "launchd.plist" embedded in each helper tool.""" + + for toolPath in toolPathList: + launchd = readPlistFromToolSection(toolPath, "__TEXT", "__launchd_plist") + + if not launchd.has_key("Label") or launchd["Label"] != os.path.basename(toolPath): + raise CheckException("'Label' in tool __TEXT / __launchd_plist section must match tool name", toolPath) + + # We don't need to check that the label matches the bundle identifier because + # we know it matches the tool name and step 4 checks that the tool name matches + # the bundle identifier. + +def checkStep5(appPath): + """There's nothing to do here; we effectively checked for this is steps 1 and 2.""" + pass + +def check(appPath): + """Checks the SMJobBless setup of the specified app.""" + + # Each of the following steps matches a bullet point in the SMJobBless header doc. + + toolPathList = checkStep1(appPath) + + checkStep2(appPath, toolPathList) + + checkStep3(appPath, toolPathList) + + checkStep4(appPath, toolPathList) + + checkStep5(appPath) + +def setreq(appPath, appInfoPlistPath, toolInfoPlistPaths): + """ + Reads information from the built app and uses it to set the SMJobBless setup + in the specified app and tool Info.plist source files. + """ + + if not os.path.isdir(appPath): + raise CheckException("app not found", appPath) + + if not os.path.isfile(appInfoPlistPath): + raise CheckException("app 'Info.plist' not found", appInfoPlistPath) + for toolInfoPlistPath in toolInfoPlistPaths: + if not os.path.isfile(toolInfoPlistPath): + raise CheckException("app 'Info.plist' not found", toolInfoPlistPath) + + # Get the designated requirement for the app and each of the tools. + + appReq = readDesignatedRequirement(appPath, "app") + + toolDirPath = os.path.join(appPath, "Contents", "Library", "LaunchServices") + if not os.path.isdir(toolDirPath): + raise CheckException("tool directory not found", toolDirPath) + + toolNameToReqMap = {} + for toolName in os.listdir(toolDirPath): + req = readDesignatedRequirement(os.path.join(toolDirPath, toolName), "tool") + toolNameToReqMap[toolName] = req + + if len(toolNameToReqMap) > len(toolInfoPlistPaths): + raise CheckException("tool directory has more tools (%d) than you've supplied tool 'Info.plist' paths (%d)" % (len(toolNameToReqMap), len(toolInfoPlistPaths)), toolDirPath) + if len(toolNameToReqMap) < len(toolInfoPlistPaths): + raise CheckException("tool directory has fewer tools (%d) than you've supplied tool 'Info.plist' paths (%d)" % (len(toolNameToReqMap), len(toolInfoPlistPaths)), toolDirPath) + + # Build the new value for SMPrivilegedExecutables. + + appToolDict = {} + toolInfoPlistPathToToolInfoMap = {} + for toolInfoPlistPath in toolInfoPlistPaths: + toolInfo = readInfoPlistFromPath(toolInfoPlistPath) + toolInfoPlistPathToToolInfoMap[toolInfoPlistPath] = toolInfo + if not toolInfo.has_key("CFBundleIdentifier"): + raise CheckException("'CFBundleIdentifier' not found", toolInfoPlistPath) + bundleID = toolInfo["CFBundleIdentifier"] + if not isinstance(bundleID, basestring): + raise CheckException("'CFBundleIdentifier' must be a string", toolInfoPlistPath) + appToolDict[bundleID] = toolNameToReqMap[bundleID] + + # Set the SMPrivilegedExecutables value in the app "Info.plist". + + appInfo = readInfoPlistFromPath(appInfoPlistPath) + needsUpdate = not appInfo.has_key("SMPrivilegedExecutables") + if not needsUpdate: + oldAppToolDict = appInfo["SMPrivilegedExecutables"] + if not isinstance(oldAppToolDict, dict): + raise CheckException("'SMPrivilegedExecutables' must be a dictionary", appInfoPlistPath) + appToolDictSorted = sorted(appToolDict.iteritems(), key=operator.itemgetter(0)) + oldAppToolDictSorted = sorted(oldAppToolDict.iteritems(), key=operator.itemgetter(0)) + needsUpdate = (appToolDictSorted != oldAppToolDictSorted) + + if needsUpdate: + appInfo["SMPrivilegedExecutables"] = appToolDict + plistlib.writePlist(appInfo, appInfoPlistPath) + print >> sys.stdout, "%s: updated" % appInfoPlistPath + + # Set the SMAuthorizedClients value in each tool's "Info.plist". + + toolAppListSorted = [ appReq ] # only one element, so obviously sorted (-: + for toolInfoPlistPath in toolInfoPlistPaths: + toolInfo = toolInfoPlistPathToToolInfoMap[toolInfoPlistPath] + + needsUpdate = not toolInfo.has_key("SMAuthorizedClients") + if not needsUpdate: + oldToolAppList = toolInfo["SMAuthorizedClients"] + if not isinstance(oldToolAppList, list): + raise CheckException("'SMAuthorizedClients' must be an array", toolInfoPlistPath) + oldToolAppListSorted = sorted(oldToolAppList) + needsUpdate = (toolAppListSorted != oldToolAppListSorted) + + if needsUpdate: + toolInfo["SMAuthorizedClients"] = toolAppListSorted + plistlib.writePlist(toolInfo, toolInfoPlistPath) + print >> sys.stdout, "%s: updated" % toolInfoPlistPath + +def main(): + options, appArgs = getopt.getopt(sys.argv[1:], "d") + + debug = False + for opt, val in options: + if opt == "-d": + debug = True + else: + raise UsageException() + + if len(appArgs) == 0: + raise UsageException() + command = appArgs[0] + if command == "check": + if len(appArgs) != 2: + raise UsageException() + check(appArgs[1]) + elif command == "setreq": + if len(appArgs) < 4: + raise UsageException() + setreq(appArgs[1], appArgs[2], appArgs[3:]) + else: + raise UsageException() + +if __name__ == "__main__": + try: + main() + except CheckException, e: + if e.path is None: + print >> sys.stderr, "%s: %s" % (os.path.basename(sys.argv[0]), e.message) + else: + path = e.path + if path.endswith("/"): + path = path[:-1] + print >> sys.stderr, "%s: %s" % (path, e.message) + sys.exit(1) + except UsageException, e: + print >> sys.stderr, "usage: %s check /path/to/app" % os.path.basename(sys.argv[0]) + print >> sys.stderr, " %s setreq /path/to/app /path/to/app/Info.plist /path/to/tool/Info.plist..." % os.path.basename(sys.argv[0]) + sys.exit(1) diff --git a/com.davidwernhart.Helper/Helper-Info.plist b/com.davidwernhart.Helper/Helper-Info.plist new file mode 100644 index 0000000..0343080 --- /dev/null +++ b/com.davidwernhart.Helper/Helper-Info.plist @@ -0,0 +1,18 @@ + + + + + CFBundleIdentifier + com.davidwernhart.Helper + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + com.davidwernhart.Helper + CFBundleVersion + 10 + SMAuthorizedClients + + identifier "com.davidwernhart.AlDente" and anchor apple generic and certificate leaf[subject.CN] = "Apple Development: david.wernhart96@gmail.com (GSDX9BQ584)" and certificate 1[field.1.2.840.113635.100.6.2.1] /* exists */ + + + diff --git a/com.davidwernhart.Helper/Helper-Launchd.plist b/com.davidwernhart.Helper/Helper-Launchd.plist new file mode 100644 index 0000000..e3fbf9d --- /dev/null +++ b/com.davidwernhart.Helper/Helper-Launchd.plist @@ -0,0 +1,13 @@ + + + + + Label + com.davidwernhart.Helper + MachServices + + com.davidwernhart.Helper.mach + + + + diff --git a/com.davidwernhart.Helper/HelperTool.swift b/com.davidwernhart.Helper/HelperTool.swift new file mode 100644 index 0000000..9339640 --- /dev/null +++ b/com.davidwernhart.Helper/HelperTool.swift @@ -0,0 +1,126 @@ +// +// HelperTool.swift +// com.davidwernhart.Helper +// +// Created by David Wernhart on 13.02.20. +// Copyright © 2020 David Wernhart. All rights reserved. +// + +import Foundation +import IOKit.pwr_mgt + +final class HelperTool: NSObject, HelperToolProtocol { + + static let instance = HelperTool() + + var modifiedKeys: [String: UInt8] = [:] + var openAssertions: [IOPMAssertionID] = [] + + func getVersion(withReply reply: (String) -> Void) { +// let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString" as String) as? String ?? "(unknown version)" +// let build = Bundle.main.object(forInfoDictionaryKey: kCFBundleVersionKey as String) as? String ?? "(unknown build)" +// reply("v\(version) (\(build))") + reply(helperVersion) + + } + + func setSMCByte(key: String, value: UInt8) { + do { + try SMCKit.open() + } catch { + print(error) + exit(EX_UNAVAILABLE) + } + let smcKey = SMCKit.getKey(key, type: DataTypes.UInt8) + let bytes: SMCBytes = (value, UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), + UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), + UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), + UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), + UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), + UInt8(0), UInt8(0)) + + if(self.modifiedKeys[key] == nil){ + readSMCByte(key: key) { (originalValue) in + self.modifiedKeys[key] = originalValue + _ = try? SMCKit.writeData(smcKey, data: bytes) + } + } + else{ + _ = try? SMCKit.writeData(smcKey, data: bytes) + } + + + } + + func readSMCByte(key: String, withReply reply: @escaping (UInt8) -> Void) { + do { + try SMCKit.open() + } catch { + print(error) + exit(EX_UNAVAILABLE) + } + + let smcKey = SMCKit.getKey(key, type: DataTypes.UInt8) + do { + let status = try SMCKit.readData(smcKey).0 + reply(status) + } catch { + reply(0) + } + } + + func readSMCUInt32(key: String, withReply reply: @escaping (UInt32) -> Void) { + do { + try SMCKit.open() + } catch { + print(error) + exit(EX_UNAVAILABLE) + } + + let smcKey = SMCKit.getKey(key, type: DataTypes.UInt32) + do { + let data = try SMCKit.readData(smcKey) + reply(UInt32(fromBytes: (data.0, data.1, data.2, data.3))) + } catch { + reply(0) + } + } + + func createAssertion(assertion:String, withReply reply: @escaping (IOPMAssertionID) -> Void){ + var assertionID : IOPMAssertionID = IOPMAssertionID(0) + let reason:CFString = "AlDente" as NSString + let cfAssertion:CFString = assertion as NSString + let success = IOPMAssertionCreateWithName(cfAssertion, + IOPMAssertionLevel(kIOPMAssertionLevelOn), + reason, + &assertionID) + if success == kIOReturnSuccess { + openAssertions.append(assertionID) + reply(assertionID) + } + else{ + reply (UInt32(kCFNumberNaN)) + } + } + + func releaseAssertion(assertionID:IOPMAssertionID){ + IOPMAssertionRelease(assertionID) + openAssertions.remove(at: openAssertions.firstIndex(of: assertionID)!) + } + + func setResetVal(key:String, value: UInt8){ + modifiedKeys[key]=value + } + + func reset(){ + for (key, value) in modifiedKeys{ + setSMCByte(key: key, value: value) + } + + for assertionID in openAssertions{ + releaseAssertion(assertionID: assertionID) + } + modifiedKeys.removeAll() + openAssertions.removeAll() + } +} diff --git a/com.davidwernhart.Helper/SMC.swift b/com.davidwernhart.Helper/SMC.swift new file mode 100644 index 0000000..609594b --- /dev/null +++ b/com.davidwernhart.Helper/SMC.swift @@ -0,0 +1,795 @@ +// +// SMC.swift +// SMCKit +// +// The MIT License +// +// Copyright (C) 2014-2017 beltex +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import IOKit +import Foundation +import os + +//------------------------------------------------------------------------------ +// MARK: Type Aliases +//------------------------------------------------------------------------------ + +// http://stackoverflow.com/a/22383661 + +/// Floating point, unsigned, 14 bits exponent, 2 bits fraction +public typealias FPE2 = (UInt8, UInt8) + +/// Floating point, signed, 7 bits exponent, 8 bits fraction +public typealias SP78 = (UInt8, UInt8) + +public typealias SMCBytes = (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, + UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, + UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, + UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, + UInt8, UInt8, UInt8, UInt8) + +//------------------------------------------------------------------------------ +// MARK: Standard Library Extensions +//------------------------------------------------------------------------------ + +extension UInt32 { + + init(fromBytes bytes: (UInt8, UInt8, UInt8, UInt8)) { + // TODO: Broken up due to "Expression was too complex" error as of + // Swift 4. + + let byte0 = UInt32(bytes.0) << 24 + let byte1 = UInt32(bytes.1) << 16 + let byte2 = UInt32(bytes.2) << 8 + let byte3 = UInt32(bytes.3) + + self = byte0 | byte1 | byte2 | byte3 + } +} + +extension Bool { + + init(fromByte byte: UInt8) { + self = byte == 1 ? true : false + } +} + +public extension Int { + + init(fromFPE2 bytes: FPE2) { + self = (Int(bytes.0) << 6) + (Int(bytes.1) >> 2) + } + + func toFPE2() -> FPE2 { + return (UInt8(self >> 6), UInt8((self << 2) ^ ((self >> 6) << 8))) + } +} + +extension Double { + + init(fromSP78 bytes: SP78) { + // FIXME: Handle second byte + let sign = bytes.0 & 0x80 == 0 ? 1.0 : -1.0 + self = sign * Double(bytes.0 & 0x7F) // AND to mask sign bit + } +} + +// Thanks to Airspeed Velocity for the great idea! +// http://airspeedvelocity.net/2015/05/22/my-talk-at-swift-summit/ +public extension FourCharCode { + + init(fromString str: String) { + precondition(str.count == 4) + + self = str.utf8.reduce(0) { sum, character in + return sum << 8 | UInt32(character) + } + } + + init(fromStaticString str: StaticString) { + precondition(str.utf8CodeUnitCount == 4) + + self = str.withUTF8Buffer { buffer in + // TODO: Broken up due to "Expression was too complex" error as of + // Swift 4. + + let byte0 = UInt32(buffer[0]) << 24 + let byte1 = UInt32(buffer[1]) << 16 + let byte2 = UInt32(buffer[2]) << 8 + let byte3 = UInt32(buffer[3]) + + return byte0 | byte1 | byte2 | byte3 + } + } + + func toString() -> String { + return String(describing: UnicodeScalar(self >> 24 & 0xff)!) + + String(describing: UnicodeScalar(self >> 16 & 0xff)!) + + String(describing: UnicodeScalar(self >> 8 & 0xff)!) + + String(describing: UnicodeScalar(self & 0xff)!) + } +} + +//------------------------------------------------------------------------------ +// MARK: Defined by AppleSMC.kext +//------------------------------------------------------------------------------ + +/// Defined by AppleSMC.kext +/// +/// This is the predefined struct that must be passed to communicate with the +/// AppleSMC driver. While the driver is closed source, the definition of this +/// struct happened to appear in the Apple PowerManagement project at around +/// version 211, and soon after disappeared. It can be seen in the PrivateLib.c +/// file under pmconfigd. Given that it is C code, this is the closest +/// translation to Swift from a type perspective. +/// +/// ### Issues +/// +/// * Padding for struct alignment when passed over to C side +/// * Size of struct must be 80 bytes +/// * C array's are bridged as tuples +/// +/// http://www.opensource.apple.com/source/PowerManagement/PowerManagement-211/ +public struct SMCParamStruct { + + /// I/O Kit function selector + public enum Selector: UInt8 { + case kSMCHandleYPCEvent = 2 + case kSMCReadKey = 5 + case kSMCWriteKey = 6 + case kSMCGetKeyFromIndex = 8 + case kSMCGetKeyInfo = 9 + } + + /// Return codes for SMCParamStruct.result property + public enum Result: UInt8 { + case kSMCSuccess = 0 + case kSMCError = 1 + case kSMCKeyNotFound = 132 + } + + public struct SMCVersion { + var major: CUnsignedChar = 0 + var minor: CUnsignedChar = 0 + var build: CUnsignedChar = 0 + var reserved: CUnsignedChar = 0 + var release: CUnsignedShort = 0 + } + + public struct SMCPLimitData { + var version: UInt16 = 0 + var length: UInt16 = 0 + var cpuPLimit: UInt32 = 0 + var gpuPLimit: UInt32 = 0 + var memPLimit: UInt32 = 0 + } + + 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) + var dataType: UInt32 = 0 + + var dataAttributes: UInt8 = 0 + } + + /// FourCharCode telling the SMC what we want + var key: UInt32 = 0 + + var vers = SMCVersion() + + var pLimitData = SMCPLimitData() + + var keyInfo = SMCKeyInfoData() + + /// Padding for struct alignment when passed over to C side + var padding: UInt16 = 0 + + /// Result of an operation + var result: UInt8 = 0 + + var status: UInt8 = 0 + + /// Method selector + var data8: UInt8 = 0 + + var data32: UInt32 = 0 + + /// Data returned from the SMC + var bytes: SMCBytes = (UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), + UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), + UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), + UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), + UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), + UInt8(0), UInt8(0)) +} + +//------------------------------------------------------------------------------ +// MARK: SMC Client +//------------------------------------------------------------------------------ + +/// SMC data type information +public struct DataTypes { + + /// Fan information struct + public static let FDS = + DataType(type: FourCharCode(fromStaticString: "{fds"), size: 16) + public static let Flag = + DataType(type: FourCharCode(fromStaticString: "flag"), size: 1) + /// See type aliases + public static let FPE2 = + DataType(type: FourCharCode(fromStaticString: "fpe2"), size: 2) + /// See type aliases + public static let SP78 = + DataType(type: FourCharCode(fromStaticString: "sp78"), size: 2) + public static let UInt8 = + DataType(type: FourCharCode(fromStaticString: "ui8 "), size: 1) + public static let UInt32 = + DataType(type: FourCharCode(fromStaticString: "ui32"), size: 4) +} + +public struct SMCKey { + let code: FourCharCode + let info: DataType +} + +public struct DataType: Equatable { + let type: FourCharCode + let size: IOByteCount +} + +public func ==(lhs: DataType, rhs: DataType) -> Bool { + return lhs.type == rhs.type && lhs.size == rhs.size +} + +/// Apple System Management Controller (SMC) user-space client for Intel-based +/// Macs. Works by talking to the AppleSMC.kext (kernel extension), the closed +/// source driver for the SMC. +public struct SMCKit { + + public enum SMCError: Error { + + /// AppleSMC driver not found + case driverNotFound + + /// Failed to open a connection to the AppleSMC driver + case failedToOpen + + /// This SMC key is not valid on this machine + case keyNotFound(code: String) + + /// Requires root privileges + case notPrivileged + + /// Fan speed must be > 0 && <= fanMaxSpeed + case unsafeFanSpeed + + /// https://developer.apple.com/library/mac/qa/qa1075/_index.html + /// + /// - parameter kIOReturn: I/O Kit error code + /// - parameter SMCResult: SMC specific return code + case unknown(kIOReturn: kern_return_t, SMCResult: UInt8) + } + + /// Connection to the SMC driver + fileprivate static var connection: io_connect_t = 0 + + /// Open connection to the SMC driver. This must be done first before any + /// other calls + public static func open() throws { + let service = IOServiceGetMatchingService(kIOMasterPortDefault, + IOServiceMatching("AppleSMC")) + + if service == 0 { throw SMCError.driverNotFound } + + let result = IOServiceOpen(service, mach_task_self_, 0, + &SMCKit.connection) + IOObjectRelease(service) + + if result != kIOReturnSuccess { throw SMCError.failedToOpen } + } + + /// Close connection to the SMC driver + @discardableResult + public static func close() -> Bool { + let result = IOServiceClose(SMCKit.connection) + return result == kIOReturnSuccess ? true : false + } + + /// Get information about a key + public static func keyInformation(_ key: FourCharCode) throws -> DataType { + var inputStruct = SMCParamStruct() + + inputStruct.key = key + inputStruct.data8 = SMCParamStruct.Selector.kSMCGetKeyInfo.rawValue + + let outputStruct = try callDriver(&inputStruct) + + return DataType(type: outputStruct.keyInfo.dataType, + size: IOByteCount(outputStruct.keyInfo.dataSize)) + } + + /// Get information about the key at index + public static func keyInformationAtIndex(_ index: Int) throws -> + FourCharCode { + var inputStruct = SMCParamStruct() + + inputStruct.data8 = SMCParamStruct.Selector.kSMCGetKeyFromIndex.rawValue + inputStruct.data32 = UInt32(index) + + let outputStruct = try callDriver(&inputStruct) + + return outputStruct.key + } + + public static func getKey(_ code: String, type: DataType) -> SMCKey { + let key = SMCKey(code: FourCharCode(fromString: code), info: type) + return key + } + + /// Read data of a key + public static func readData(_ key: SMCKey) throws -> SMCBytes { + var inputStruct = SMCParamStruct() + + inputStruct.key = key.code + inputStruct.keyInfo.dataSize = UInt32(IOByteCount(key.info.size)) + inputStruct.data8 = SMCParamStruct.Selector.kSMCReadKey.rawValue + + let outputStruct = try callDriver(&inputStruct) + + return outputStruct.bytes + } + + /// Write data for a key + public static func writeData(_ key: SMCKey, data: SMCBytes) throws { + var inputStruct = SMCParamStruct() + + inputStruct.key = key.code + inputStruct.bytes = data + inputStruct.keyInfo.dataSize = UInt32(IOByteCount(key.info.size)) + inputStruct.data8 = SMCParamStruct.Selector.kSMCWriteKey.rawValue + + _ = try callDriver(&inputStruct) + } + + /// Make an actual call to the SMC driver + public static func callDriver(_ inputStruct: inout SMCParamStruct, + selector: SMCParamStruct.Selector = .kSMCHandleYPCEvent) + throws -> SMCParamStruct { + os_log("SMCPARAMSTRUCT SIZE: %d", MemoryLayout.stride) + assert(MemoryLayout.stride == 80, "SMCParamStruct size is != 80") + + var outputStruct = SMCParamStruct() + let inputStructSize = MemoryLayout.stride + var outputStructSize = MemoryLayout.stride + + let result = IOConnectCallStructMethod(SMCKit.connection, + UInt32(selector.rawValue), + &inputStruct, + inputStructSize, + &outputStruct, + &outputStructSize) + + switch (result, outputStruct.result) { + case (kIOReturnSuccess, SMCParamStruct.Result.kSMCSuccess.rawValue): + return outputStruct + case (kIOReturnSuccess, SMCParamStruct.Result.kSMCKeyNotFound.rawValue): + throw SMCError.keyNotFound(code: inputStruct.key.toString()) + case (kIOReturnNotPrivileged, _): + throw SMCError.notPrivileged + default: + throw SMCError.unknown(kIOReturn: result, + SMCResult: outputStruct.result) + } + } +} + +//------------------------------------------------------------------------------ +// MARK: General +//------------------------------------------------------------------------------ + +extension SMCKit { + + /// Get all valid SMC keys for this machine + public static func allKeys() throws -> [SMCKey] { + let count = try keyCount() + var keys = [SMCKey]() + + for i in 0 ..< count { + let key = try keyInformationAtIndex(i) + let info = try keyInformation(key) + keys.append(SMCKey(code: key, info: info)) + } + + return keys + } + + /// Get the number of valid SMC keys for this machine + public static func keyCount() throws -> Int { + let key = SMCKey(code: FourCharCode(fromStaticString: "#KEY"), + info: DataTypes.UInt32) + + let data = try readData(key) + return Int(UInt32(fromBytes: (data.0, data.1, data.2, data.3))) + } + + /// Is this key valid on this machine? + public static func isKeyFound(_ code: FourCharCode) throws -> Bool { + do { + _ = try keyInformation(code) + } catch SMCError.keyNotFound { return false } + + return true + } +} + +//------------------------------------------------------------------------------ +// MARK: Temperature +//------------------------------------------------------------------------------ + +/// The list is NOT exhaustive. In addition, the names of the sensors may not be +/// mapped to the correct hardware component. +/// +/// ### Sources +/// +/// * powermetrics(1) +/// * https://www.apple.com/downloads/dashboard/status/istatpro.html +/// * https://github.com/hholtmann/smcFanControl +/// * https://github.com/jedda/OSX-Monitoring-Tools +/// * http://www.opensource.apple.com/source/net_snmp/ +/// * http://www.parhelia.ch/blog/statics/k3_keys.html +public struct TemperatureSensors { + + public static let AMBIENT_AIR_0 = TemperatureSensor(name: "AMBIENT_AIR_0", + code: FourCharCode(fromStaticString: "TA0P")) + public static let AMBIENT_AIR_1 = TemperatureSensor(name: "AMBIENT_AIR_1", + code: FourCharCode(fromStaticString: "TA1P")) + // Via powermetrics(1) + public static let CPU_0_DIE = TemperatureSensor(name: "CPU_0_DIE", + code: FourCharCode(fromStaticString: "TC0F")) + public static let CPU_0_DIODE = TemperatureSensor(name: "CPU_0_DIODE", + code: FourCharCode(fromStaticString: "TC0D")) + public static let CPU_0_HEATSINK = TemperatureSensor(name: "CPU_0_HEATSINK", + code: FourCharCode(fromStaticString: "TC0H")) + public static let CPU_0_PROXIMITY = + TemperatureSensor(name: "CPU_0_PROXIMITY", + code: FourCharCode(fromStaticString: "TC0P")) + public static let ENCLOSURE_BASE_0 = + TemperatureSensor(name: "ENCLOSURE_BASE_0", + code: FourCharCode(fromStaticString: "TB0T")) + public static let ENCLOSURE_BASE_1 = + TemperatureSensor(name: "ENCLOSURE_BASE_1", + code: FourCharCode(fromStaticString: "TB1T")) + public static let ENCLOSURE_BASE_2 = + TemperatureSensor(name: "ENCLOSURE_BASE_2", + code: FourCharCode(fromStaticString: "TB2T")) + public static let ENCLOSURE_BASE_3 = + TemperatureSensor(name: "ENCLOSURE_BASE_3", + code: FourCharCode(fromStaticString: "TB3T")) + public static let GPU_0_DIODE = TemperatureSensor(name: "GPU_0_DIODE", + code: FourCharCode(fromStaticString: "TG0D")) + public static let GPU_0_HEATSINK = TemperatureSensor(name: "GPU_0_HEATSINK", + code: FourCharCode(fromStaticString: "TG0H")) + public static let GPU_0_PROXIMITY = + TemperatureSensor(name: "GPU_0_PROXIMITY", + code: FourCharCode(fromStaticString: "TG0P")) + public static let HDD_PROXIMITY = TemperatureSensor(name: "HDD_PROXIMITY", + code: FourCharCode(fromStaticString: "TH0P")) + public static let HEATSINK_0 = TemperatureSensor(name: "HEATSINK_0", + code: FourCharCode(fromStaticString: "Th0H")) + public static let HEATSINK_1 = TemperatureSensor(name: "HEATSINK_1", + code: FourCharCode(fromStaticString: "Th1H")) + public static let HEATSINK_2 = TemperatureSensor(name: "HEATSINK_2", + code: FourCharCode(fromStaticString: "Th2H")) + public static let LCD_PROXIMITY = TemperatureSensor(name: "LCD_PROXIMITY", + code: FourCharCode(fromStaticString: "TL0P")) + public static let MEM_SLOT_0 = TemperatureSensor(name: "MEM_SLOT_0", + code: FourCharCode(fromStaticString: "TM0S")) + public static let MEM_SLOTS_PROXIMITY = + TemperatureSensor(name: "MEM_SLOTS_PROXIMITY", + code: FourCharCode(fromStaticString: "TM0P")) + public static let MISC_PROXIMITY = TemperatureSensor(name: "MISC_PROXIMITY", + code: FourCharCode(fromStaticString: "Tm0P")) + public static let NORTHBRIDGE = TemperatureSensor(name: "NORTHBRIDGE", + code: FourCharCode(fromStaticString: "TN0H")) + public static let NORTHBRIDGE_DIODE = + TemperatureSensor(name: "NORTHBRIDGE_DIODE", + code: FourCharCode(fromStaticString: "TN0D")) + public static let NORTHBRIDGE_PROXIMITY = + TemperatureSensor(name: "NORTHBRIDGE_PROXIMITY", + code: FourCharCode(fromStaticString: "TN0P")) + public static let ODD_PROXIMITY = TemperatureSensor(name: "ODD_PROXIMITY", + code: FourCharCode(fromStaticString: "TO0P")) + public static let PALM_REST = TemperatureSensor(name: "PALM_REST", + code: FourCharCode(fromStaticString: "Ts0P")) + public static let PWR_SUPPLY_PROXIMITY = + TemperatureSensor(name: "PWR_SUPPLY_PROXIMITY", + code: FourCharCode(fromStaticString: "Tp0P")) + public static let THUNDERBOLT_0 = TemperatureSensor(name: "THUNDERBOLT_0", + code: FourCharCode(fromStaticString: "TI0P")) + public static let THUNDERBOLT_1 = TemperatureSensor(name: "THUNDERBOLT_1", + code: FourCharCode(fromStaticString: "TI1P")) + + public static let all = [AMBIENT_AIR_0.code: AMBIENT_AIR_0, + AMBIENT_AIR_1.code: AMBIENT_AIR_1, + CPU_0_DIE.code: CPU_0_DIE, + CPU_0_DIODE.code: CPU_0_DIODE, + CPU_0_HEATSINK.code: CPU_0_HEATSINK, + CPU_0_PROXIMITY.code: CPU_0_PROXIMITY, + ENCLOSURE_BASE_0.code: ENCLOSURE_BASE_0, + ENCLOSURE_BASE_1.code: ENCLOSURE_BASE_1, + ENCLOSURE_BASE_2.code: ENCLOSURE_BASE_2, + ENCLOSURE_BASE_3.code: ENCLOSURE_BASE_3, + GPU_0_DIODE.code: GPU_0_DIODE, + GPU_0_HEATSINK.code: GPU_0_HEATSINK, + GPU_0_PROXIMITY.code: GPU_0_PROXIMITY, + HDD_PROXIMITY.code: HDD_PROXIMITY, + HEATSINK_0.code: HEATSINK_0, + HEATSINK_1.code: HEATSINK_1, + HEATSINK_2.code: HEATSINK_2, + MEM_SLOT_0.code: MEM_SLOT_0, + MEM_SLOTS_PROXIMITY.code: MEM_SLOTS_PROXIMITY, + PALM_REST.code: PALM_REST, + LCD_PROXIMITY.code: LCD_PROXIMITY, + MISC_PROXIMITY.code: MISC_PROXIMITY, + NORTHBRIDGE.code: NORTHBRIDGE, + NORTHBRIDGE_DIODE.code: NORTHBRIDGE_DIODE, + NORTHBRIDGE_PROXIMITY.code: NORTHBRIDGE_PROXIMITY, + ODD_PROXIMITY.code: ODD_PROXIMITY, + PWR_SUPPLY_PROXIMITY.code: PWR_SUPPLY_PROXIMITY, + THUNDERBOLT_0.code: THUNDERBOLT_0, + THUNDERBOLT_1.code: THUNDERBOLT_1] +} + +public struct TemperatureSensor { + public let name: String + public let code: FourCharCode +} + +public enum TemperatureUnit { + case celius + case fahrenheit + case kelvin + + public static func toFahrenheit(_ celius: Double) -> Double { + // https://en.wikipedia.org/wiki/Fahrenheit#Definition_and_conversions + return (celius * 1.8) + 32 + } + + public static func toKelvin(_ celius: Double) -> Double { + // https://en.wikipedia.org/wiki/Kelvin + return celius + 273.15 + } +} + +extension SMCKit { + + public static func allKnownTemperatureSensors() throws -> + [TemperatureSensor] { + var sensors = [TemperatureSensor]() + + for sensor in TemperatureSensors.all.values { + if try isKeyFound(sensor.code) { sensors.append(sensor) } + } + + return sensors + } + + public static func allUnknownTemperatureSensors() throws -> [TemperatureSensor] { + let keys = try allKeys() + + return keys.filter { $0.code.toString().hasPrefix("T") && + $0.info == DataTypes.SP78 && + TemperatureSensors.all[$0.code] == nil } + .map { TemperatureSensor(name: "Unknown", code: $0.code) } + } + + /// Get current temperature of a sensor + public static func temperature(_ sensorCode: FourCharCode, + unit: TemperatureUnit = .celius) throws -> Double { + let data = try readData(SMCKey(code: sensorCode, info: DataTypes.SP78)) + + let temperatureInCelius = Double(fromSP78: (data.0, data.1)) + + switch unit { + case .celius: + return temperatureInCelius + case .fahrenheit: + return TemperatureUnit.toFahrenheit(temperatureInCelius) + case .kelvin: + return TemperatureUnit.toKelvin(temperatureInCelius) + } + } +} + +//------------------------------------------------------------------------------ +// MARK: Fan +//------------------------------------------------------------------------------ + +public struct Fan { + // TODO: Should we start the fan id from 1 instead of 0? + public let id: Int + public let name: String + public let minSpeed: Int + public let maxSpeed: Int +} + +extension SMCKit { + + public static func allFans() throws -> [Fan] { + let count = try fanCount() + var fans = [Fan]() + + for i in 0 ..< count { + fans.append(try SMCKit.fan(i)) + } + + return fans + } + + public static func fan(_ id: Int) throws -> Fan { + let name = try fanName(id) + let minSpeed = try fanMinSpeed(id) + let maxSpeed = try fanMaxSpeed(id) + return Fan(id: id, name: name, minSpeed: minSpeed, maxSpeed: maxSpeed) + } + + /// Number of fans this machine has. All Intel based Macs, except for the + /// 2015 MacBook (8,1), have at least 1 + public static func fanCount() throws -> Int { + let key = SMCKey(code: FourCharCode(fromStaticString: "FNum"), + info: DataTypes.UInt8) + + let data = try readData(key) + return Int(data.0) + } + + public static func fanName(_ id: Int) throws -> String { + let key = SMCKey(code: FourCharCode(fromString: "F\(id)ID"), + info: DataTypes.FDS) + let data = try readData(key) + + // The last 12 bytes of '{fds' data type, a custom struct defined by the + // AppleSMC.kext that is 16 bytes, contains the fan name + let c1 = String(UnicodeScalar(data.4)) + let c2 = String(UnicodeScalar(data.5)) + let c3 = String(UnicodeScalar(data.6)) + let c4 = String(UnicodeScalar(data.7)) + let c5 = String(UnicodeScalar(data.8)) + let c6 = String(UnicodeScalar(data.9)) + let c7 = String(UnicodeScalar(data.10)) + let c8 = String(UnicodeScalar(data.11)) + let c9 = String(UnicodeScalar(data.12)) + let c10 = String(UnicodeScalar(data.13)) + let c11 = String(UnicodeScalar(data.14)) + let c12 = String(UnicodeScalar(data.15)) + + let name = c1 + c2 + c3 + c4 + c5 + c6 + c7 + c8 + c9 + c10 + c11 + c12 + + let characterSet = CharacterSet.whitespaces + return name.trimmingCharacters(in: characterSet) + } + + public static func fanCurrentSpeed(_ id: Int) throws -> Int { + let key = SMCKey(code: FourCharCode(fromString: "F\(id)Ac"), + info: DataTypes.FPE2) + + let data = try readData(key) + return Int(fromFPE2: (data.0, data.1)) + } + + public static func fanMinSpeed(_ id: Int) throws -> Int { + let key = SMCKey(code: FourCharCode(fromString: "F\(id)Mn"), + info: DataTypes.FPE2) + + let data = try readData(key) + return Int(fromFPE2: (data.0, data.1)) + } + + public static func fanMaxSpeed(_ id: Int) throws -> Int { + let key = SMCKey(code: FourCharCode(fromString: "F\(id)Mx"), + info: DataTypes.FPE2) + + let data = try readData(key) + return Int(fromFPE2: (data.0, data.1)) + } + + /// Requires root privileges. By minimum we mean that OS X can interject and + /// raise the fan speed if needed, however it will not go below this. + /// + /// WARNING: You are playing with hardware here, BE CAREFUL. + /// + /// - Throws: Of note, `SMCKit.SMCError`'s `UnsafeFanSpeed` and `NotPrivileged` + public static func fanSetMinSpeed(_ id: Int, speed: Int) throws { + let maxSpeed = try fanMaxSpeed(id) + if speed <= 0 || speed > maxSpeed { throw SMCError.unsafeFanSpeed } + + let data = speed.toFPE2() + let bytes: SMCBytes = (data.0, data.1, UInt8(0), UInt8(0), UInt8(0), UInt8(0), + UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), + UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), + UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), + UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), + UInt8(0), UInt8(0)) + + let key = SMCKey(code: FourCharCode(fromString: "F\(id)Mn"), + info: DataTypes.FPE2) + + try writeData(key, data: bytes) + } +} + +//------------------------------------------------------------------------------ +// MARK: Miscellaneous +//------------------------------------------------------------------------------ + +public struct batteryInfo { + public let batteryCount: Int + public let isACPresent: Bool + public let isBatteryPowered: Bool + public let isBatteryOk: Bool + public let isCharging: Bool +} + +extension SMCKit { + + public static func isOpticalDiskDriveFull() throws -> Bool { + // TODO: Should we catch key not found? That just means the machine + // doesn't have an ODD. Returning false though is not fully correct. + // Maybe we could throw a no ODD error instead? + let key = SMCKey(code: FourCharCode(fromStaticString: "MSDI"), + info: DataTypes.Flag) + + let data = try readData(key) + return Bool(fromByte: data.0) + } + + public static func batteryInformation() throws -> batteryInfo { + let batteryCountKey = + SMCKey(code: FourCharCode(fromStaticString: "BNum"), + info: DataTypes.UInt8) + let batteryPoweredKey = + SMCKey(code: FourCharCode(fromStaticString: "BATP"), + info: DataTypes.Flag) + let batteryInfoKey = + SMCKey(code: FourCharCode(fromStaticString: "BSIn"), + info: DataTypes.UInt8) + + let batteryCountData = try readData(batteryCountKey) + let batteryCount = Int(batteryCountData.0) + + let isBatteryPoweredData = try readData(batteryPoweredKey) + let isBatteryPowered = Bool(fromByte: isBatteryPoweredData.0) + + let batteryInfoData = try readData(batteryInfoKey) + let isCharging = batteryInfoData.0 & 1 == 1 ? true : false + let isACPresent = (batteryInfoData.0 >> 1) & 1 == 1 ? true : false + let isBatteryOk = (batteryInfoData.0 >> 6) & 1 == 1 ? true : false + + return batteryInfo(batteryCount: batteryCount, isACPresent: isACPresent, + isBatteryPowered: isBatteryPowered, + isBatteryOk: isBatteryOk, + isCharging: isCharging) + } +} diff --git a/com.davidwernhart.Helper/main.swift b/com.davidwernhart.Helper/main.swift new file mode 100644 index 0000000..a9cec8d --- /dev/null +++ b/com.davidwernhart.Helper/main.swift @@ -0,0 +1,44 @@ +// +// main.swift +// com.davidwernhart.Helper +// +// Created by David Wernhart on 10.02.20. +// Copyright © 2020 David Wernhart. All rights reserved. +// + +import Foundation +import AppKit + +final class HelperDelegate: NSObject, NSXPCListenerDelegate { + func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool { + newConnection.exportedInterface = NSXPCInterface(with: HelperToolProtocol.self) + newConnection.exportedObject = HelperTool.instance + newConnection.resume() + return true + } +} + +let delegate = HelperDelegate() +let listener = NSXPCListener(machServiceName: "com.davidwernhart.Helper.mach") +listener.delegate = delegate +listener.resume() +var hasChecked = false +Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { timer in + let workspace = NSWorkspace.shared + let applications = workspace.runningApplications + var foundApp = false + for app in applications { + if(app.bundleIdentifier?.elementsEqual("com.davidwernhart.AlDente") == true){ + foundApp = true + } + } + if(foundApp && hasChecked){ + hasChecked = false + } + else if(!foundApp && !hasChecked){ + hasChecked = true + HelperTool.instance.reset() + exit(0) + } +} +RunLoop.current.run()