├── .gitattributes ├── .github └── workflows │ ├── debug.yml │ └── release.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── ent.plist ├── hud-prefix.pch ├── ipabuild.sh ├── layout ├── Applications │ └── Helium.app │ │ ├── Assets.car │ │ ├── Info.plist │ │ ├── credits │ │ ├── bomberfish.png │ │ └── fuuko.png │ │ ├── en.lproj │ │ └── Localizable.strings │ │ ├── fonts │ │ └── .gitkeep │ │ ├── icon.png │ │ └── zh-Hans.lproj │ │ └── Localizable.strings ├── DEBIAN │ └── control └── Library │ └── LaunchDaemons │ └── com.leemin.helium.plist └── src ├── app ├── MainApplication.h ├── MainApplication.mm ├── MainApplicationDelegate.h └── MainApplicationDelegate.mm ├── assets ├── bezels │ ├── BigNotch.png │ ├── DynamicIsland.png │ └── SmallNotch.png └── credits │ ├── bomberfish.png │ ├── fuuko.png │ ├── leminlimez.png │ └── lessica.png ├── bridging ├── Helium-Bridging-Header.h ├── SwiftObjCPPBridger.h └── SwiftObjCPPBridger.m ├── controllers ├── DeviceScaleManager.swift └── WidgetManager.swift ├── extensions ├── Alert++.swift ├── Color++.swift ├── DarwinNotificationCenter.swift ├── EZTimer.h ├── EZTimer.mm ├── FlipEffect.swift ├── FontUtils.h ├── FontUtils.mm ├── FoundationExtensions.swift ├── Haptic++.swift ├── LunarDate.h ├── LunarDate.mm ├── UsefulFunctions.h ├── UsefulFunctions.mm ├── UserDefaultsExtensions.swift ├── ViewExtensions.swift ├── WeatherUtils.h └── WeatherUtils.mm ├── helpers ├── private_headers │ ├── AXEventHandInfoRepresentation.h │ ├── AXEventPathInfoRepresentation.h │ ├── AXEventRepresentation.h │ ├── BackboardServices.h │ ├── CAFilter.h │ ├── FBSOrientationObserver.h │ ├── FBSOrientationUpdate.h │ ├── IOHIDEvent+KIF.h │ ├── IOHIDEvent+KIF.m │ ├── IOKit+SPI.h │ ├── LSApplicationProxy.h │ ├── LSApplicationWorkspace.h │ ├── NSUserDefaults+Private.h │ ├── SBSAccessibilityWindowHostingController.h │ ├── SpringBoardServices.h │ ├── UIApplication+Private.h │ ├── UIApplicationRotationFollowingController.h │ ├── UIApplicationRotationFollowingControllerNoTouches.h │ ├── UIApplicationRotationFollowingWindow.h │ ├── UIAutoRotatingWindow.h │ ├── UIEvent+Private.h │ ├── UIEventDispatcher.h │ ├── UIEventFetcher.h │ ├── UITouch+Private.h │ ├── UITouch-KIFAdditions.h │ ├── UITouch-KIFAdditions.m │ ├── UIWindow+Private.h │ ├── kern_memorystatus.h │ └── libproc.h └── ts │ ├── TSEventFetcher.h │ ├── TSEventFetcher.mm │ └── pac_helper.h ├── hud ├── AnyBackdropView.h ├── AnyBackdropView.mm ├── HUDApp.mm ├── HUDHelper.h ├── HUDHelper.mm ├── HUDMainApplication.h ├── HUDMainApplication.mm ├── HUDMainApplicationDelegate.h ├── HUDMainApplicationDelegate.mm ├── HUDMainWindow.h ├── HUDMainWindow.mm ├── HUDRootViewController.h └── HUDRootViewController.mm ├── views ├── UIApplicationViews.swift ├── navigation │ ├── HomePageView.swift │ ├── SettingsView.swift │ └── WidgetCustomizationView.swift ├── widget │ ├── WidgetConfigureView.swift │ ├── WidgetPreferencesView.swift │ └── WidgetPreviewsView.swift └── widgetset │ ├── EditWidgetSetView.swift │ └── SelectPresetView.swift └── widgets ├── DeviceScaleManager.h ├── DeviceScaleManager.m ├── WidgetManager.h └── WidgetManager.mm /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/workflows/debug.yml: -------------------------------------------------------------------------------- 1 | name: Build Helium Debug 2 | 3 | on: 4 | push: 5 | branches: 6 | - "**" 7 | tags: 8 | - "test" 9 | pull_request: 10 | 11 | env: 12 | THEOS: '' 13 | XCODE_VERSION: '15.1' 14 | logT: '' 15 | 16 | jobs: 17 | build: 18 | runs-on: macos-13 19 | 20 | steps: 21 | - name: Setup Xcode version 22 | uses: maxim-lobanov/setup-xcode@v1 23 | with: 24 | xcode-version: ${{ env.XCODE_VERSION }} 25 | 26 | - name: Install Homebrew dependencies 27 | run: | 28 | HOMEBREW_NO_AUTO_UPDATE=1 brew install dpkg ldid make libplist openssl@3 29 | echo "/usr/local/opt/make/libexec/gnubin" >> $GITHUB_PATH 30 | 31 | - name: Checkout XXTouchNG/theos 32 | uses: actions/checkout@v4 33 | with: 34 | repository: XXTouchNG/theos 35 | ref: 78ee784d8d3238982c9abdc58cd39919263648b1 36 | path: theos 37 | submodules: recursive 38 | 39 | - name: Add THEOS environment variables 40 | run: | 41 | rm -rf $GITHUB_WORKSPACE/theos/sdks 42 | echo "THEOS=$GITHUB_WORKSPACE/theos" >> $GITHUB_ENV 43 | 44 | - name: Restore additional SDKs 45 | id: cached-sdks-restore 46 | uses: actions/cache/restore@v4 47 | with: 48 | path: ${{ env.THEOS }}/sdks 49 | key: ${{ runner.os }}-sdks-${{ env.XCODE_VERSION }} 50 | 51 | - if: ${{ steps.cached-sdks-restore.outputs.cache-hit != 'true' }} 52 | name: Checkout theos/sdks 53 | uses: actions/checkout@v4 54 | with: 55 | repository: theos/sdks 56 | ref: master 57 | path: ${{ env.THEOS }}/sdks 58 | 59 | - if: ${{ steps.cached-sdks-restore.outputs.cache-hit != 'true' }} 60 | name: Save SDKs 61 | id: cached-sdks-save 62 | uses: actions/cache/save@v4 63 | with: 64 | path: ${{ env.THEOS }}/sdks 65 | key: ${{ steps.cached-sdks-restore.outputs.cache-primary-key }} 66 | 67 | - name: Checkout 68 | uses: actions/checkout@v4 69 | with: 70 | path: Helium 71 | submodules: recursive 72 | 73 | - name: Setup build environment 74 | run: | 75 | echo "Available SDKs: $(find $THEOS/sdks -name "*.sdk" -maxdepth 1 -print)" 76 | sT=$(TZ=UTC-8 date +'%S') 77 | echo "msT=$(date -j -f "%Y-%m-%d %H:%M:%S" "$(TZ=UTC-8 date +'%Y-%m-%d %H:%M'):${sT}" +%s)" >> $GITHUB_ENV 78 | echo "logT=$(TZ=UTC-8 date +'%Y-%m-%d %H:%M'):${sT}" >> $GITHUB_ENV 79 | echo $msT 80 | echo $logT 81 | 82 | - name: Make IPA 83 | run: | 84 | cd $GITHUB_WORKSPACE/Helium 85 | ./ipabuild.sh --debug 86 | 87 | - name: Collect symbols 88 | run: | 89 | cd $GITHUB_WORKSPACE/Helium 90 | find .theos/obj -name "*.dSYM" -print | zip -r build/Helium_symbols.zip -@ 91 | 92 | - name: Upload artifacts 93 | uses: actions/upload-artifact@v4 94 | with: 95 | name: Helium.tipa 96 | path: ${{ github.workspace }}/Helium/build/Helium.tipa 97 | 98 | - name: Upload symbols 99 | uses: actions/upload-artifact@v4 100 | with: 101 | name: Helium_symbols 102 | path: ${{ github.workspace }}/Helium/build/Helium_symbols.zip 103 | 104 | - name: Upload testing release 105 | uses: softprops/action-gh-release@v1 106 | if: startsWith(github.ref, 'refs/tags/test') 107 | with: 108 | name: "for testing" 109 | token: ${{ secrets.GITHUB_TOKEN }} 110 | body: build time ${{ env.logT }} 111 | generate_release_notes: true 112 | prerelease: true 113 | files: | 114 | ${{ github.workspace }}/Helium/build/Helium.tipa -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Build Helium Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*.*.**" 7 | 8 | env: 9 | THEOS: '' 10 | XCODE_VERSION: '15.1' 11 | logT: '' 12 | 13 | jobs: 14 | build: 15 | runs-on: macos-13 16 | 17 | steps: 18 | - name: Setup Xcode version 19 | uses: maxim-lobanov/setup-xcode@v1 20 | with: 21 | xcode-version: ${{ env.XCODE_VERSION }} 22 | 23 | - name: Install Homebrew dependencies 24 | run: | 25 | HOMEBREW_NO_AUTO_UPDATE=1 brew install dpkg ldid make libplist openssl@3 26 | echo "/usr/local/opt/make/libexec/gnubin" >> $GITHUB_PATH 27 | 28 | - name: Checkout XXTouchNG/theos 29 | uses: actions/checkout@v4 30 | with: 31 | repository: XXTouchNG/theos 32 | ref: 78ee784d8d3238982c9abdc58cd39919263648b1 33 | path: theos 34 | submodules: recursive 35 | 36 | - name: Add THEOS environment variables 37 | run: | 38 | rm -rf $GITHUB_WORKSPACE/theos/sdks 39 | echo "THEOS=$GITHUB_WORKSPACE/theos" >> $GITHUB_ENV 40 | 41 | - name: Restore additional SDKs 42 | id: cached-sdks-restore 43 | uses: actions/cache/restore@v4 44 | with: 45 | path: ${{ env.THEOS }}/sdks 46 | key: ${{ runner.os }}-sdks-${{ env.XCODE_VERSION }} 47 | 48 | - if: ${{ steps.cached-sdks-restore.outputs.cache-hit != 'true' }} 49 | name: Checkout theos/sdks 50 | uses: actions/checkout@v4 51 | with: 52 | repository: theos/sdks 53 | ref: master 54 | path: ${{ env.THEOS }}/sdks 55 | 56 | - if: ${{ steps.cached-sdks-restore.outputs.cache-hit != 'true' }} 57 | name: Save SDKs 58 | id: cached-sdks-save 59 | uses: actions/cache/save@v4 60 | with: 61 | path: ${{ env.THEOS }}/sdks 62 | key: ${{ steps.cached-sdks-restore.outputs.cache-primary-key }} 63 | 64 | - name: Checkout 65 | uses: actions/checkout@v4 66 | with: 67 | path: Helium 68 | submodules: recursive 69 | 70 | - name: Setup build environment 71 | run: | 72 | echo "Available SDKs: $(find $THEOS/sdks -name "*.sdk" -maxdepth 1 -print)" 73 | sT=$(TZ=UTC-8 date +'%S') 74 | echo "msT=$(date -j -f "%Y-%m-%d %H:%M:%S" "$(TZ=UTC-8 date +'%Y-%m-%d %H:%M'):${sT}" +%s)" >> $GITHUB_ENV 75 | echo "logT=$(TZ=UTC-8 date +'%Y-%m-%d %H:%M'):${sT}" >> $GITHUB_ENV 76 | echo $msT 77 | echo $logT 78 | 79 | - name: Make IPA 80 | run: | 81 | cd $GITHUB_WORKSPACE/Helium 82 | ./ipabuild.sh 83 | 84 | - name: Collect symbols 85 | run: | 86 | cd $GITHUB_WORKSPACE/Helium 87 | find .theos/obj -name "*.dSYM" -print | zip -r build/Helium_symbols.zip -@ 88 | 89 | - name: Upload artifacts 90 | uses: actions/upload-artifact@v4 91 | with: 92 | name: Helium.tipa 93 | path: ${{ github.workspace }}/Helium/build/Helium.tipa 94 | 95 | - name: Upload symbols 96 | uses: actions/upload-artifact@v4 97 | with: 98 | name: Helium_symbols 99 | path: ${{ github.workspace }}/Helium/build/Helium_symbols.zip 100 | 101 | - name: Upload release 102 | uses: softprops/action-gh-release@v1 103 | if: startsWith(github.ref, 'refs/tags/v') 104 | with: 105 | token: ${{ secrets.GITHUB_TOKEN }} 106 | body: build time ${{ env.logT }} 107 | generate_release_notes: true 108 | files: | 109 | ${{ github.workspace }}/Helium/build/Helium.tipa -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .theos 2 | .DS_Store 3 | build 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ARCHS := arm64 arm64e 2 | TARGET := iphone:clang:16.5:14.0 3 | INSTALL_TARGET_PROCESSES := Helium 4 | 5 | TARGET_CC := $(shell xcrun --sdk iphoneos --find clang) 6 | TARGET_CXX := $(shell xcrun --sdk iphoneos --find clang++) 7 | TARGET_LD := $(shell xcrun --sdk iphoneos --find clang++) 8 | 9 | include $(THEOS)/makefiles/common.mk 10 | APPLICATION_NAME = Helium 11 | 12 | SRC_DIR := src 13 | # Directories 14 | APP_DIR := $(SRC_DIR)/app 15 | BRIDGING_DIR := $(SRC_DIR)/bridging 16 | CONTROLLERS_DIR := $(SRC_DIR)/controllers 17 | EXTENSIONS_DIR := $(SRC_DIR)/extensions 18 | 19 | HELPERS_DIR := $(SRC_DIR)/helpers 20 | PRIV_DIR := $(HELPERS_DIR)/private_headers 21 | TS_DIR := $(HELPERS_DIR)/ts 22 | 23 | HUD_DIR := $(SRC_DIR)/hud 24 | 25 | VIEWS_DIR := $(SRC_DIR)/views 26 | NAVVIEWS_DIR := $(VIEWS_DIR)/navigation 27 | WIDGETVIEWS_DIR := $(VIEWS_DIR)/widget 28 | WIDGETSETVIEWS_DIR := $(VIEWS_DIR)/widgetset 29 | 30 | WIDGETS_DIR := $(SRC_DIR)/widgets 31 | 32 | $(APPLICATION_NAME)_USE_MODULES := 0 33 | # Add Files From Directories 34 | $(APPLICATION_NAME)_FILES += $(wildcard $(APP_DIR)/*.mm) 35 | $(APPLICATION_NAME)_FILES += $(wildcard $(BRIDGING_DIR)/*.m) 36 | $(APPLICATION_NAME)_FILES += $(wildcard $(CONTROLLERS_DIR)/*.swift) 37 | $(APPLICATION_NAME)_FILES += $(wildcard $(EXTENSIONS_DIR)/*.swift) 38 | $(APPLICATION_NAME)_FILES += $(wildcard $(EXTENSIONS_DIR)/*.mm) 39 | $(APPLICATION_NAME)_FILES += $(wildcard $(PRIV_DIR)/*.m) 40 | $(APPLICATION_NAME)_FILES += $(wildcard $(TS_DIR)/*.mm) 41 | $(APPLICATION_NAME)_FILES += $(wildcard $(HUD_DIR)/*.mm) 42 | $(APPLICATION_NAME)_FILES += $(wildcard $(VIEWS_DIR)/*.swift) 43 | $(APPLICATION_NAME)_FILES += $(wildcard $(NAVVIEWS_DIR)/*.swift) 44 | $(APPLICATION_NAME)_FILES += $(wildcard $(WIDGETVIEWS_DIR)/*.swift) 45 | $(APPLICATION_NAME)_FILES += $(wildcard $(WIDGETSETVIEWS_DIR)/*.swift) 46 | $(APPLICATION_NAME)_FILES += $(wildcard $(WIDGETS_DIR)/*.mm) 47 | $(APPLICATION_NAME)_FILES += $(wildcard $(WIDGETS_DIR)/*.m) 48 | 49 | $(APPLICATION_NAME)_CFLAGS += -fobjc-arc -Iinclude 50 | $(APPLICATION_NAME)_CFLAGS += -include hud-prefix.pch -Wno-deprecated-declarations 51 | $(APPLICATION_NAME)_SWIFTFLAGS += -import-objc-header src/bridging/Helium-Bridging-Header.h 52 | 53 | $(APPLICATION_NAME)_FRAMEWORKS += CoreGraphics QuartzCore UIKit Foundation 54 | $(APPLICATION_NAME)_PRIVATE_FRAMEWORKS += BackBoardServices GraphicsServices IOKit SpringBoardServices 55 | 56 | ifeq ($(TARGET_CODESIGN),ldid) 57 | $(APPLICATION_NAME)_CODESIGN_FLAGS += -Sent.plist 58 | else 59 | $(APPLICATION_NAME)_CODESIGN_FLAGS += --entitlements ent.plist $(TARGET_CODESIGN_FLAGS) 60 | endif 61 | 62 | include $(THEOS_MAKE_PATH)/application.mk 63 | 64 | after-stage:: 65 | $(ECHO_NOTHING)mkdir -p packages $(THEOS_STAGING_DIR)/Payload$(ECHO_END) 66 | $(ECHO_NOTHING)cp -rp $(THEOS_STAGING_DIR)/Applications/Helium.app $(THEOS_STAGING_DIR)/Payload$(ECHO_END) 67 | $(ECHO_NOTHING)cd $(THEOS_STAGING_DIR); zip -qr Helium.tipa Payload; cd -;$(ECHO_END) 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Helium 2 | Status Bar Widgets for TrollStore iPhones on iOS 14+. Works on Jailbroken devices as well. 3 | 4 | More widgets to come in future updates! 5 | 6 | **Note:** on iOS 16+, you must enable developer mode for this to work properly. 7 | 8 | ## Building 9 | [Theos](https://theos.dev) is required to compile the app. The SDK used is iOS 15.0, but you can use any SDK you want. 10 | To change the SDK, go to the `Makefile` and modify the `TARGET` to your SDK version: 11 | ``` 12 | TARGET := iphone:clang:[SDK Version]:[Minimum Version] 13 | ``` 14 | Run `./ipabuild.sh` to build the ipa. The resulting tipa should be in a folder called 'build'. 15 | 16 | ## Tested Devices 17 | - iPhone 13 Pro (iOS 15.3.1, Jailed & Jailbroken) 18 | - iPhone X (iOS 16.1.1, Jailed & Jailbroken) 19 | - iPhone X (iOS 16.6.1, Jailed) 20 | - iPad 7th Generation (iOS 14.8.1, Jailed & Jailbroken) 21 | - iPad 7th Generation (iOS 16.7.2, Jailbroken) 22 | 23 | ## Credits 24 | - [TrollSpeed](https://github.com/Lessica/TrollSpeed) for the AssistiveTouch logic allowing this to work. 25 | - [Cowabunga](https://github.com/leminlimez/Cowabunga) for part of the code. 26 | - [AsakuraFuuko](https://github.com/AsakuraFuuko) for forking and updating. -------------------------------------------------------------------------------- /ent.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.AXMediaUtilitiesService-access 6 | 7 | com.apple.DragUI.accessibility 8 | 9 | com.apple.QuartzCore.displayable-context 10 | 11 | com.apple.QuartzCore.secure-mode 12 | 13 | com.apple.accessibility.api 14 | 15 | com.apple.accessibility.physicalinteraction.client 16 | 17 | com.apple.accessibility.zoom.client 18 | 19 | com.apple.assistivetouch.daemon 20 | 21 | com.apple.backboard.client 22 | 23 | com.apple.backboardd.global-pointer-event-routing 24 | 25 | com.apple.backboardd.launchapplications 26 | 27 | com.apple.bluetooth.system 28 | 29 | com.apple.carousel.backlightaccess 30 | 31 | com.apple.coreaudio.allow-opus-codec 32 | 33 | com.apple.coreaudio.register-internal-aus 34 | 35 | com.apple.developer.ubiquity-kvstore-identifier 36 | com.leemin.helium 37 | com.apple.diagnosticd.diagnostic 38 | 39 | com.apple.frontboard.debugapplications 40 | 41 | com.apple.hid.manager.user-access-device 42 | 43 | com.apple.hid.manager.user-access-keyboard 44 | 45 | com.apple.idle-timer-services 46 | 47 | com.apple.managedconfiguration.profiled-access 48 | 49 | com.apple.pointerui.persistentlyHidePointer 50 | 51 | com.apple.private.CoreAuthentication.SPI 52 | 53 | com.apple.private.LocalAuthentication.Storage 54 | 55 | com.apple.private.acccessibility.motionTrackingClient 56 | 57 | com.apple.private.appleaccount.app-hidden-from-icloud-settings 58 | 59 | com.apple.private.applecredentialmanager.allow 60 | 61 | com.apple.private.assets.accessible-asset-types 62 | 63 | com.apple.MobileAsset.VoiceServices.CustomVoice 64 | com.apple.MobileAsset.VoiceServicesVocalizerVoice 65 | com.apple.MobileAsset.VoiceServices.GryphonVoice 66 | com.apple.MobileAsset.MacinTalkVoiceAssets 67 | com.apple.MobileAsset.VoiceServices.CombinedVocalizerVoices 68 | 69 | com.apple.private.avfoundation.capture.nonstandard-client.allow 70 | 71 | com.apple.private.coreaudio.viewInterruptorName.allow 72 | 73 | com.apple.private.hid.client.event-dispatch 74 | 75 | com.apple.private.hid.client.event-filter 76 | 77 | com.apple.private.hid.client.event-monitor 78 | 79 | com.apple.private.hid.client.service-protected 80 | 81 | com.apple.private.hid.manager.client 82 | 83 | com.apple.private.ids.messaging 84 | 85 | com.apple.private.alloy.accessibility.switchcontrol 86 | 87 | com.apple.private.ids.messaging.urgent-priority 88 | 89 | com.apple.private.alloy.accessibility.switchcontrol 90 | 91 | com.apple.private.kernel.jetsam 92 | 93 | com.apple.private.memorystatus 94 | 95 | com.apple.private.persona-mgmt 96 | 97 | com.apple.private.security.no-sandbox 98 | 99 | com.apple.private.sysdiagnose 100 | 101 | com.apple.private.ubiquity-additional-kvstore-identifiers 102 | 103 | com.apple.AssistiveTouch 104 | com.apple.Accessibility.SwitchControl 105 | com.apple.Accessibility 106 | 107 | com.apple.security.exception.mach-lookup.global-name 108 | 109 | com.apple.accessibility.motiontrackingd 110 | com.apple.accessibility.AXSpringBoardServer 111 | com.apple.accessibility.AXPineBoardServer 112 | com.apple.accessibility.AXCarouselServer 113 | AXPerformanceTestReportingServer 114 | com.apple.telephonyutilities.callservicesdaemon.callstatecontroller 115 | com.apple.AXMediaUtilitiesService 116 | com.apple.audio.hapticd 117 | AccessibilityDebuggerServices 118 | com.apple.sysdiagnose.service.xpc 119 | com.apple.TextInput 120 | com.apple.SBUserNotification 121 | com.apple.siri.activation.service 122 | com.apple.commandandcontrol 123 | com.apple.sharing.accessibility 124 | com.apple.TextInput.accessibility 125 | com.apple.appleneuralengine 126 | com.apple.fontservicesd 127 | com.apple.PointerUI.pointeruid.service 128 | 129 | com.apple.security.exception.mach-lookup.local-name 130 | 131 | com.apple.AXMediaUtilitiesService 132 | 133 | com.apple.security.exception.mach-register.global-name 134 | 135 | com.leemin.helium.gsEvents 136 | 137 | com.apple.security.exception.mach-register.local-name 138 | 139 | com.leemin.helium 140 | 141 | com.apple.security.exception.shared-preference.read-only 142 | 143 | com.apple.purplebuddy 144 | com.apple.uikitservices.userInterfaceStyleMode 145 | 146 | com.apple.security.exception.shared-preference.read-write 147 | 148 | com.apple.assistant.logging 149 | 150 | com.apple.security.iokit-user-client-class 151 | 152 | IOSurfaceRootUserClient 153 | AGXDeviceUserClient 154 | IOPMPowerSourceClient 155 | 156 | com.apple.security.system-groups 157 | 158 | systemgroup.com.apple.powerlog 159 | 160 | com.apple.springboard-ui.client 161 | 162 | com.apple.springboard.CFUserNotification 163 | 164 | com.apple.springboard.accessibility-window-hosting 165 | 166 | com.apple.springboard.activateassistant 167 | 168 | com.apple.springboard.appbackgroundstyle 169 | 170 | com.apple.springboard.launchapplications 171 | 172 | com.apple.system-task-ports 173 | 174 | com.apple.system.diagnostics.iokit-properties 175 | 176 | file-read-data 177 | 178 | get-task-allow 179 | 180 | platform-application 181 | 182 | run-unsigned-code 183 | 184 | task_for_pid-allow 185 | 186 | user-preference-read 187 | 188 | user-preference-write 189 | 190 | 191 | -------------------------------------------------------------------------------- /hud-prefix.pch: -------------------------------------------------------------------------------- 1 | #if __has_include() 2 | #import 3 | #endif 4 | 5 | #ifdef __OBJC__ 6 | #if __has_include() 7 | #import 8 | #endif 9 | 10 | #import 11 | #import "src/helpers/private_headers/NSUserDefaults+Private.h" 12 | #import 13 | #import 14 | #endif 15 | 16 | #define USER_DEFAULTS_PATH @"/var/mobile/Library/Preferences/com.leemin.helium.plist" 17 | 18 | // HUD -> APP: Notify APP that the HUD's view is appeared. 19 | #define NOTIFY_LAUNCHED_HUD "com.leemin.notification.hud.launched" 20 | 21 | // APP -> HUD: Notify HUD to dismiss itself. 22 | #define NOTIFY_DISMISSAL_HUD "com.leemin.notification.hud.dismissal" 23 | 24 | // APP -> HUD: Notify HUD that the user defaults has been changed by APP. 25 | #define NOTIFY_RELOAD_HUD "com.leemin.notification.hud.reload" -------------------------------------------------------------------------------- /ipabuild.sh: -------------------------------------------------------------------------------- 1 | if [[ $* == *--scriptdebug* ]]; then 2 | set -x 3 | fi 4 | set -e 5 | 6 | WORKING_LOCATION="$(pwd)" 7 | APP_BUILD_FILES="$WORKING_LOCATION/layout/Applications/Helium.app" 8 | DEBUG_LOCATION="$WORKING_LOCATION/.theos/obj/debug" 9 | RELEASE_LOCATION="$WORKING_LOCATION/.theos/obj" 10 | if [[ $* == *--debug* ]]; then 11 | BUILD_LOCATION="$DEBUG_LOCATION/Helium.app" 12 | else 13 | BUILD_LOCATION="$RELEASE_LOCATION/Helium.app" 14 | fi 15 | 16 | if [[ $* == *--clean* ]]; then 17 | echo "[*] Cleaning..." 18 | rm -rf build 19 | make clean 20 | fi 21 | 22 | if [ ! -d "build" ]; then 23 | mkdir build 24 | fi 25 | #remove existing archive if there 26 | if [ -d "build/Helium.tipa" ]; then 27 | rm -rf "build/Helium.tipa" 28 | fi 29 | 30 | if ! type "gmake" >/dev/null; then 31 | echo "[!] gmake not found, using macOS bundled make instead" 32 | make clean 33 | if [[ $* == *--debug* ]]; then 34 | make 35 | else 36 | make FINALPACKAGE=1 37 | fi 38 | else 39 | gmake clean 40 | if [[ $* == *--debug* ]]; then 41 | gmake -j"$(sysctl -n machdep.cpu.thread_count)" 42 | else 43 | gmake -j"$(sysctl -n machdep.cpu.thread_count)" FINALPACKAGE=1 44 | fi 45 | fi 46 | 47 | if [ -d $BUILD_LOCATION ]; then 48 | # Add the necessary files 49 | echo "Adding application files" 50 | cp -r "$APP_BUILD_FILES/icon.png" "$BUILD_LOCATION/icon.png" 51 | cp -r "$APP_BUILD_FILES/Info.plist" "$BUILD_LOCATION/Info.plist" 52 | cp -r "$APP_BUILD_FILES/Assets.car" "$BUILD_LOCATION/Assets.car" 53 | cp -r "$APP_BUILD_FILES/en.lproj" "$BUILD_LOCATION/" 54 | cp -r "$APP_BUILD_FILES/zh-Hans.lproj" "$BUILD_LOCATION/" 55 | cp -r "$APP_BUILD_FILES/fonts" "$BUILD_LOCATION/" 56 | cp -r "$APP_BUILD_FILES/credits" "$BUILD_LOCATION/" 57 | 58 | # Create payload 59 | echo "Creating payload" 60 | cd build 61 | mkdir Payload 62 | cp -r $BUILD_LOCATION Payload/Helium.app 63 | 64 | # Archive 65 | echo "Archiving" 66 | if [[ $* != *--debug* ]]; then 67 | strip Payload/Helium.app/Helium 68 | fi 69 | zip -vr Helium.tipa Payload 70 | rm -rf Helium.app 71 | rm -rf Payload 72 | fi 73 | -------------------------------------------------------------------------------- /layout/Applications/Helium.app/Assets.car: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leminlimez/Helium/213f2302d2d85afe108b0985f9fe53e7ca44ef6a/layout/Applications/Helium.app/Assets.car -------------------------------------------------------------------------------- /layout/Applications/Helium.app/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | com.leemin.helium 7 | CFBundleInfoDictionaryVersion 8 | 6.0 9 | CFBundleName 10 | Helium 11 | CFBundleDisplayName 12 | Helium 13 | CFBundleVersion 14 | 3.2.5 15 | CFBundleExecutable 16 | Helium 17 | NSPrincipalClass 18 | HUDMainApplication 19 | CFBundlePackageType 20 | APPL 21 | CFBundleIconFile 22 | icon.png 23 | CFBundleSignature 24 | ???? 25 | UIApplicationShowsViewsWhileLocked 26 | 27 | UIApplicationSystemWindowsSecureKey 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationLandscapeLeft 32 | UIInterfaceOrientationLandscapeRight 33 | UIInterfaceOrientationPortrait 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationLandscapeLeft 38 | UIInterfaceOrientationLandscapeRight 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | 42 | CFBundleURLTypes 43 | 44 | 45 | CFBundleTypeRole 46 | Editor 47 | CFBundleURLName 48 | com.leemin.helium 49 | CFBundleURLSchemes 50 | 51 | helium 52 | 53 | 54 | 55 | LSApplicationCategoryType 56 | public.app-category.developer-tools 57 | CFBundleLocalizations 58 | 59 | zh 60 | en 61 | 62 | CFBundleDevelopmentRegion 63 | en 64 | UIFileSharingEnabled 65 | 66 | NSHumanReadableCopyright 67 | Copyright © 2009 Apple Inc. All Rights Reserved. 68 | CFBundleShortVersionString 69 | 3.2.6 70 | 71 | 72 | -------------------------------------------------------------------------------- /layout/Applications/Helium.app/credits/bomberfish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leminlimez/Helium/213f2302d2d85afe108b0985f9fe53e7ca44ef6a/layout/Applications/Helium.app/credits/bomberfish.png -------------------------------------------------------------------------------- /layout/Applications/Helium.app/credits/fuuko.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leminlimez/Helium/213f2302d2d85afe108b0985f9fe53e7ca44ef6a/layout/Applications/Helium.app/credits/fuuko.png -------------------------------------------------------------------------------- /layout/Applications/Helium.app/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* Alert++.swift */ 2 | "Error" = "Error"; 3 | "OK" = "OK"; 4 | "Cancel" = "Cancel"; 5 | "Text" = "Text"; 6 | 7 | /* ViewExtensions.swift */ 8 | "Enter Value" = "Enter Value"; 9 | "Enter a value." = "Enter a value."; 10 | 11 | /* HomePageView.swift */ 12 | "You can quit the app now.\nThe HUD will persist on your screen." = "You can quit the app now.\nThe HUD will persist on your screen."; 13 | "Stopped." = "Stopped."; 14 | "Disable HUD" = "Disable HUD"; 15 | "Enable HUD" = "Enable HUD"; 16 | "Helium" = "Helium"; 17 | "Closing HUD" = "Closing HUD"; 18 | "Opening HUD" = "Opening HUD"; 19 | "Status: Running" = "Status: Running"; 20 | "Status: Stopped" = "Status: Stopped"; 21 | 22 | /* SettingsView.swift */ 23 | "Save" = "Save"; 24 | "Version " = "Version "; 25 | "UNKNOWN" = "UNKNOWN"; 26 | "Release" = "Release"; 27 | "Date Locale" = "Date Locale"; 28 | "Weather Api key" = "QWeather Api key"; 29 | "Hide Save Confirmation Popup" = "Hide Save Confirmation Popup"; 30 | "Display Debug Border" = "Display Debug Border"; 31 | "Use Adaptive Colors" = "Use Adaptive Colors"; 32 | "Helium Data" = "Helium Data"; 33 | "Successfully deleted user data!" = "Successfully deleted user data!"; 34 | "Please restart the app to continue." = "Please restart the app to continue."; 35 | "Failed to delete user data!" = "Failed to delete user data!"; 36 | "Reset Data" = "Reset Data"; 37 | "Preferences" = "Preferences"; 38 | "Side Widget Size" = "Side Widget Size"; 39 | "Side Size" = "Side Size"; 40 | "Center Widget Size" = "Center Widget Size"; 41 | "Center Size" = "Center Size"; 42 | "Debug Preferences" = "Debug Preferences"; 43 | "Main Developer" = "Main Developer"; 44 | "Modder" = "Modder"; 45 | "TrollSpeed & Assistive Touch Logic" = "TrollSpeed & Assistive Touch Logic"; 46 | "UI improvements" = "UI improvements"; 47 | "Credits" = "Credits"; 48 | "Settings saved successfully" = "Settings saved successfully"; 49 | 50 | /* WidgetCustomizationView.swift */ 51 | "Delete " = "Delete "; 52 | "Are you sure you want to delete the widget set " = "Are you sure you want to delete the widget set "; 53 | "Enter Name" = "Enter Name"; 54 | "Choose a name for the widget set." = "Choose a name for the widget set."; 55 | "Confirm" = "Confirm"; 56 | "Name" = "Name"; 57 | "Choose Side" = "Choose Side"; 58 | "Choose a side for the widget set to anchor to. This can be changed later." = "Choose a side for the widget set to anchor to. This can be changed later."; 59 | "Left" = "Left"; 60 | "Center" = "Center"; 61 | "Right" = "Right"; 62 | "Untitled" = "Untitled"; 63 | "Customize" = "Customize"; 64 | 65 | /* WidgetPreferencesView.swift */ 66 | "Date Format" = "Date Format"; 67 | "E MMM dd" = "E MMM dd"; 68 | "Network Type" = "Network Type"; 69 | "Download" = "Download"; 70 | "Upload" = "Upload"; 71 | "Speed Icon" = "Speed Icon"; 72 | "Minimum Unit" = "Minimum Unit"; 73 | "Hide Speed When 0" = "Hide Speed When 0"; 74 | "Temperature Unit" = "Temperature Unit"; 75 | "Celcius" = "Celcius"; 76 | "Fahrenheit" = "Fahrenheit"; 77 | "Battery Option" = "Battery Option"; 78 | "Watts" = "Watts"; 79 | "Charging Current" = "Charging Current"; 80 | "Amperage" = "Amperage"; 81 | "Charge Cycles" = "Charge Cycles"; 82 | "Current Capacity" = "Current Capacity"; 83 | "Time Format" = "Time Format"; 84 | "Label Text" = "Label Text"; 85 | "Example" = "Example"; 86 | "Location" = "LocationID or Latitude,Longitude"; 87 | "Input" = "Input"; 88 | "Get" = "Get"; 89 | "Input Location Name" = "Input Location Name"; 90 | "Get Location ID" = "Get Location ID"; 91 | "Show Percent (%) Symbol" = "Show Percent (%) Symbol"; 92 | "Fill Symbol" = "Fill Symbol"; 93 | "No Configurable Aspects" = "No Configurable Aspects"; 94 | "Save Changes" = "Save Changes"; 95 | "Would you like to save changes to the widget?" = "Would you like to save changes to the widget?"; 96 | "Weather Format Now" = "Now Weather Format Explanation:\n{i} Weather Icon\t\t\t{n} Weather Text\n{t} Current Temperature\t{h} Humidity\n{w} Wind Direction\t\t{wp} Wind Power\n{bt} Body Temperature\t{pc} Precipitation\n{ps} Pressure\t\t\t\t{v} Visibility\n{c} Cloud Cover"; 97 | "Weather Format Today" = "Today Weather Format Explanation:\n{di} Daytime Weather Icon\n{dn} Daytime Weather Text\n{dt} Daytime Temperature\n{dw} Daytime Wind Direction\n{dwp} Daytime Wind Power\n{ni} Nighttime Weather Icon\n{nn} Nighttime Weather Text\n{nt} Nighttime Temperature\n{nw} Nighttime Wind Direction\n{nwp} Nighttime Wind Power\n{tpc} Today Precipitation\n{tuv} Today UV Index\n{th} Today Humidity\n{tps} Today Pressure\n{tv} Today Visibility\n{tc} Today Cloud Cover"; 98 | "Format" = "Format"; 99 | 100 | /* WidgetPreviewsView.swift */ 101 | "ERROR" = "ERROR"; 102 | "Unknown" = "Unknown"; 103 | "Weather Preview" = "🌤Sun 15°~20° (20°)💧54%"; 104 | 105 | /* EditWidgetSetView.swift */ 106 | "Enable" = "Enable Widget Set"; 107 | "Orientation Mode" = "Orientation Mode"; 108 | "Portrait & Landscape" = "Portrait & Landscape"; 109 | "Portrait" = "Portrait"; 110 | "Landscape" = "Landscape"; 111 | "Widget Set Title" = "Widget Set Title"; 112 | "Title" = "Title"; 113 | "Update Interval (seconds)" = "Update Interval (seconds)"; 114 | "Seconds" = "Seconds"; 115 | "Widget Set Details" = "Widget Set Details"; 116 | "Horizontal Anchor Side" = "Horizontal Anchor Side"; 117 | "Vertical Anchor Side" = "Vertical Anchor Side"; 118 | "Top" = "Top"; 119 | "Bottom" = "Bottom"; 120 | "Portrait Offset X" = "Portrait Offset X"; 121 | "Portrait Offset Y" = "Portrait Offset Y"; 122 | "Landscape Offset X" = "Offset X"; 123 | "Landscape Offset Y" = "Offset Y"; 124 | "Positioning" = "Positioning"; 125 | "Auto Resize" = "Auto Resize"; 126 | "Width" = "Width"; 127 | "Height" = "Height"; 128 | "Size Constraints" = "Size Constraints"; 129 | "Background Blur" = "Background Blur"; 130 | "Blur Style" = "Blur Style"; 131 | "Light" = "Light"; 132 | "Dark" = "Dark"; 133 | "Blur Corner Radius" = "Blur Corner Radius"; 134 | "Blur Alpha" = "Blur Alpha"; 135 | "Blur" = "Blur"; 136 | "Custom Text Color" = "Custom Text Color"; 137 | "Text Color" = "Text Color"; 138 | "Set Text Color" = "Set Text Color"; 139 | "Adaptive Color" = "Adaptive Color"; 140 | "Text Font" = "Text Font"; 141 | "Font Preview" = "Preview Abc123"; 142 | "Bold Text" = "Bold Text"; 143 | "Italic Text" = "Italic Text"; 144 | "Text Alignment" = "Text Alignment"; 145 | "Font Size" = "Font Size"; 146 | "Text Alpha" = "Text Alpha"; 147 | "Text Properties" = "Text Properties"; 148 | "Add Widget" = "Add Widget"; 149 | "Delete Widget" = "Delete Widget"; 150 | "Are you sure you want to delete this widget?" = "Are you sure you want to delete this widget?"; 151 | "Widgets" = "Widgets"; 152 | "Edit Widget" = "Edit Widget"; 153 | "Would you like to save the changes to your current widget set?" = "Would you like to save the changes to your current widget set?"; 154 | 155 | /* UIApplicationViews.swift */ 156 | "Home" = "Home"; 157 | "Settings" = "Settings"; 158 | "Info" = "Info"; 159 | "Make sure you enable developer mode before using! This will not work otherwise." = "Make sure you enable developer mode before using! This will not work otherwise."; 160 | "Not Supported" = "Not Supported"; 161 | "This app must be installed with TrollStore." = "This app must be installed with TrollStore."; 162 | 163 | /* WidgetManager.swift */ 164 | "Untitled" = "Untitled"; 165 | "Date" = "Date"; 166 | "Mon Oct 16" = "Mon Oct 16"; 167 | "Network" = "Network"; 168 | "Device Temperature" = "Device Temperature"; 169 | "Battery Details" = "Battery Details"; 170 | "Time" = "Time"; 171 | "Text Label" = "Text Label"; 172 | "Example" = "Example"; 173 | "Battery Capacity" = "Battery Capacity"; 174 | "Charging Symbol" = "Charging Symbol"; 175 | "Weather" = "Weather"; 176 | 177 | /* WeatherUtils.mm */ 178 | "error" = "error"; -------------------------------------------------------------------------------- /layout/Applications/Helium.app/fonts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leminlimez/Helium/213f2302d2d85afe108b0985f9fe53e7ca44ef6a/layout/Applications/Helium.app/fonts/.gitkeep -------------------------------------------------------------------------------- /layout/Applications/Helium.app/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leminlimez/Helium/213f2302d2d85afe108b0985f9fe53e7ca44ef6a/layout/Applications/Helium.app/icon.png -------------------------------------------------------------------------------- /layout/Applications/Helium.app/zh-Hans.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* Alert++.swift */ 2 | "Error" = "出错了"; 3 | "OK" = "确定"; 4 | "Cancel" = "取消"; 5 | "Text" = "文字段"; 6 | 7 | /* ViewExtensions.swift */ 8 | "Enter Value" = "请输入值"; 9 | "Enter a value." = "请输入一个值"; 10 | 11 | /* HomePageView.swift */ 12 | "You can quit the app now.\nThe HUD will persist on your screen." = "您现在可以退出应用。\nHUD 将持续显示在屏幕上。"; 13 | "Stopped." = "已停止"; 14 | "Disable HUD" = "关闭 HUD"; 15 | "Enable HUD" = "启用 HUD"; 16 | "Helium" = "氦气"; 17 | "Closing HUD" = "正在关闭 HUD"; 18 | "Opening HUD" = "正在打开 HUD"; 19 | "Status: Running" = "状态: 运行中"; 20 | "Status: Stopped" = "状态: 已停止"; 21 | 22 | /* SettingsView.swift */ 23 | "Save" = "保存"; 24 | "Version " = "版本 "; 25 | "UNKNOWN" = "未知"; 26 | "Release" = "发布"; 27 | "Date Locale" = "日期地区"; 28 | "Weather Api key" = "和风天气ApiKey"; 29 | "Show when Rotating" = "旋转屏幕时显示"; 30 | "Hide Save Confirmation Popup" = "不显示保存确认弹窗"; 31 | "Display Debug Border" = "显示调试边框"; 32 | "Use Adaptive Colors" = "使用自适应颜色"; 33 | "Helium Data" = "氦气数据"; 34 | "Successfully deleted user data!" = "已成功删除用户数据!"; 35 | "Please restart the app to continue." = "请重启应用以继续"; 36 | "Failed to delete user data!" = "删除用户数据失败!"; 37 | "Reset Data" = "重置数据"; 38 | "Preferences" = "偏好设置"; 39 | "Side Widget Size" = "侧边小部件大小"; 40 | "Side Size" = "侧边尺寸"; 41 | "Center Widget Size" = "中心小部件大小"; 42 | "Center Size" = "中心尺寸"; 43 | "Debug Preferences" = "调试设置"; 44 | "Main Developer" = "主开发者"; 45 | "Modder" = "修改者"; 46 | "TrollSpeed & Assistive Touch Logic" = "TrollSpeed & Assistive Touch 逻辑"; 47 | "UI improvements" = "UI优化"; 48 | "Credits" = "致谢"; 49 | "Settings saved successfully" = "设置保存成功"; 50 | 51 | /* WidgetCustomizationView.swift */ 52 | "Delete " = "删除 "; 53 | "Are you sure you want to delete the widget set " = "确认要删除小部件集 "; 54 | "Enter Name" = "输入名称"; 55 | "Choose a name for the widget set." = "为小部件集选择一个名称"; 56 | "Confirm" = "确认"; 57 | "Name" = "名称"; 58 | "Choose Side" = "选择显示位置"; 59 | "Choose a side for the widget set to anchor to. This can be changed later." = "选择小部件集的显示位置。稍后可以更改"; 60 | "Left" = "左边"; 61 | "Center" = "中间"; 62 | "Right" = "右边"; 63 | "Untitled" = "未命名"; 64 | "Customize" = "自定义"; 65 | 66 | /* WidgetPreferencesView.swift */ 67 | "Date Format" = "日期格式"; 68 | "E MMM dd" = "MM月dd E"; 69 | "Network Type" = "网络类型"; 70 | "Download" = "下载"; 71 | "Upload" = "上传"; 72 | "Speed Icon" = "网速图标"; 73 | "Minimum Unit" = "最小单位"; 74 | "Hide Speed When 0" = "网速为0时隐藏"; 75 | "Temperature Unit" = "温度单位"; 76 | "Celcius" = "摄氏度"; 77 | "Fahrenheit" = "华氏度"; 78 | "Battery Option" = "电池选项"; 79 | "Watts" = "瓦特"; 80 | "Charging Current" = "充电电流"; 81 | "Amperage" = "使用电流"; 82 | "Charge Cycles" = "充电周期"; 83 | "Current Capacity" = "当前容量"; 84 | "Time Format" = "时间格式"; 85 | "Label Text" = "标签文本"; 86 | "Example" = "示例"; 87 | "Location" = "地区ID或者经纬度"; 88 | "Input" = "输入"; 89 | "Get" = "获取"; 90 | "Input Location Name" = "输入地区名称"; 91 | "Get Location ID" = "获取地区ID"; 92 | "Show Percent (%) Symbol" = "显示电量%"; 93 | "Fill Symbol" = "填充符号"; 94 | "No Configurable Aspects" = "无可配置项"; 95 | "Save Changes" = "保存更改"; 96 | "Would you like to save changes to the widget?" = "是否保存对小部件的更改?"; 97 | "Weather Format Now" = "实时天气格式说明:\n{i} 天气图标\t{n} 天气文字\n{t} 当前温度\t{h} 湿度\n{w} 风向\t\t{wp} 风力\n{bt} 体感温度\t{pc} 降水量\n{ps} 大气压力\t{v} 能见度\n{c} 云量"; 98 | "Weather Format Today" = "今日天气格式说明:\n{di} 白天天气图标\t{dn} 白天天气文本\n{dt} 白天气温\t\t{dw} 白天风向\n{dwp} 白天风力\t{nwp} 夜间风力\n{ni} 夜间天气图标\t{nn} 夜间天气文本\n{nt} 夜间气温\t\t{nw} 夜间风向\n{tpc} 今日降水量\t{tuv} 今日紫外线指数\n{th} 今日湿度\t\t{tps} 今日气压\n{tv} 今日能见度\t{tc} 今日云量"; 99 | "Format" = "格式"; 100 | 101 | /* WidgetPreviewsView.swift */ 102 | "ERROR" = "错误"; 103 | "Unknown" = "未知"; 104 | "Weather Preview" = "🌤晴 15°~20° (20°)💧54%"; 105 | 106 | /* EditWidgetSetView.swift */ 107 | "Enable" = "启用小部件集"; 108 | "Orientation Mode" = "显示模式"; 109 | "Portrait & Landscape" = "竖屏和横屏"; 110 | "Portrait" = "竖屏"; 111 | "Landscape" = "横屏"; 112 | "Widget Set Title" = "小部件集标题"; 113 | "Title" = "标题"; 114 | "Update Interval (seconds)" = "更新间隔 (秒)"; 115 | "Seconds" = "秒"; 116 | "Widget Set Details" = "小部件集详情"; 117 | "Horizontal Anchor Side" = "水平显示位置"; 118 | "Vertical Anchor Side" = "垂直显示位置"; 119 | "Top" = "顶部"; 120 | "Bottom" = "底部"; 121 | "Offset X" = "X偏移"; 122 | "Offset Y" = "Y偏移"; 123 | "Portrait Offset X" = "竖屏X偏移"; 124 | "Portrait Offset Y" = "竖屏Y偏移"; 125 | "Landscape Offset X" = "横屏X偏移"; 126 | "Landscape Offset Y" = "横屏Y偏移"; 127 | "Positioning" = "定位"; 128 | "Auto Resize" = "自动调整大小"; 129 | "Width" = "宽度"; 130 | "Height" = "高度"; 131 | "Size Constraints" = "尺寸限制"; 132 | "Background Blur" = "背景模糊"; 133 | "Blur Style" = "模糊样式"; 134 | "Light" = "明亮"; 135 | "Dark" = "黑暗"; 136 | "Blur Corner Radius" = "模糊角半径"; 137 | "Blur Alpha" = "模糊透明度"; 138 | "Blur" = "模糊"; 139 | "Custom Text Color" = "自定义文本颜色"; 140 | "Text Color" = "文本颜色"; 141 | "Set Text Color" = "设置文本颜色"; 142 | "Adaptive Color" = "自适应颜色"; 143 | "Text Font" = "文本字体"; 144 | "Font Preview" = "预览Abc123"; 145 | "Bold Text" = "粗体文本"; 146 | "Italic Text" = "斜体文本"; 147 | "Text Alignment" = "文本对齐"; 148 | "Font Size" = "字体大小"; 149 | "Text Alpha" = "文字透明度"; 150 | "Text Properties" = "文本属性"; 151 | "Add Widget" = "添加小部件"; 152 | "Delete Widget" = "删除小部件"; 153 | "Are you sure you want to delete this widget?" = "确认删除此小部件?"; 154 | "Widgets" = "小部件"; 155 | "Edit Widget" = "编辑小部件"; 156 | "Save Changes" = "保存更改"; 157 | "Would you like to save the changes to your current widget set?" = "是否保存当前小部件集的更改?"; 158 | 159 | /* UIApplicationViews.swift */ 160 | "Home" = "首页"; 161 | "Settings" = "设置"; 162 | "Info" = "信息"; 163 | "Make sure you enable developer mode before using! This will not work otherwise." = "使用前,请确保已启用开发者模式!否则无法正常工作"; 164 | "Not Supported" = "不支持"; 165 | "This app must be installed with TrollStore." = "此应用必须通过 TrollStore 安装"; 166 | 167 | /* WidgetManager.swift */ 168 | "Untitled" = "未命名"; 169 | "Date" = "日期"; 170 | "Mon Oct 16" = "10月16 周一"; 171 | "Network" = "网速"; 172 | "Device Temperature" = "设备温度"; 173 | "Battery Details" = "电池详情"; 174 | "Time" = "时间"; 175 | "Text Label" = "文本标签"; 176 | "Example" = "示例"; 177 | "Battery Capacity" = "电池容量"; 178 | "Charging Symbol" = "充电符号"; 179 | "Weather" = "天气"; 180 | 181 | /* WeatherUtils.mm */ 182 | "error" = "出错啦"; -------------------------------------------------------------------------------- /layout/DEBIAN/control: -------------------------------------------------------------------------------- 1 | Package: com.leemin.helium 2 | Name: Helium 3 | Version: 1.0 4 | Section: Tweaks 5 | Depends: firmware (>= 13.0), mobilesubstrate (>= 0.9.7000), com.rpetrich.rocketbootstrap (>= 1.0.9) 6 | Architecture: iphoneos-arm 7 | Author: lemin 8 | Maintainer: lemin 9 | Description: I am on helium 10 | 11 | -------------------------------------------------------------------------------- /layout/Library/LaunchDaemons/com.leemin.helium.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | EnablePressuredExit 6 | 7 | EnableTransactions 8 | 9 | GroupName 10 | wheel 11 | HighPriorityIO 12 | 13 | KeepAlive 14 | 15 | Label 16 | com.leemin.helium 17 | POSIXSpawnType 18 | App 19 | ProcessType 20 | Interactive 21 | ProgramArguments 22 | 23 | /Applications/Helium.app/Helium 24 | -hud 25 | 26 | RunAtLoad 27 | 28 | ThrottleInterval 29 | 5 30 | UserName 31 | root 32 | _AdditionalProperties 33 | 34 | RunningBoard 35 | 36 | Managed 37 | 38 | Reported 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/app/MainApplication.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | NS_ASSUME_NONNULL_BEGIN 4 | 5 | @interface MainApplication : UIApplication 6 | @end 7 | 8 | NS_ASSUME_NONNULL_END -------------------------------------------------------------------------------- /src/app/MainApplication.mm: -------------------------------------------------------------------------------- 1 | // 2 | // MainApplication.mm 3 | // 4 | // 5 | // Created by lemin on 10/4/23. 6 | // 7 | 8 | #import "MainApplication.h" 9 | 10 | @implementation MainApplication 11 | 12 | - (instancetype)init 13 | { 14 | self = [super init]; 15 | if (self) 16 | { 17 | #if DEBUG 18 | /* Force HIDTransformer to print logs */ 19 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogTouch" inDomain:@"com.apple.UIKit"]; 20 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogGesture" inDomain:@"com.apple.UIKit"]; 21 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogEventDispatch" inDomain:@"com.apple.UIKit"]; 22 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogGestureEnvironment" inDomain:@"com.apple.UIKit"]; 23 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogGestureExclusion" inDomain:@"com.apple.UIKit"]; 24 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogSystemGestureUpdate" inDomain:@"com.apple.UIKit"]; 25 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogGesturePerformance" inDomain:@"com.apple.UIKit"]; 26 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogHIDTransformer" inDomain:@"com.apple.UIKit"]; 27 | [[NSUserDefaults standardUserDefaults] synchronize]; 28 | #endif 29 | } 30 | return self; 31 | } 32 | 33 | @end -------------------------------------------------------------------------------- /src/app/MainApplicationDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | NS_ASSUME_NONNULL_BEGIN 4 | 5 | @interface MainApplicationDelegate : UIResponder 6 | @property (nonatomic, strong) UIWindow *window; 7 | @end 8 | 9 | NS_ASSUME_NONNULL_END -------------------------------------------------------------------------------- /src/app/MainApplicationDelegate.mm: -------------------------------------------------------------------------------- 1 | #import "MainApplicationDelegate.h" 2 | #import "MainApplication.h" 3 | #import "Helium-Swift.h" 4 | #import "../extensions/FontUtils.h" 5 | 6 | @implementation MainApplicationDelegate 7 | 8 | - (instancetype)init { 9 | if (self = [super init]) { 10 | os_log_debug(OS_LOG_DEFAULT, "- [MainApplicationDelegate init]"); 11 | } 12 | return self; 13 | } 14 | 15 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 16 | os_log_debug(OS_LOG_DEFAULT, "- [MainApplicationDelegate application:%{public}@ didFinishLaunchingWithOptions:%{public}@]", application, launchOptions); 17 | 18 | // load fonts from app 19 | [FontUtils loadFontsFromFolder:[NSString stringWithFormat:@"%@%@", [[NSBundle mainBundle] resourcePath], @"/fonts"]]; 20 | // load fonts from documents 21 | [FontUtils loadFontsFromFolder:[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]]; 22 | [FontUtils loadAllFonts]; 23 | 24 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 25 | [self.window setRootViewController:[[[ContentInterface alloc] init] createUI]]; 26 | [self.window makeKeyAndVisible]; 27 | 28 | return YES; 29 | } 30 | 31 | @end -------------------------------------------------------------------------------- /src/assets/bezels/BigNotch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leminlimez/Helium/213f2302d2d85afe108b0985f9fe53e7ca44ef6a/src/assets/bezels/BigNotch.png -------------------------------------------------------------------------------- /src/assets/bezels/DynamicIsland.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leminlimez/Helium/213f2302d2d85afe108b0985f9fe53e7ca44ef6a/src/assets/bezels/DynamicIsland.png -------------------------------------------------------------------------------- /src/assets/bezels/SmallNotch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leminlimez/Helium/213f2302d2d85afe108b0985f9fe53e7ca44ef6a/src/assets/bezels/SmallNotch.png -------------------------------------------------------------------------------- /src/assets/credits/bomberfish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leminlimez/Helium/213f2302d2d85afe108b0985f9fe53e7ca44ef6a/src/assets/credits/bomberfish.png -------------------------------------------------------------------------------- /src/assets/credits/fuuko.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leminlimez/Helium/213f2302d2d85afe108b0985f9fe53e7ca44ef6a/src/assets/credits/fuuko.png -------------------------------------------------------------------------------- /src/assets/credits/leminlimez.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leminlimez/Helium/213f2302d2d85afe108b0985f9fe53e7ca44ef6a/src/assets/credits/leminlimez.png -------------------------------------------------------------------------------- /src/assets/credits/lessica.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leminlimez/Helium/213f2302d2d85afe108b0985f9fe53e7ca44ef6a/src/assets/credits/lessica.png -------------------------------------------------------------------------------- /src/bridging/Helium-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // XXTAssistiveTouch-Bridging-Header.h 3 | // 4 | // 5 | // Created by lemin on 10/13/23. 6 | // 7 | 8 | #import "SwiftObjCPPBridger.h" 9 | #import "../extensions/LunarDate.h" 10 | #import "../extensions/FontUtils.h" 11 | #import "../extensions/WeatherUtils.h" -------------------------------------------------------------------------------- /src/bridging/SwiftObjCPPBridger.h: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftObjCPPBridger.h 3 | // 4 | // 5 | // Created by lemin on 10/13/23. 6 | // 7 | 8 | #import 9 | 10 | #pragma mark - HUD Functions 11 | 12 | BOOL IsHUDEnabledBridger(); 13 | void SetHUDEnabledBridger(BOOL isEnabled); 14 | void waitForNotificationBridger(void (^onFinish)(), BOOL isEnabled); -------------------------------------------------------------------------------- /src/bridging/SwiftObjCPPBridger.m: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftObjCPPBridger.m 3 | // 4 | // 5 | // Created by lemin on 10/13/23. 6 | // 7 | 8 | #import "SwiftObjCPPBridger.h" 9 | 10 | #pragma mark - HUD Functions 11 | 12 | extern BOOL IsHUDEnabled(void); 13 | extern void SetHUDEnabled(BOOL isEnabled); 14 | extern void waitForNotification(void (^onFinish)(), BOOL isEnabled); 15 | 16 | BOOL IsHUDEnabledBridger() 17 | { 18 | return (int)IsHUDEnabled(); 19 | } 20 | 21 | void SetHUDEnabledBridger(BOOL isEnabled) 22 | { 23 | SetHUDEnabled(isEnabled); 24 | } 25 | 26 | void waitForNotificationBridger(void (^onFinish)(), BOOL isEnabled) 27 | { 28 | waitForNotification(onFinish, isEnabled); 29 | } -------------------------------------------------------------------------------- /src/controllers/DeviceScaleManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeviceScaleManager.swift 3 | // 4 | // 5 | // Created by lemin on 12/15/23. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | struct PresetStruct: Identifiable { 12 | var id = UUID() 13 | 14 | var width: Double 15 | var offsetX: Double 16 | var offsetY: Double 17 | } 18 | 19 | enum Preset: String, CaseIterable { 20 | case above = "Above Status Bar" 21 | case below = "Below Status Bar" 22 | } 23 | 24 | class DeviceScaleManager { 25 | static let shared = DeviceScaleManager() 26 | 27 | private func getDeviceName() -> String { 28 | if let simulatorModelIdentifier = ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] { 29 | return simulatorModelIdentifier 30 | } 31 | 32 | var sysinfo = utsname() 33 | uname(&sysinfo) // ignore return value 34 | let deviceModel = String(bytes: Data(bytes: &sysinfo.machine, count: Int(_SYS_NAMELEN)), encoding: .ascii)?.trimmingCharacters(in: .controlCharacters) 35 | 36 | return deviceModel ?? "" 37 | } 38 | 39 | /* 40 | Sizes: 41 | 0 = No Notch 42 | 1 = Small Notch 43 | 2 = Large Notch 44 | 3 = Dynamic Island 45 | */ 46 | private func getDeviceSize() -> Int { 47 | let deviceModel: String = getDeviceName() 48 | 49 | // get the notch size 50 | if (deviceModel.starts(with: "iPhone14")) { 51 | // Small Notch 52 | return 1 53 | } else if ( 54 | deviceModel.starts(with: "iPhone10,3") 55 | || deviceModel.starts(with: "iPhone10,6") 56 | || deviceModel.starts(with: "iPhone11") 57 | || deviceModel.starts(with: "iPhone12") 58 | || deviceModel.starts(with: "iPhone13") 59 | ) { 60 | // Big Notch 61 | return 2 62 | } else if ( 63 | deviceModel.starts(with: "iPhone15") 64 | || deviceModel.starts(with: "iPhone16") 65 | ) { 66 | // Dynamic Island 67 | return 3 68 | } 69 | return 0 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/extensions/Alert++.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Alert++.swift 3 | // Cowabunga 4 | // 5 | // Created by sourcelocation on 30/01/2023. 6 | // 7 | 8 | import UIKit 9 | 10 | // credit: sourcelocation & TrollTools 11 | var currentUIAlertController: UIAlertController? 12 | 13 | 14 | fileprivate let errorString = NSLocalizedString("Error", comment: "") 15 | fileprivate let okString = NSLocalizedString("OK", comment: "") 16 | fileprivate let cancelString = NSLocalizedString("Cancel", comment: "") 17 | fileprivate let placeholderString = NSLocalizedString("Text", comment: "") 18 | 19 | extension UIApplication { 20 | 21 | func dismissAlert(animated: Bool) { 22 | DispatchQueue.main.async { 23 | currentUIAlertController?.dismiss(animated: animated) 24 | } 25 | } 26 | func alert(title: String = errorString, body: String, animated: Bool = true, withButton: Bool = true) { 27 | DispatchQueue.main.async { 28 | var body = body 29 | 30 | if title == errorString { 31 | // append debug info 32 | let device = UIDevice.current 33 | let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "" 34 | let appBuild = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "" 35 | let systemVersion = device.systemVersion 36 | body += "\n\(device.systemName) \(systemVersion), version \(appVersion) build \(appBuild) escaped=\(FileManager.default.isReadableFile(atPath: "/var/mobile"))" 37 | } 38 | 39 | currentUIAlertController = UIAlertController(title: title, message: body, preferredStyle: .alert) 40 | if withButton { currentUIAlertController?.addAction(.init(title: okString, style: .cancel)) } 41 | self.present(alert: currentUIAlertController!) 42 | } 43 | } 44 | func inputAlert(title: String, body: String, confirmTitle: String = okString, placeholder: String = placeholderString, text: String = "", keyboardType: UIKeyboardType = .default, onOK: @escaping (String) -> (), noCancel: Bool) { 45 | DispatchQueue.main.async { 46 | currentUIAlertController = UIAlertController(title: title, message: body, preferredStyle: .alert) 47 | currentUIAlertController?.addTextField { (textField) in 48 | textField.placeholder = placeholder 49 | textField.text = text 50 | textField.keyboardType = keyboardType 51 | } 52 | if !noCancel { 53 | currentUIAlertController?.addAction(.init(title: cancelString, style: .cancel)) 54 | } 55 | currentUIAlertController?.addAction(.init(title: confirmTitle, style: noCancel ? .cancel : .default, handler: { _ in 56 | onOK(currentUIAlertController?.textFields?[0].text ?? "") 57 | })) 58 | self.present(alert: currentUIAlertController!) 59 | } 60 | } 61 | func optionsAlert(title: String, body: String, options: [String], preferredStyle: UIAlertController.Style = .actionSheet, onSelection: @escaping (String) -> ()) { 62 | DispatchQueue.main.async { 63 | currentUIAlertController = UIAlertController(title: title, message: body, preferredStyle: preferredStyle) 64 | // add all the options 65 | for alertOption in options { 66 | currentUIAlertController?.addAction(.init(title: alertOption, style: .default, handler: { _ in 67 | onSelection(alertOption) 68 | })) 69 | } 70 | currentUIAlertController?.addAction(.init(title: cancelString, style: .cancel)) 71 | // present popover for iPads 72 | let view: UIView? = UIApplication.shared.windows.first?.rootViewController?.view 73 | currentUIAlertController?.popoverPresentationController?.sourceView = view // prevents crashing on iPads 74 | currentUIAlertController?.popoverPresentationController?.sourceRect = CGRect(x: view?.bounds.midX ?? 0, y: view?.bounds.maxY ?? 0, width: 0, height: 0) // show up at center bottom on iPads 75 | self.present(alert: currentUIAlertController!) 76 | } 77 | } 78 | func confirmAlert(title: String = errorString, body: String, confirmTitle: String = okString, onOK: @escaping () -> (), noCancel: Bool) { 79 | DispatchQueue.main.async { 80 | currentUIAlertController = UIAlertController(title: title, message: body, preferredStyle: .alert) 81 | if !noCancel { 82 | currentUIAlertController?.addAction(.init(title: cancelString, style: .cancel)) 83 | } 84 | currentUIAlertController?.addAction(.init(title: confirmTitle, style: noCancel ? .cancel : .default, handler: { _ in 85 | onOK() 86 | })) 87 | self.present(alert: currentUIAlertController!) 88 | } 89 | } 90 | func change(title: String = errorString, body: String) { 91 | DispatchQueue.main.async { 92 | currentUIAlertController?.title = title 93 | currentUIAlertController?.message = body 94 | } 95 | } 96 | 97 | func present(alert: UIAlertController) { 98 | if var topController = self.windows[0].rootViewController { 99 | while let presentedViewController = topController.presentedViewController { 100 | topController = presentedViewController 101 | } 102 | 103 | topController.present(alert, animated: true) 104 | // topController should now be your topmost view controller 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/extensions/Color++.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Color++.swift 3 | // Cowabunga 4 | // 5 | // Created by sourcelocation on 30/01/2023. 6 | // 7 | 8 | import SwiftUI 9 | 10 | extension UIColor { 11 | var rgba: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) { 12 | var red: CGFloat = 0 13 | var green: CGFloat = 0 14 | var blue: CGFloat = 0 15 | var alpha: CGFloat = 0 16 | getRed(&red, green: &green, blue: &blue, alpha: &alpha) 17 | 18 | return (red, green, blue, alpha) 19 | } 20 | 21 | var data: Data? { 22 | do { 23 | let data = try NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: false) 24 | return data 25 | } catch let error { 26 | print("error color key data not saved \(error.localizedDescription)") 27 | } 28 | return nil 29 | } 30 | 31 | static func getColorFromData(data colorData: Data?) -> UIColor? { 32 | guard let colorDataUnwrapped = colorData else { return nil; } 33 | do { 34 | return try NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: colorDataUnwrapped) 35 | } catch let error { 36 | print("color error \(error.localizedDescription)") 37 | return nil 38 | } 39 | } 40 | } 41 | 42 | extension Color { 43 | init(uiColor14: UIColor) { 44 | self.init(red: Double(uiColor14.rgba.red), 45 | green: Double(uiColor14.rgba.green), 46 | blue: Double(uiColor14.rgba.blue), 47 | opacity: Double(uiColor14.rgba.alpha)) 48 | } 49 | 50 | init?(hex: String) { 51 | let r, g, b, a: CGFloat 52 | 53 | if hex.hasPrefix("#") { 54 | let start = hex.index(hex.startIndex, offsetBy: 1) 55 | let hexColor = String(hex[start...]) 56 | 57 | if hexColor.count == 8 { 58 | let scanner = Scanner(string: hexColor) 59 | var hexNumber: UInt64 = 0 60 | 61 | if scanner.scanHexInt64(&hexNumber) { 62 | r = CGFloat((hexNumber & 0xff000000) >> 24) / 255 63 | g = CGFloat((hexNumber & 0x00ff0000) >> 16) / 255 64 | b = CGFloat((hexNumber & 0x0000ff00) >> 8) / 255 65 | a = CGFloat(hexNumber & 0x000000ff) / 255 66 | 67 | self.init(red: r, green: g, blue: b, opacity: a) 68 | return 69 | } 70 | } else if hexColor.count == 6 { 71 | let scanner = Scanner(string: hexColor) 72 | var hexNumber: UInt64 = 0 73 | 74 | if scanner.scanHexInt64(&hexNumber) { 75 | r = CGFloat((hexNumber & 0xff000000) >> 16) / 255 76 | g = CGFloat((hexNumber & 0x00ff0000) >> 8) / 255 77 | b = CGFloat(hexNumber & 0x0000ff00) / 255 78 | 79 | self.init(red: r, green: g, blue: b) 80 | return 81 | } 82 | } 83 | } 84 | 85 | return nil 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/extensions/DarwinNotificationCenter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DarwinNotificationCenter.swift 3 | // DarwinNotificationCenter 4 | // 5 | // Copyright (c) 2019 - 2020 Jason Nam (https://jasonnam.com) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | // 25 | 26 | import Foundation 27 | 28 | public class DarwinNotificationCenter { 29 | 30 | public static let `default` = DarwinNotificationCenter() 31 | 32 | public private(set) var observations: [String: (String) -> Void] = [:] 33 | 34 | public func addObserver(forName name: String, using block: @escaping (String) -> Void) { 35 | observations[name] = block 36 | 37 | let callback: CFNotificationCallback = { _, _, name, _, _ in 38 | guard let name = name?.rawValue as String? else { return } 39 | DarwinNotificationCenter.default.observations[name]?(name) 40 | } 41 | 42 | CFNotificationCenterAddObserver( 43 | CFNotificationCenterGetDarwinNotifyCenter(), 44 | Unmanaged.passUnretained(self).toOpaque(), 45 | callback, 46 | name as CFString, 47 | nil, 48 | .deliverImmediately 49 | ) 50 | } 51 | 52 | public func removeObserver(withName name: String) { 53 | observations.removeValue(forKey: name) 54 | 55 | CFNotificationCenterRemoveObserver( 56 | CFNotificationCenterGetDarwinNotifyCenter(), 57 | Unmanaged.passUnretained(self).toOpaque(), 58 | CFNotificationName(name as CFString), 59 | nil 60 | ) 61 | } 62 | 63 | public func removeAllObservers() { 64 | CFNotificationCenterRemoveEveryObserver( 65 | CFNotificationCenterGetDarwinNotifyCenter(), 66 | Unmanaged.passUnretained(self).toOpaque() 67 | ) 68 | } 69 | 70 | public func post(name: String) { 71 | CFNotificationCenterPostNotification( 72 | CFNotificationCenterGetDarwinNotifyCenter(), 73 | CFNotificationName(name as CFString), 74 | nil, 75 | nil, 76 | true 77 | ) 78 | } 79 | } -------------------------------------------------------------------------------- /src/extensions/EZTimer.h: -------------------------------------------------------------------------------- 1 | // 2 | // EZTimer.h 3 | // EZKit 4 | // 5 | // Created by macbook pro on 2018/3/20. 6 | // Copyright © 2018年 sheep. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | ///线程选择 12 | typedef NS_ENUM(NSUInteger, EZTimerQueueType) { 13 | EZTimerQueueTypeGlobal, 14 | EZTimerQueueTypeConcurrent, 15 | EZtimerQueueTypeSerial, 16 | }; 17 | 18 | ///timer执行方式选择,默认立刻执行一次,此时block会立刻执行,next表示下一个interval执行。 19 | typedef NS_ENUM(NSUInteger, EZTimerResumeType) { 20 | EZTimerResumeTypeNow, 21 | EZTimerQueueTypeNext, 22 | }; 23 | 24 | typedef void (^EZTimerBlock)(NSString *timerName); 25 | 26 | @interface EZTimer : NSObject 27 | /** 28 | * 单例模式 29 | */ 30 | +(instancetype)shareInstance; 31 | 32 | /** 33 | * 简单方式创建并执行timer,其他未给出的参数均为默认参数 34 | * timerName timer的名称,创建好的timer以name为key存储于timers字典中 35 | * interval timer时间间隔 36 | * resumeType 是否立刻开始执行,默认立刻开始执行,此时block会立刻执行,next下一个interval执行 37 | * action timer回调 38 | */ 39 | -(void)repeatTimer:(NSString*)timerName timerInterval:(double)interval resumeType:(EZTimerResumeType)resumeType action:(EZTimerBlock)action; 40 | 41 | /** 42 | * 简单方式创建并执行timer 43 | * timerName timer的名称,创建好的timer以name为key存储于timers字典中,必须 44 | * interval timer时间间隔 0 默认60s 45 | * leeway timer的精度,默认0.1s 46 | * resumeType 是否立刻开始执行,默认立刻开始执行,此时block会立刻执行,next下一个interval执行 47 | * queue 创建timer所在的线程,默认global 48 | * queueName 线程命名,当global时name不起作用。 49 | * repeats 是否重复运行 50 | * action timer回调 51 | */ 52 | -(void)timer:(NSString*)timerName timerInterval:(double)interval leeway:(double)leeway resumeType:(EZTimerResumeType)resumeType queue:(EZTimerQueueType)queue queueName:(NSString *)queueName repeats:(BOOL)repeats action:(EZTimerBlock)action; 53 | 54 | /** 55 | * 注销此timer 56 | * timerName timer名称 57 | */ 58 | -(void)cancel:(NSString *)timerName; 59 | /** 60 | * 暂停此timer 61 | * 暂停及恢复的操作不建议使用,这两个操作需配对使用, 62 | * 不然会出现崩溃,原因是source未提供检测状态的接口 63 | * timerName timer名称 64 | */ 65 | -(void)pause:(NSString *)timerName; 66 | /** 67 | * 恢复此timer 68 | * 暂停及恢复的操作不建议使用,这两个操作需配对使用, 69 | * 不然会出现崩溃,原因是source未提供检测状态的接口 70 | * timerName timer名称 71 | */ 72 | -(void)resume:(NSString *)timerName; 73 | 74 | @end 75 | -------------------------------------------------------------------------------- /src/extensions/EZTimer.mm: -------------------------------------------------------------------------------- 1 | // 2 | // EZTimer.mm 3 | // EZKit 4 | // 5 | // Created by macbook pro on 2018/3/20. 6 | // Copyright © 2018年 sheep. All rights reserved. 7 | // 8 | 9 | #import "EZTimer.h" 10 | 11 | #define EZTimerQueueName(x) [NSString stringWithFormat:@"NSTimer_%@_queue",x] 12 | 13 | #define EZTimerDfaultLeeway 0.1 14 | 15 | #define EZTimerDfaultTimeInterval 60 16 | 17 | #define EZTIMERSTATUSKEY_RESUME @"EZTIMERSTATUSKEY_RESUME" 18 | #define EZTIMERSTATUSKEY_PAUSE @"EZTIMERSTATUSKEY_PAUSE" 19 | 20 | // #ifdef DEBUG 21 | // #define EZLog(...) NSLog(__VA_ARGS__) 22 | // #else 23 | #define EZLog(...) 24 | // #endif 25 | 26 | @interface EZTimer() 27 | 28 | @property(nonatomic,strong)NSMutableDictionary *timers; 29 | 30 | @property(nonatomic,strong)NSMutableDictionary *timersFlags; 31 | 32 | @end 33 | 34 | @implementation EZTimer 35 | 36 | +(instancetype)shareInstance{ 37 | static EZTimer *instance = nil; 38 | static dispatch_once_t onceToken; 39 | dispatch_once(&onceToken, ^{ 40 | instance = [[EZTimer alloc] init]; 41 | }); 42 | 43 | return instance; 44 | } 45 | 46 | 47 | -(void)repeatTimer:(NSString*)timerName timerInterval:(double)interval resumeType:(EZTimerResumeType)resumeType action:(EZTimerBlock)action{ 48 | [self timer:timerName timerInterval:interval leeway:EZTimerDfaultLeeway resumeType:resumeType queue:EZTimerQueueTypeGlobal queueName:nil repeats:YES action:action]; 49 | } 50 | 51 | 52 | -(void)timer:(NSString*)timerName timerInterval:(double)interval leeway:(double)leeway resumeType:(EZTimerResumeType)resumeType queue:(EZTimerQueueType)queue queueName:(NSString *)queueName repeats:(BOOL)repeats action:(EZTimerBlock)action{ 53 | 54 | dispatch_queue_t que = nil; 55 | if (!timerName) { return; } 56 | if (!queueName) { 57 | queueName = EZTimerQueueName(timerName); 58 | } 59 | switch (queue) { 60 | 61 | case EZTimerQueueTypeConcurrent:{ 62 | que = dispatch_queue_create([queueName UTF8String], DISPATCH_QUEUE_CONCURRENT); 63 | break; 64 | } 65 | case EZtimerQueueTypeSerial:{ 66 | que = dispatch_queue_create([queueName UTF8String], DISPATCH_QUEUE_SERIAL); 67 | break; 68 | } 69 | default: 70 | que = dispatch_get_global_queue(0, 0); 71 | break; 72 | } 73 | dispatch_source_t timer = [self.timers objectForKey:timerName]; 74 | if (!timer) { 75 | timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, que); 76 | [self.timers setObject:timer forKey:timerName]; 77 | //timer 状态标识 78 | NSMutableDictionary *dic =[NSMutableDictionary dictionaryWithObjectsAndKeys:@0,EZTIMERSTATUSKEY_RESUME,@0,EZTIMERSTATUSKEY_PAUSE, nil]; 79 | [self.timersFlags setObject:dic forKey:timerName]; 80 | } 81 | 82 | dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), (interval==0?EZTimerDfaultTimeInterval:interval) * NSEC_PER_SEC, (leeway == 0 ? EZTimerDfaultLeeway:leeway) * NSEC_PER_SEC); 83 | 84 | __weak typeof(self) weakSelf = self; 85 | dispatch_source_set_event_handler(timer, ^{ 86 | EZLog(@"tiemr action"); 87 | action(timerName); 88 | if (!repeats) { 89 | //dispatch_source_cancel(timer); 90 | [weakSelf cancel:timerName]; 91 | EZLog(@"tiemr action once"); 92 | } 93 | }); 94 | if (resumeType == EZTimerResumeTypeNow) { 95 | //dispatch_resume(timer); 96 | [self resume:timerName]; 97 | }else{ 98 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(interval * NSEC_PER_SEC)), que, ^{ 99 | //dispatch_resume(timer); 100 | [weakSelf resume:timerName]; 101 | }); 102 | } 103 | } 104 | 105 | 106 | //当 timer 处于 suspend状态时不能被 释放. 107 | -(void)cancel:(NSString *)timerName{ 108 | 109 | dispatch_source_t timer = [self.timers objectForKey:timerName]; 110 | //NSAssert(timer, @"%s\n定时器列表中不存在此名称的timer -- %@",__func__,timerName); 111 | if (!timer) { 112 | EZLog(@"tiemr cancel retrun - because timer had been cancel"); 113 | return; 114 | } 115 | NSMutableDictionary *timerDic = [self.timersFlags objectForKey:timerName]; 116 | if (timerDic && [timerDic[EZTIMERSTATUSKEY_PAUSE] boolValue]) { 117 | EZLog(@"timer had paused,resume first then cancel it"); 118 | dispatch_resume(timer); 119 | dispatch_source_cancel(timer); 120 | } 121 | [self.timers removeObjectForKey:timerName]; 122 | [self.timersFlags removeObjectForKey:timerName]; 123 | 124 | EZLog(@"tiemr cancel - cancel"); 125 | 126 | } 127 | 128 | -(void)pause:(NSString *)timerName{ 129 | 130 | dispatch_source_t timer = [self.timers objectForKey:timerName]; 131 | //NSAssert(timer, @"%s\n定时器列表中不存在此名称的timer -- %@",__func__,timerName); 132 | if (!timer) { 133 | return; 134 | } 135 | NSMutableDictionary *timerDic = [self.timersFlags objectForKey:timerName]; 136 | if (timerDic && [timerDic[EZTIMERSTATUSKEY_PAUSE] boolValue]) { 137 | EZLog(@"tiemr pause return- because timer had paused"); 138 | return ; 139 | } 140 | dispatch_suspend(timer); 141 | timerDic[EZTIMERSTATUSKEY_PAUSE] = @1; 142 | timerDic[EZTIMERSTATUSKEY_RESUME] = @0; 143 | EZLog(@"tiemr pause - paused" ); 144 | 145 | } 146 | 147 | -(void)resume:(NSString *)timerName{ 148 | dispatch_source_t timer = [self.timers objectForKey:timerName]; 149 | //NSAssert(timer, @"%s\n定时器列表中不存在此名称的timer -- %@",__func__,timerName); 150 | if (!timer) { 151 | return; 152 | } 153 | NSMutableDictionary *timerDic = [self.timersFlags objectForKey:timerName]; 154 | if (timerDic && [timerDic[EZTIMERSTATUSKEY_RESUME] boolValue]) { 155 | EZLog(@"timer resuem return - because timer had resume"); 156 | return; 157 | } 158 | dispatch_resume(timer); 159 | timerDic[EZTIMERSTATUSKEY_RESUME] = @1; 160 | timerDic[EZTIMERSTATUSKEY_PAUSE] = @0; 161 | EZLog(@"tiemr resume - resumed"); 162 | 163 | } 164 | 165 | -(NSMutableDictionary *)timers{ 166 | if (!_timers) { 167 | _timers = [NSMutableDictionary dictionary]; 168 | } 169 | return _timers; 170 | } 171 | 172 | -(NSMutableDictionary *)timersFlags{ 173 | if (!_timersFlags) { 174 | _timersFlags = [NSMutableDictionary dictionary]; 175 | } 176 | return _timersFlags; 177 | } 178 | 179 | @end 180 | -------------------------------------------------------------------------------- /src/extensions/FlipEffect.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FlipEffect.swift 3 | // Cowabunga 4 | // 5 | // Created by lemin on 3/1/23. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct FlipEffect: GeometryEffect { 11 | 12 | var animatableData: Double { 13 | get { angle } 14 | set { angle = newValue } 15 | } 16 | 17 | @Binding var flipped: Bool 18 | var angle: Double 19 | let axis: (x: CGFloat, y: CGFloat) 20 | 21 | func effectValue(size: CGSize) -> ProjectionTransform { 22 | 23 | DispatchQueue.main.async { 24 | self.flipped = self.angle >= 90 && self.angle < 270 25 | } 26 | 27 | let tweakedAngle = flipped ? -180 + angle : angle 28 | let a = CGFloat(Angle(degrees: tweakedAngle).radians) 29 | 30 | var transform3d = CATransform3DIdentity; 31 | transform3d.m34 = -1/max(size.width, size.height) 32 | 33 | transform3d = CATransform3DRotate(transform3d, a, axis.x, axis.y, 0) 34 | transform3d = CATransform3DTranslate(transform3d, -size.width/2.0, -size.height/2.0, 0) 35 | 36 | let affineTransform = ProjectionTransform(CGAffineTransform(translationX: size.width/2.0, y: size.height / 2.0)) 37 | 38 | return ProjectionTransform(transform3d).concatenating(affineTransform) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/extensions/FontUtils.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import 5 | 6 | @interface FontUtils : NSObject 7 | 8 | + (void) loadFontsFromFolder:(NSString *)fontFolder; 9 | + (void) loadAllFonts; 10 | + (NSArray *) allFontNames; 11 | + (UIFont*) loadFontWithName:(NSString*)fontName size:(float)size bold:(BOOL) bold italic:(BOOL) italic; 12 | @end -------------------------------------------------------------------------------- /src/extensions/FontUtils.mm: -------------------------------------------------------------------------------- 1 | #import "FontUtils.h" 2 | 3 | static NSMutableArray *_allFontNames = [NSMutableArray array]; 4 | 5 | @implementation FontUtils 6 | 7 | + (void)loadFontsFromFolder:(NSString *)fontFolder { 8 | NSFileManager *fileManager = [NSFileManager defaultManager]; 9 | NSArray *contents = [fileManager contentsOfDirectoryAtPath:fontFolder error:NULL]; 10 | 11 | for (NSString *filename in contents) { 12 | if ([filename.pathExtension isEqualToString:@"ttf"] || [filename.pathExtension isEqualToString:@"otf"]) { 13 | NSString *fontPath = [fontFolder stringByAppendingPathComponent:filename]; 14 | 15 | NSData *fontData = [NSData dataWithContentsOfFile:fontPath]; 16 | CFErrorRef error; 17 | CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)fontData); 18 | CGFontRef font = CGFontCreateWithDataProvider(provider); 19 | 20 | if (!CTFontManagerRegisterGraphicsFont(font, &error)) { 21 | CFStringRef errorDescription = CFErrorCopyDescription(error); 22 | NSLog(@"Failed to load font: %@", errorDescription); 23 | CFSafeRelease(errorDescription); 24 | } 25 | // CFStringRef fontNameRef = CGFontCopyPostScriptName(font); 26 | // NSString *fontName = (__bridge NSString*)fontNameRef; 27 | // CFRelease(fontNameRef); 28 | // [_allFontNames addObject:fontName]; 29 | // NSLog(@"fontName: %@",fontName); 30 | 31 | CFSafeRelease(font); 32 | CFSafeRelease(provider); 33 | } 34 | } 35 | } 36 | 37 | + (void) loadAllFonts { 38 | NSArray *familyNames = [UIFont familyNames]; 39 | // for (NSString *familyName in familyNames) { 40 | // NSArray *names = [UIFont fontNamesForFamilyName:familyName]; 41 | // [_allFontNames addObjectsFromArray:names]; 42 | // } 43 | [_allFontNames addObjectsFromArray:familyNames]; 44 | // CFArrayRef allFonts = CTFontManagerCopyAvailableFontFamilyNames(); 45 | // for (NSInteger i = 0; i < CFArrayGetCount(allFonts); i++) { 46 | // NSString *fontName = (__bridge NSString *)CFArrayGetValueAtIndex(allFonts, i); 47 | // [_allFontNames addObject:fontName]; 48 | // } 49 | // CFRelease(allFonts); 50 | } 51 | 52 | + (NSArray *)allFontNames { 53 | NSMutableArray *copyArray = [[NSMutableArray alloc] init]; 54 | copyArray = [_allFontNames sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)].mutableCopy; 55 | [copyArray insertObject:@"System Font" atIndex:0]; 56 | return copyArray; 57 | } 58 | 59 | + (UIFont*)loadFontWithName:(NSString*)fontName size:(float)size bold:(BOOL) bold italic:(BOOL) italic{ 60 | UIFont *font = [UIFont fontWithDescriptor:[UIFontDescriptor fontDescriptorWithName:fontName size:size] size:size]; 61 | if ([fontName isEqualToString:@"System Font"]) { 62 | font = [UIFont systemFontOfSize: size]; 63 | } 64 | UIFontDescriptorSymbolicTraits symbolicTraits = 0; 65 | if (bold) { 66 | symbolicTraits |= UIFontDescriptorTraitBold; 67 | } 68 | if (italic) { 69 | symbolicTraits |= UIFontDescriptorTraitItalic; 70 | } 71 | UIFont *specialFont = [UIFont fontWithDescriptor:[[font fontDescriptor] fontDescriptorWithSymbolicTraits:symbolicTraits] size:size]; 72 | return specialFont; 73 | } 74 | 75 | void CFSafeRelease(CFTypeRef cf) { 76 | if (cf != NULL) { 77 | CFRelease(cf); 78 | } 79 | } 80 | @end -------------------------------------------------------------------------------- /src/extensions/FoundationExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FoundationExtensions.swift 3 | // 4 | // 5 | // Created by lemin on 10/13/23. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Bundle { 11 | var releaseVersionNumber: String? { 12 | return infoDictionary?["CFBundleShortVersionString"] as? String 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/extensions/Haptic++.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Haptic++.swift 3 | // PsychicPaper 4 | // 5 | // Created by Hariz Shirazi on 2023-02-04. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | /// Wrapper around UIKit haptics 12 | class Haptic { 13 | /// Shared instance 14 | static let shared = Haptic() 15 | private init() {} 16 | /// Play haptic feedback 17 | func play(_ feedbackStyle: UIImpactFeedbackGenerator.FeedbackStyle) { 18 | UIImpactFeedbackGenerator(style: feedbackStyle).impactOccurred() 19 | } 20 | 21 | /// Provide haptic user feedback for an action 22 | func notify(_ feedbackType: UINotificationFeedbackGenerator.FeedbackType) { 23 | UINotificationFeedbackGenerator().notificationOccurred(feedbackType) 24 | } 25 | 26 | /// Play feedback for a selection 27 | func selection() { 28 | UISelectionFeedbackGenerator().selectionChanged() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/extensions/LunarDate.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface LunarDate : NSObject 4 | 5 | + (NSCalendar *)chineseCalendar; 6 | + (NSString*)getChineseCalendarWithDate:(NSDate *)date format:(NSString *)format; 7 | 8 | @end -------------------------------------------------------------------------------- /src/extensions/LunarDate.mm: -------------------------------------------------------------------------------- 1 | #import "LunarDate.h" 2 | 3 | #define ChineseMonths @[@"正月", @"二月", @"三月", @"四月", @"五月", @"六月", @"七月", @"八月",@"九月", @"十月", @"冬月", @"腊月"] 4 | 5 | #define ChineseDays @[@"初一", @"初二", @"初三", @"初四", @"初五", @"初六", @"初七", @"初八", @"初九", @"初十",@"十一", @"十二", @"十三", @"十四", @"十五", @"十六", @"十七", @"十八", @"十九", @"二十", @"廿一", @"廿二", @"廿三", @"廿四", @"廿五", @"廿六", @"廿七", @"廿八", @"廿九", @"三十"] 6 | 7 | #define ChineseYears @[@"甲子",@"乙丑",@"丙寅",@"丁卯",@"戊辰",@"己巳",@"庚午",@"辛未",@"壬申",@"癸酉",@"甲戌",@"乙亥",@"丙子",@"丁丑",@"戊寅",@"己卯",@"庚辰",@"辛巳",@"壬午",@"癸未",@"甲申",@"乙酉",@"丙戌",@"丁亥",@"戊子",@"己丑",@"庚寅",@"辛卯",@"壬辰",@"癸巳",@"甲午",@"乙未",@"丙申",@"丁酉",@"戊戌",@"己亥",@"庚子",@"辛丑",@"壬寅",@"癸卯",@"甲辰",@"乙巳",@"丙午",@"丁未",@"戊申",@"己酉",@"庚戌",@"辛亥",@"壬子",@"癸丑",@"甲寅",@"乙卯",@"丙辰",@"丁巳",@"戊午",@"己未",@"庚申",@"辛酉",@"壬戌",@"癸亥"] 8 | 9 | @implementation LunarDate 10 | 11 | + (NSCalendar *)chineseCalendar 12 | { 13 | static NSCalendar *chineseCalendar_sharedCalendar = nil; 14 | if (!chineseCalendar_sharedCalendar) 15 | chineseCalendar_sharedCalendar =[[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierChinese]; 16 | return chineseCalendar_sharedCalendar; 17 | } 18 | 19 | /** 20 | 获取农历年月日 21 | 22 | @param date 输入日期 23 | @return 返回干支年 农历月份 农历初几 24 | */ 25 | + (NSString*)getChineseCalendarWithDate:(NSDate *)date format:(NSString *)format{ 26 | NSCalendar *chineseCalendar = [[self class] chineseCalendar]; 27 | unsigned unitFlags = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay; 28 | NSDateComponents *localeComp = [chineseCalendar components:unitFlags fromDate:date]; 29 | 30 | NSString *y_str = [ChineseYears objectAtIndex:localeComp.year-1]; 31 | NSString *m_str = [ChineseMonths objectAtIndex:localeComp.month-1]; 32 | NSString *d_str = [ChineseDays objectAtIndex:localeComp.day-1]; 33 | format = [format stringByReplacingOccurrencesOfString:@"CNYY" withString:y_str]; 34 | format = [format stringByReplacingOccurrencesOfString:@"CNMM" withString:m_str]; 35 | format = [format stringByReplacingOccurrencesOfString:@"CNDD" withString:d_str]; 36 | return format; 37 | } 38 | 39 | @end -------------------------------------------------------------------------------- /src/extensions/UsefulFunctions.h: -------------------------------------------------------------------------------- 1 | // 2 | // UsefulFunctions.h 3 | // 4 | // 5 | // Created by lemin on 12/8/23. 6 | // 7 | 8 | BOOL getBoolFromDictKey(NSDictionary *dict, NSString *key, BOOL defaultValue); 9 | BOOL getBoolFromDictKey(NSDictionary *dict, NSString *key); 10 | 11 | NSInteger getIntFromDictKey(NSDictionary *dict, NSString *key, NSInteger defaultValue); 12 | NSInteger getIntFromDictKey(NSDictionary *dict, NSString *key); 13 | 14 | double getDoubleFromDictKey(NSDictionary *dict, NSString *key, double defaultValue); 15 | double getDoubleFromDictKey(NSDictionary *dict, NSString *key); 16 | 17 | NSString* getStringFromDictKey(NSDictionary *dict, NSString *key, NSString *defaultValue); 18 | NSString* getStringFromDictKey(NSDictionary *dict, NSString *key); -------------------------------------------------------------------------------- /src/extensions/UsefulFunctions.mm: -------------------------------------------------------------------------------- 1 | // 2 | // UsefulFunctions.mm 3 | // 4 | // 5 | // Created by lemin on 12/8/23. 6 | // 7 | 8 | #import 9 | #import "UsefulFunctions.h" 10 | 11 | // MARK: Dictionary Value Functions 12 | // Bools 13 | BOOL getBoolFromDictKey(NSDictionary *dict, NSString *key, BOOL defaultValue) 14 | { 15 | return [dict valueForKey:key] ? [[dict valueForKey:key] boolValue] : defaultValue; 16 | } 17 | BOOL getBoolFromDictKey(NSDictionary *dict, NSString *key) 18 | { 19 | return getBoolFromDictKey(dict, key, NO); 20 | } 21 | 22 | // Ints 23 | NSInteger getIntFromDictKey(NSDictionary *dict, NSString *key, NSInteger defaultValue) 24 | { 25 | return [dict valueForKey:key] ? [[dict valueForKey:key] integerValue] : defaultValue; 26 | } 27 | 28 | NSInteger getIntFromDictKey(NSDictionary *dict, NSString *key) 29 | { 30 | return getIntFromDictKey(dict, key, 0); 31 | } 32 | 33 | // Doubles 34 | double getDoubleFromDictKey(NSDictionary *dict, NSString *key, double defaultValue) 35 | { 36 | return [dict valueForKey:key] ? [[dict valueForKey:key] doubleValue] : defaultValue; 37 | } 38 | 39 | double getDoubleFromDictKey(NSDictionary *dict, NSString *key) 40 | { 41 | return getDoubleFromDictKey(dict, key, 0.0); 42 | } 43 | 44 | // String 45 | NSString* getStringFromDictKey(NSDictionary *dict, NSString *key, NSString *defaultValue) 46 | { 47 | return [dict valueForKey:key] ? [dict valueForKey:key] : defaultValue; 48 | } 49 | 50 | NSString* getStringFromDictKey(NSDictionary *dict, NSString *key) 51 | { 52 | return getStringFromDictKey(dict, key, @""); 53 | } -------------------------------------------------------------------------------- /src/extensions/UserDefaultsExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserDefaultsExtensions.swift 3 | // 4 | // 5 | // Created by lemin on 12/8/23. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | extension UserDefaults { 12 | // MARK: Managing the Data 13 | func loadUserDefaults(forPath path: String) -> [String: Any] { 14 | do { 15 | let plistData: Data = try Data(contentsOf: URL(fileURLWithPath: path)) 16 | if let dict: [String: Any] = try PropertyListSerialization.propertyList(from: plistData, options: [], format: nil) as? [String: Any] { 17 | return dict 18 | } 19 | } catch { 20 | // file doesn't exist 21 | } 22 | return [:] 23 | } 24 | 25 | func setValue(_ value: Any?, forKey key: String, forPath path: String) { 26 | var dict = loadUserDefaults(forPath: path) 27 | dict[key] = value 28 | do { 29 | let newData: Data = try PropertyListSerialization.data(fromPropertyList: dict, format: .xml, options: 0) 30 | try newData.write(to: URL(fileURLWithPath: path)) 31 | } catch { 32 | print(error.localizedDescription) 33 | } 34 | } 35 | 36 | func removeObject(forKey defaultName: String, forPath path: String) { 37 | setValue(nil, forKey: defaultName, forPath: path) 38 | } 39 | 40 | func deleteUserDefaults(forPath path: String) throws { 41 | try FileManager.default.removeItem(atPath: path) 42 | } 43 | 44 | 45 | // MARK: Specific Type Functions 46 | 47 | // string 48 | func string(forKey defaultName: String, forPath path: String) -> String? { 49 | let dict = loadUserDefaults(forPath: path) 50 | return dict[defaultName] as? String 51 | } 52 | 53 | // double 54 | func double(forKey defaultName: String, forPath path: String) -> Double { 55 | let dict = loadUserDefaults(forPath: path) 56 | return dict[defaultName] as? Double ?? 0 57 | } 58 | 59 | // integer 60 | func integer(forKey defaultName: String, forPath path: String) -> Int { 61 | let dict = loadUserDefaults(forPath: path) 62 | return dict[defaultName] as? Int ?? 0 63 | } 64 | 65 | // bool 66 | func bool(forKey defaultName: String, forPath path: String) -> Bool { 67 | let dict = loadUserDefaults(forPath: path) 68 | return dict[defaultName] as? Bool ?? false 69 | } 70 | 71 | // array 72 | func array(forKey defaultName: String, forPath path: String) -> [Any]? { 73 | let dict = loadUserDefaults(forPath: path) 74 | return dict[defaultName] as? [Any] 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/extensions/ViewExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewExtensions.swift 3 | // 4 | // 5 | // Created by lemin on 10/13/23. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | // MARK: Tinted Button Style 12 | struct TintedButton: ButtonStyle { 13 | var color: Color 14 | var fullWidth: Bool = false 15 | 16 | func makeBody(configuration: Configuration) -> some View { 17 | ZStack { 18 | if fullWidth { 19 | configuration.label 20 | .padding(15) 21 | .frame(maxWidth: .infinity) 22 | .background(AnyView(color.opacity(0.2))) 23 | .cornerRadius(8) 24 | .foregroundColor(color) 25 | } else { 26 | configuration.label 27 | .padding(15) 28 | .background(AnyView(color.opacity(0.2))) 29 | .cornerRadius(8) 30 | .foregroundColor(color) 31 | } 32 | } 33 | } 34 | 35 | init(color: Color = .blue, fullWidth: Bool = false) { 36 | self.color = color 37 | self.fullWidth = fullWidth 38 | } 39 | } 40 | 41 | // MARK: Extended Slider 42 | struct BetterSlider: View { 43 | @Binding var value: Double 44 | var bounds: ClosedRange 45 | var step: Double.Stride? = nil 46 | 47 | var inputTitle: String = NSLocalizedString("Enter Value", comment:"") 48 | var inputBody: String = NSLocalizedString("Enter a value.", comment:"") 49 | 50 | var body: some View { 51 | HStack { 52 | if step == nil { 53 | Slider(value: $value, in: bounds) 54 | } else { 55 | Slider(value: $value, in: bounds, step: step!) 56 | } 57 | Spacer() 58 | Button(action: { 59 | UIApplication.shared.inputAlert(title: inputTitle, body: inputBody, onOK: { val in 60 | var finalVal: Double = Double(val) ?? value 61 | // make sure it is within bounds, then set it 62 | if (finalVal > bounds.upperBound) { 63 | finalVal = bounds.upperBound 64 | } else if (finalVal < bounds.lowerBound) { 65 | finalVal = bounds.lowerBound 66 | } 67 | // if step != nil { 68 | // // make sure it is within the step 69 | // finalVal = finalVal.truncatingRemainder(dividingBy: step!) 70 | // } 71 | value = finalVal 72 | }, noCancel: false) 73 | }) { 74 | Text(String(format: "%.2f", value)) 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/extensions/WeatherUtils.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import 5 | 6 | @interface WeatherUtils : NSObject 7 | + (NSString *)getWeatherIcon:(NSString *)text; 8 | + (NSDictionary *)fetchNowWeatherForLocation:(NSString *)location apiKey:(NSString *) apiKey dateLocale:(NSString *)dateLocale; 9 | + (NSDictionary *)fetchTodayWeatherForLocation:(NSString *)location apiKey:(NSString *) apiKey dateLocale:(NSString *)dateLocale; 10 | + (NSData *)fetchLocationIDForName:(NSString *)name apiKey:(NSString *) apiKey dateLocale:(NSString *)dateLocale; 11 | + (NSString *)formatNowResult:(NSDictionary *)data format:(NSString *)format; 12 | + (NSString *)formatTodayResult:(NSDictionary *)data format:(NSString *)format; 13 | + (NSString *)getDataFrom:(NSString *)url; 14 | + (NSString *)convertDateLocaleToLang:(NSString *)locale; 15 | @end -------------------------------------------------------------------------------- /src/extensions/WeatherUtils.mm: -------------------------------------------------------------------------------- 1 | #import "WeatherUtils.h" 2 | 3 | @implementation WeatherUtils 4 | 5 | + (NSString *)getWeatherIcon:(NSString *)text { 6 | NSString *weatherIcon = @"🌤️"; 7 | NSArray *weatherIconList = @[@"☀️", @"☁️", @"⛅️", 8 | @"☃️", @"⛈️", @"🏜️", @"🏜️", @"🌫️", @"🌫️", @"🌪️", @"🌧️"]; 9 | NSArray *weatherType = @[@"晴|sunny", @"阴|overcast", @"云|cloudy", @"雪|snow", @"雷|thunder", @"沙|sand", @"尘|dust", @"雾|foggy", @"霾|haze", @"风|wind", @"雨|rain"]; 10 | 11 | NSRegularExpression *regex; 12 | for (int i = 0; i < weatherType.count; i++) { 13 | NSString *pattern = [NSString stringWithFormat:@".*%@.*", weatherType[i]]; 14 | regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:nil]; 15 | if ([regex numberOfMatchesInString:text options:0 range:NSMakeRange(0, [text length])] > 0) { 16 | weatherIcon = weatherIconList[i]; 17 | break; 18 | } 19 | } 20 | 21 | return weatherIcon; 22 | } 23 | 24 | + (NSDictionary *)fetchNowWeatherForLocation:(NSString *)location apiKey:(NSString *) apiKey dateLocale:(NSString *)dateLocale{ 25 | NSString *res = [self getDataFrom:[NSString stringWithFormat:@"https://devapi.qweather.com/v7/weather/now?location=%@&key=%@&lang=%@", location, apiKey, [self convertDateLocaleToLang:dateLocale]]]; 26 | NSData *data = [res dataUsingEncoding:NSUTF8StringEncoding]; 27 | NSError *erro = nil; 28 | if (data!=nil) { 29 | NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&erro ]; 30 | return json; 31 | } 32 | return nil; 33 | } 34 | 35 | + (NSDictionary *)fetchTodayWeatherForLocation:(NSString *)location apiKey:(NSString *) apiKey dateLocale:(NSString *)dateLocale{ 36 | NSString *res = [self getDataFrom:[NSString stringWithFormat:@"https://devapi.qweather.com/v7/weather/3d?location=%@&key=%@&lang=%@", location, apiKey, [self convertDateLocaleToLang:dateLocale]]]; 37 | NSData *data = [res dataUsingEncoding:NSUTF8StringEncoding]; 38 | NSError *erro = nil; 39 | if (data!=nil) { 40 | NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&erro ]; 41 | return json; 42 | } 43 | return nil; 44 | } 45 | 46 | + (NSData *)fetchLocationIDForName:(NSString *)name apiKey:(NSString *) apiKey dateLocale:(NSString *)dateLocale{ 47 | NSString *res = [self getDataFrom:[NSString stringWithFormat:@"https://geoapi.qweather.com/v2/city/lookup?location=%@&key=%@&lang=%@", [self encodeURIComponent:name], apiKey, [self convertDateLocaleToLang:dateLocale]]]; 48 | NSData *data = [res dataUsingEncoding:NSUTF8StringEncoding]; 49 | if (data!=nil) { 50 | // NSLog(@"weather location:%@", data); 51 | return data; 52 | } 53 | return nil; 54 | } 55 | 56 | + (NSString*)formatNowResult:(NSDictionary *)data format:(NSString *)format { 57 | // NSLog(@"weather now:%@", data); 58 | if(data && [data[@"code"] isEqualToString:@"200"]) { 59 | format = [format stringByReplacingOccurrencesOfString:@"{i}" withString:[WeatherUtils getWeatherIcon: data[@"now"][@"text"]]]; 60 | format = [format stringByReplacingOccurrencesOfString:@"{n}" withString:data[@"now"][@"text"]]; 61 | format = [format stringByReplacingOccurrencesOfString:@"{t}" withString:data[@"now"][@"temp"]]; 62 | format = [format stringByReplacingOccurrencesOfString:@"{bt}" withString:data[@"now"][@"feelsLike"]]; 63 | format = [format stringByReplacingOccurrencesOfString:@"{h}" withString:data[@"now"][@"humidity"]]; 64 | format = [format stringByReplacingOccurrencesOfString:@"{w}" withString:data[@"now"][@"windDir"]]; 65 | format = [format stringByReplacingOccurrencesOfString:@"{wp}" withString:data[@"now"][@"windScale"]]; 66 | format = [format stringByReplacingOccurrencesOfString:@"{pc}" withString:data[@"now"][@"precip"]]; 67 | format = [format stringByReplacingOccurrencesOfString:@"{ps}" withString:data[@"now"][@"pressure"]]; 68 | format = [format stringByReplacingOccurrencesOfString:@"{v}" withString:data[@"now"][@"vis"]]; 69 | format = [format stringByReplacingOccurrencesOfString:@"{c}" withString:data[@"now"][@"cloud"]]; 70 | } else { 71 | format = NSLocalizedString(@"error", comment:@""); 72 | } 73 | return format; 74 | } 75 | 76 | + (NSString*)formatTodayResult:(NSDictionary *)data format:(NSString *)format { 77 | // NSLog(@"weather today:%@", data); 78 | if(data && [data[@"code"] isEqualToString:@"200"]) { 79 | format = [format stringByReplacingOccurrencesOfString:@"{di}" withString:[WeatherUtils getWeatherIcon: data[@"daily"][0][@"textDay"]]]; 80 | format = [format stringByReplacingOccurrencesOfString:@"{dn}" withString:data[@"daily"][0][@"textDay"]]; 81 | format = [format stringByReplacingOccurrencesOfString:@"{dt}" withString:data[@"daily"][0][@"tempMax"]]; 82 | format = [format stringByReplacingOccurrencesOfString:@"{dw}" withString:data[@"daily"][0][@"windDirDay"]]; 83 | format = [format stringByReplacingOccurrencesOfString:@"{dwp}" withString:data[@"daily"][0][@"windScaleDay"]]; 84 | format = [format stringByReplacingOccurrencesOfString:@"{ni}" withString:[WeatherUtils getWeatherIcon: data[@"daily"][0][@"textNight"]]]; 85 | format = [format stringByReplacingOccurrencesOfString:@"{nn}" withString:data[@"daily"][0][@"textNight"]]; 86 | format = [format stringByReplacingOccurrencesOfString:@"{nt}" withString:data[@"daily"][0][@"tempMin"]]; 87 | format = [format stringByReplacingOccurrencesOfString:@"{nw}" withString:data[@"daily"][0][@"windDirNight"]]; 88 | format = [format stringByReplacingOccurrencesOfString:@"{nwp}" withString:data[@"daily"][0][@"windScaleNight"]]; 89 | format = [format stringByReplacingOccurrencesOfString:@"{tpc}" withString:data[@"daily"][0][@"precip"]]; 90 | format = [format stringByReplacingOccurrencesOfString:@"{tuv}" withString:data[@"daily"][0][@"uvIndex"]]; 91 | format = [format stringByReplacingOccurrencesOfString:@"{th}" withString:data[@"daily"][0][@"humidity"]]; 92 | format = [format stringByReplacingOccurrencesOfString:@"{tps}" withString:data[@"daily"][0][@"pressure"]]; 93 | format = [format stringByReplacingOccurrencesOfString:@"{tv}" withString:data[@"daily"][0][@"vis"]]; 94 | format = [format stringByReplacingOccurrencesOfString:@"{tc}" withString:data[@"daily"][0][@"cloud"]]; 95 | } else { 96 | format = NSLocalizedString(@"error", comment:@""); 97 | } 98 | return format; 99 | } 100 | 101 | + (NSString *)getDataFrom:(NSString *)url{ 102 | // NSLog(@"url:%@", url); 103 | NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init]; 104 | [request setHTTPMethod:@"GET"]; 105 | [request setURL:[NSURL URLWithString:url]]; 106 | 107 | NSError *error = nil; 108 | NSHTTPURLResponse *responseCode = nil; 109 | 110 | NSData *oResponseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&responseCode error:&error]; 111 | 112 | if([responseCode statusCode] != 200){ 113 | NSLog(@"Error getting %@, HTTP status code %li", url, [responseCode statusCode]); 114 | return nil; 115 | } 116 | 117 | return [[NSString alloc] initWithData:oResponseData encoding:NSUTF8StringEncoding]; 118 | } 119 | 120 | + (NSString *)convertDateLocaleToLang:(NSString *)locale{ 121 | NSArray *items = @[@"zh_CN", @"en_US"]; 122 | int item = [items indexOfObject:locale]; 123 | switch(item) { 124 | case 0: return @"zh"; 125 | case 1: return @"en"; 126 | default: return @"en"; 127 | } 128 | } 129 | 130 | + (NSString *)encodeURIComponent:(NSString *)string 131 | { 132 | NSString *s = [string stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; 133 | return s; 134 | } 135 | @end 136 | -------------------------------------------------------------------------------- /src/helpers/private_headers/AXEventHandInfoRepresentation.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "AXEventPathInfoRepresentation.h" 3 | 4 | @interface AXEventHandInfoRepresentation : NSObject 5 | - (NSArray *)paths; 6 | @end -------------------------------------------------------------------------------- /src/helpers/private_headers/AXEventPathInfoRepresentation.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface AXEventPathInfoRepresentation : NSObject 4 | @property (assign, nonatomic) unsigned char pathIdentity; 5 | @end -------------------------------------------------------------------------------- /src/helpers/private_headers/AXEventRepresentation.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "AXEventHandInfoRepresentation.h" 3 | #import "IOKit+SPI.h" 4 | 5 | @interface AXEventRepresentation : NSObject 6 | @property (nonatomic, readonly) BOOL isTouchDown; 7 | @property (nonatomic, readonly) BOOL isMove; 8 | @property (nonatomic, readonly) BOOL isChordChange; 9 | @property (nonatomic, readonly) BOOL isLift; 10 | @property (nonatomic, readonly) BOOL isInRange; 11 | @property (nonatomic, readonly) BOOL isInRangeLift; 12 | @property (nonatomic, readonly) BOOL isCancel; 13 | + (instancetype)representationWithHIDEvent:(IOHIDEventRef)event hidStreamIdentifier:(NSString *)identifier; 14 | - (AXEventHandInfoRepresentation *)handInfo; 15 | - (CGPoint)location; 16 | @end -------------------------------------------------------------------------------- /src/helpers/private_headers/BackboardServices.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "IOKit+SPI.h" 3 | 4 | #if __cplusplus 5 | extern "C" { 6 | #endif 7 | void *BKSHIDEventRegisterEventCallback(void (*)(void *, void *, IOHIDServiceRef, IOHIDEventRef)); 8 | void UIApplicationInstantiateSingleton(id aclass); 9 | void UIApplicationInitialize(); 10 | void BKSDisplayServicesStart(); 11 | void GSInitialize(); 12 | void GSEventInitialize(Boolean registerPurple); 13 | void GSEventPopRunLoopMode(CFStringRef mode); 14 | void GSEventPushRunLoopMode(CFStringRef mode); 15 | void GSEventRegisterEventCallBack(void (*)(GSEventRef)); 16 | #if __cplusplus 17 | } 18 | #endif -------------------------------------------------------------------------------- /src/helpers/private_headers/CAFilter.h: -------------------------------------------------------------------------------- 1 | /* CoreAnimation - CAFilter.h 2 | 3 | Copyright (c) 2006-2007 Apple Inc. 4 | All rights reserved. */ 5 | 6 | #ifndef CAFILTER_H 7 | #define CAFILTER_H 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | CA_EXTERN_C_BEGIN 13 | 14 | @interface CAFilter : NSObject 15 | 16 | @property (class, readonly) NSArray *_Nonnull filterTypes; 17 | 18 | @property (assign) BOOL cachesInputImage; 19 | @property (assign, getter=isEnabled) BOOL enabled; 20 | @property (copy) NSString *name; 21 | @property (readonly, assign) NSString *type; 22 | 23 | @property (readonly, strong) NSArray *inputKeys; 24 | @property (readonly, strong) NSArray *outputKeys; 25 | 26 | + (nullable CAFilter *)filterWithType:(nonnull NSString *)type NS_SWIFT_UNAVAILABLE("Use init(type:) instead."); 27 | + (nullable CAFilter *)filterWithName:(nonnull NSString *)name NS_SWIFT_UNAVAILABLE("Use init(type:) instead."); 28 | - (nullable instancetype)initWithType:(nonnull NSString *)type; 29 | - (nullable instancetype)initWithName:(nonnull NSString *)name NS_SWIFT_UNAVAILABLE("Use init(type:) instead."); 30 | 31 | - (void)setDefaults; 32 | 33 | @end 34 | 35 | /** Filter types. **/ 36 | 37 | CA_EXTERN NSString * const kCAFilterMultiplyColor; 38 | CA_EXTERN NSString * const kCAFilterColorAdd; 39 | CA_EXTERN NSString * const kCAFilterColorSubtract; 40 | CA_EXTERN NSString * const kCAFilterColorMonochrome; 41 | CA_EXTERN NSString * const kCAFilterColorMatrix; 42 | CA_EXTERN NSString * const kCAFilterColorHueRotate; 43 | CA_EXTERN NSString * const kCAFilterColorSaturate; 44 | CA_EXTERN NSString * const kCAFilterColorBrightness; 45 | CA_EXTERN NSString * const kCAFilterColorContrast; 46 | CA_EXTERN NSString * const kCAFilterColorInvert; 47 | CA_EXTERN NSString * const kCAFilterLuminanceToAlpha; 48 | CA_EXTERN NSString * const kCAFilterBias; 49 | CA_EXTERN NSString * const kCAFilterDistanceField; 50 | CA_EXTERN NSString * const kCAFilterGaussianBlur; 51 | CA_EXTERN NSString * const kCAFilterLanczosResize; 52 | CA_EXTERN NSString * const kCAFilterClear; 53 | CA_EXTERN NSString * const kCAFilterCopy; 54 | CA_EXTERN NSString * const kCAFilterSourceOver; 55 | CA_EXTERN NSString * const kCAFilterSourceIn; 56 | CA_EXTERN NSString * const kCAFilterSourceOut; 57 | CA_EXTERN NSString * const kCAFilterSourceAtop; 58 | CA_EXTERN NSString * const kCAFilterDest; 59 | CA_EXTERN NSString * const kCAFilterDestOver; 60 | CA_EXTERN NSString * const kCAFilterDestIn; 61 | CA_EXTERN NSString * const kCAFilterDestOut; 62 | CA_EXTERN NSString * const kCAFilterDestAtop; 63 | CA_EXTERN NSString * const kCAFilterXor; 64 | CA_EXTERN NSString * const kCAFilterPlusL; 65 | CA_EXTERN NSString * const kCAFilterSubtractS; 66 | CA_EXTERN NSString * const kCAFilterSubtractD; 67 | CA_EXTERN NSString * const kCAFilterMultiply; 68 | CA_EXTERN NSString * const kCAFilterMinimum; 69 | CA_EXTERN NSString * const kCAFilterMaximum; 70 | CA_EXTERN NSString * const kCAFilterPlusD; 71 | CA_EXTERN NSString * const kCAFilterNormalBlendMode; 72 | CA_EXTERN NSString * const kCAFilterMultiplyBlendMode; 73 | CA_EXTERN NSString * const kCAFilterScreenBlendMode; 74 | CA_EXTERN NSString * const kCAFilterOverlayBlendMode; 75 | CA_EXTERN NSString * const kCAFilterDarkenBlendMode; 76 | CA_EXTERN NSString * const kCAFilterLightenBlendMode; 77 | CA_EXTERN NSString * const kCAFilterColorDodgeBlendMode; 78 | CA_EXTERN NSString * const kCAFilterColorBurnBlendMode; 79 | CA_EXTERN NSString * const kCAFilterSoftLightBlendMode; 80 | CA_EXTERN NSString * const kCAFilterHardLightBlendMode; 81 | CA_EXTERN NSString * const kCAFilterDifferenceBlendMode; 82 | CA_EXTERN NSString * const kCAFilterExclusionBlendMode; 83 | CA_EXTERN NSString * const kCAFilterSubtractBlendMode; 84 | CA_EXTERN NSString * const kCAFilterDivideBlendMode; 85 | CA_EXTERN NSString * const kCAFilterLinearBurnBlendMode; 86 | CA_EXTERN NSString * const kCAFilterLinearDodgeBlendMode; 87 | CA_EXTERN NSString * const kCAFilterLinearLightBlendMode; 88 | CA_EXTERN NSString * const kCAFilterPinLightBlendMode; 89 | CA_EXTERN NSString * const kCAFilterPageCurl; 90 | CA_EXTERN NSString * const kCAFilterVibrantDark; 91 | CA_EXTERN NSString * const kCAFilterVibrantLight; 92 | CA_EXTERN NSString * const kCAFilterDarkenSourceOver; 93 | CA_EXTERN NSString * const kCAFilterLightenSourceOver; 94 | 95 | CA_EXTERN NSString * const kCAFilterHomeAffordanceBase; 96 | 97 | CA_EXTERN_C_END 98 | 99 | struct CAColorMatrix { 100 | float m11, m12, m13, m14, m15; 101 | float m21, m22, m23, m24, m25; 102 | float m31, m32, m33, m34, m35; 103 | float m41, m42, m43, m44, m45; 104 | }; 105 | typedef struct CAColorMatrix CAColorMatrix; 106 | 107 | @interface NSValue (CADetails) 108 | + (NSValue *)valueWithCAColorMatrix:(CAColorMatrix)t; 109 | @end 110 | 111 | NS_ASSUME_NONNULL_END 112 | 113 | #endif // CAFILTER_H -------------------------------------------------------------------------------- /src/helpers/private_headers/FBSOrientationObserver.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface FBSOrientationObserver : NSObject 4 | - (long long)activeInterfaceOrientation; 5 | - (void)activeInterfaceOrientationWithCompletion:(id)arg1; 6 | - (void)invalidate; 7 | - (void)setHandler:(id)arg1; 8 | - (id)handler; 9 | @end -------------------------------------------------------------------------------- /src/helpers/private_headers/FBSOrientationUpdate.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface FBSOrientationUpdate : NSObject 4 | - (unsigned long long)sequenceNumber; 5 | - (long long)rotationDirection; 6 | - (long long)orientation; 7 | - (double)duration; 8 | @end -------------------------------------------------------------------------------- /src/helpers/private_headers/IOHIDEvent+KIF.h: -------------------------------------------------------------------------------- 1 | // 2 | // IOHIDEvent+KIF.h 3 | // KIF 4 | // 5 | // Created by Thomas Bonnin on 7/6/15. 6 | // 7 | 8 | #import 9 | #import "IOKit+SPI.h" 10 | 11 | typedef struct __IOHIDEvent * IOHIDEventRef; 12 | IOHIDEventRef kif_IOHIDEventWithTouches(NSArray *touches) CF_RETURNS_RETAINED; -------------------------------------------------------------------------------- /src/helpers/private_headers/IOHIDEvent+KIF.m: -------------------------------------------------------------------------------- 1 | // 2 | // IOHIDEvent+KIF.m 3 | // KIF 4 | // 5 | // Created by Thomas Bonnin on 7/6/15. 6 | // 7 | 8 | #import "IOHIDEvent+KIF.h" 9 | #import 10 | #import 11 | 12 | #define IOHIDEventFieldBase(type) (type << 16) 13 | 14 | #ifdef __LP64__ 15 | typedef double IOHIDFloat; 16 | #else 17 | typedef float IOHIDFloat; 18 | #endif 19 | 20 | typedef UInt32 IOOptionBits; 21 | typedef uint32_t IOHIDDigitizerTransducerType; 22 | typedef uint32_t IOHIDEventField; 23 | typedef uint32_t IOHIDEventType; 24 | 25 | void IOHIDEventAppendEvent(IOHIDEventRef event, IOHIDEventRef childEvent); 26 | void IOHIDEventSetIntegerValue(IOHIDEventRef event, IOHIDEventField field, 27 | int value); 28 | void IOHIDEventSetSenderID(IOHIDEventRef event, uint64_t sender); 29 | 30 | // Derived from 31 | // https://opensource.apple.com/source/IOHIDFamily/IOHIDFamily-606.1.7/IOHIDFamily/IOHIDEventTypes.h 32 | 33 | enum { 34 | kIOHIDDigitizerTransducerTypeStylus = 0, 35 | kIOHIDDigitizerTransducerTypePuck, 36 | kIOHIDDigitizerTransducerTypeFinger, 37 | kIOHIDDigitizerTransducerTypeHand 38 | }; 39 | 40 | enum { 41 | kIOHIDEventTypeNULL, // 0 42 | kIOHIDEventTypeVendorDefined, 43 | kIOHIDEventTypeButton, 44 | kIOHIDEventTypeKeyboard, 45 | kIOHIDEventTypeTranslation, 46 | kIOHIDEventTypeRotation, // 5 47 | kIOHIDEventTypeScroll, 48 | kIOHIDEventTypeScale, 49 | kIOHIDEventTypeZoom, 50 | kIOHIDEventTypeVelocity, 51 | kIOHIDEventTypeOrientation, // 10 52 | kIOHIDEventTypeDigitizer, 53 | kIOHIDEventTypeAmbientLightSensor, 54 | kIOHIDEventTypeAccelerometer, 55 | kIOHIDEventTypeProximity, 56 | kIOHIDEventTypeTemperature, // 15 57 | kIOHIDEventTypeNavigationSwipe, 58 | kIOHIDEventTypePointer, 59 | kIOHIDEventTypeProgress, 60 | kIOHIDEventTypeMultiAxisPointer, 61 | kIOHIDEventTypeGyro, // 20 62 | kIOHIDEventTypeCompass, 63 | kIOHIDEventTypeZoomToggle, 64 | kIOHIDEventTypeDockSwipe, // Just like kIOHIDEventTypeNavigationSwipe, but 65 | // intended for consumption by Dock 66 | kIOHIDEventTypeSymbolicHotKey, 67 | kIOHIDEventTypePower, // 25 68 | kIOHIDEventTypeLED, 69 | kIOHIDEventTypeFluidTouchGesture, // This will eventually superseed Navagation 70 | // and Dock swipes 71 | kIOHIDEventTypeBoundaryScroll, 72 | kIOHIDEventTypeBiometric, 73 | kIOHIDEventTypeUnicode, // 30 74 | kIOHIDEventTypeAtmosphericPressure, 75 | kIOHIDEventTypeUndefined, 76 | kIOHIDEventTypeCount, // This should always be last 77 | 78 | // DEPRECATED: 79 | kIOHIDEventTypeSwipe = kIOHIDEventTypeNavigationSwipe, 80 | kIOHIDEventTypeMouse = kIOHIDEventTypePointer 81 | }; 82 | 83 | enum { 84 | kIOHIDDigitizerEventRange = 1 << 0, 85 | kIOHIDDigitizerEventTouch = 1 << 1, 86 | kIOHIDDigitizerEventPosition = 1 << 2, 87 | kIOHIDDigitizerEventStop = 1 << 3, 88 | kIOHIDDigitizerEventPeak = 1 << 4, 89 | kIOHIDDigitizerEventIdentity = 1 << 5, 90 | kIOHIDDigitizerEventAttribute = 1 << 6, 91 | kIOHIDDigitizerEventCancel = 1 << 7, 92 | kIOHIDDigitizerEventStart = 1 << 8, 93 | kIOHIDDigitizerEventResting = 1 << 9, 94 | kIOHIDDigitizerEventFromEdgeFlat = 1 << 10, 95 | kIOHIDDigitizerEventFromEdgeTip = 1 << 11, 96 | kIOHIDDigitizerEventFromCorner = 1 << 12, 97 | kIOHIDDigitizerEventSwipePending = 1 << 13, 98 | kIOHIDDigitizerEventSwipeUp = 1 << 24, 99 | kIOHIDDigitizerEventSwipeDown = 1 << 25, 100 | kIOHIDDigitizerEventSwipeLeft = 1 << 26, 101 | kIOHIDDigitizerEventSwipeRight = 1 << 27, 102 | kIOHIDDigitizerEventSwipeMask = 0xFF << 24, 103 | }; 104 | 105 | enum { 106 | kIOHIDEventFieldDigitizerX = IOHIDEventFieldBase(kIOHIDEventTypeDigitizer), 107 | kIOHIDEventFieldDigitizerY, 108 | kIOHIDEventFieldDigitizerZ, 109 | kIOHIDEventFieldDigitizerButtonMask, 110 | kIOHIDEventFieldDigitizerType, 111 | kIOHIDEventFieldDigitizerIndex, 112 | kIOHIDEventFieldDigitizerIdentity, 113 | kIOHIDEventFieldDigitizerEventMask, 114 | kIOHIDEventFieldDigitizerRange, 115 | kIOHIDEventFieldDigitizerTouch, 116 | kIOHIDEventFieldDigitizerPressure, 117 | kIOHIDEventFieldDigitizerAuxiliaryPressure, // BarrelPressure 118 | kIOHIDEventFieldDigitizerTwist, 119 | kIOHIDEventFieldDigitizerTiltX, 120 | kIOHIDEventFieldDigitizerTiltY, 121 | kIOHIDEventFieldDigitizerAltitude, 122 | kIOHIDEventFieldDigitizerAzimuth, 123 | kIOHIDEventFieldDigitizerQuality, 124 | kIOHIDEventFieldDigitizerDensity, 125 | kIOHIDEventFieldDigitizerIrregularity, 126 | kIOHIDEventFieldDigitizerMajorRadius, 127 | kIOHIDEventFieldDigitizerMinorRadius, 128 | kIOHIDEventFieldDigitizerCollection, 129 | kIOHIDEventFieldDigitizerCollectionChord, 130 | kIOHIDEventFieldDigitizerChildEventMask, 131 | kIOHIDEventFieldDigitizerIsDisplayIntegrated, 132 | kIOHIDEventFieldDigitizerQualityRadiiAccuracy, 133 | }; 134 | 135 | IOHIDEventRef IOHIDEventCreateDigitizerEvent( 136 | CFAllocatorRef allocator, AbsoluteTime timeStamp, 137 | IOHIDDigitizerTransducerType type, uint32_t index, uint32_t identity, 138 | uint32_t eventMask, uint32_t buttonMask, IOHIDFloat x, IOHIDFloat y, 139 | IOHIDFloat z, IOHIDFloat tipPressure, IOHIDFloat barrelPressure, 140 | Boolean range, Boolean touch, IOOptionBits options); 141 | 142 | IOHIDEventRef IOHIDEventCreateDigitizerFingerEventWithQuality( 143 | CFAllocatorRef allocator, AbsoluteTime timeStamp, uint32_t index, 144 | uint32_t identity, uint32_t eventMask, IOHIDFloat x, IOHIDFloat y, 145 | IOHIDFloat z, IOHIDFloat tipPressure, IOHIDFloat twist, 146 | IOHIDFloat minorRadius, IOHIDFloat majorRadius, IOHIDFloat quality, 147 | IOHIDFloat density, IOHIDFloat irregularity, Boolean range, Boolean touch, 148 | IOOptionBits options); 149 | 150 | IOHIDEventRef kif_IOHIDEventWithTouches(NSArray *touches) 151 | { 152 | uint64_t abTime = mach_absolute_time(); 153 | AbsoluteTime timeStamp; 154 | timeStamp.hi = (UInt32)(abTime >> 32); 155 | timeStamp.lo = (UInt32)(abTime); 156 | 157 | IOHIDEventRef handEvent = IOHIDEventCreateDigitizerEvent( 158 | kCFAllocatorDefault, // allocator 内存分配器 159 | timeStamp, // timestamp 时间戳 160 | kIOHIDDigitizerTransducerTypeHand, // type 161 | 0, // index 162 | 0, // identity 163 | kIOHIDDigitizerEventTouch, // eventMask 164 | 0, // buttonMask 165 | 0, // x 166 | 0, // y 167 | 0, // z 168 | 0, // tipPressure 169 | 0, // barrelPressure 170 | 0, // range 171 | true, // touch 172 | 0); // options 173 | 174 | IOHIDEventSetIntegerValue(handEvent, 175 | kIOHIDEventFieldDigitizerIsDisplayIntegrated, true); 176 | 177 | for (UITouch *touch in touches) 178 | { 179 | uint32_t eventMask = 180 | (touch.phase == UITouchPhaseMoved) 181 | ? kIOHIDDigitizerEventPosition 182 | : (kIOHIDDigitizerEventRange | kIOHIDDigitizerEventTouch); 183 | 184 | uint32_t isTouching = (touch.phase == UITouchPhaseEnded) ? 0 : 1; 185 | CGPoint touchLocation = [touch locationInView:touch.window]; 186 | IOHIDEventRef fingerEvent = IOHIDEventCreateDigitizerFingerEventWithQuality( 187 | kCFAllocatorDefault, // allocator 188 | timeStamp, // timestamp 189 | (UInt32)[touches indexOfObject:touch] + 1, // index 190 | 2, // identity 191 | eventMask, // eventMask 192 | (IOHIDFloat)touchLocation.x, // x 193 | (IOHIDFloat)touchLocation.y, // y 194 | 0.0, // z 195 | 0, // tipPressure 196 | 0, // twist 197 | 5.0, // minor radius 198 | 5.0, // major radius 199 | 1.0, // quality 200 | 1.0, // density 201 | 1.0, // irregularity 202 | (IOHIDFloat)isTouching, // range 203 | (IOHIDFloat)isTouching, // touch 204 | 0); // options 205 | 206 | IOHIDEventSetIntegerValue(fingerEvent, 207 | kIOHIDEventFieldDigitizerIsDisplayIntegrated, 1); 208 | 209 | IOHIDEventAppendEvent(handEvent, fingerEvent); 210 | CFRelease(fingerEvent); 211 | } 212 | 213 | return handEvent; 214 | } -------------------------------------------------------------------------------- /src/helpers/private_headers/IOKit+SPI.h: -------------------------------------------------------------------------------- 1 | typedef struct __IOHIDEvent *IOHIDEventRef; 2 | typedef struct __IOHIDNotification *IOHIDNotificationRef; 3 | typedef struct __IOHIDService *IOHIDServiceRef; 4 | typedef struct __GSEvent *GSEventRef; -------------------------------------------------------------------------------- /src/helpers/private_headers/LSApplicationProxy.h: -------------------------------------------------------------------------------- 1 | #ifndef LSApplicationProxy_h 2 | #define LSApplicationProxy_h 3 | 4 | #import 5 | 6 | @class LSPlugInKitProxy; 7 | 8 | @interface LSApplicationProxy : NSObject 9 | + (LSApplicationProxy *)applicationProxyForIdentifier:(NSString *)bundleIdentifier; 10 | - (BOOL)installed; 11 | - (BOOL)restricted; 12 | - (NSString *)applicationIdentifier; 13 | - (NSString *)localizedName; 14 | - (NSString *)shortVersionString; 15 | - (NSString *)applicationType; 16 | - (NSURL *)bundleURL; 17 | - (NSURL *)dataContainerURL; 18 | - (NSURL *)bundleContainerURL; 19 | - (NSDictionary *)groupContainerURLs; 20 | - (NSDictionary *)entitlements; 21 | - (NSArray *)plugInKitPlugins; 22 | @end 23 | 24 | #endif /* LSApplicationProxy_h */ 25 | -------------------------------------------------------------------------------- /src/helpers/private_headers/LSApplicationWorkspace.h: -------------------------------------------------------------------------------- 1 | #ifndef LSApplicationWorkspace_h 2 | #define LSApplicationWorkspace_h 3 | 4 | #import 5 | 6 | @class LSApplicationProxy; 7 | 8 | @interface LSApplicationWorkspace : NSObject 9 | + (LSApplicationWorkspace *)defaultWorkspace; 10 | - (NSArray *)allApplications; 11 | - (void)enumerateApplicationsOfType:(NSInteger)type block:(void (^)(id))block; 12 | - (BOOL)openApplicationWithBundleID:(NSString *)bundleIdentifier; 13 | - (BOOL)installApplication:(NSURL *)ipaPath withOptions:(id)arg2 error:(NSError *__autoreleasing*)error; 14 | - (BOOL)uninstallApplication:(NSString *)bundleIdentifier withOptions:(id)arg2; 15 | - (BOOL)uninstallApplication:(NSString *)arg1 withOptions:(id)arg2 error:(NSError *__autoreleasing*)arg3 usingBlock:(/*^block*/id)arg4 ; 16 | - (BOOL)invalidateIconCache:(id)arg1; 17 | - (BOOL)openSensitiveURL:(NSURL *)url withOptions:(id)arg2 error:(NSError *__autoreleasing*)error; 18 | - (void)removeObserver:(id)arg1; 19 | - (void)addObserver:(id)arg1; 20 | @end 21 | 22 | #endif /* LSApplicationWorkspace_h */ 23 | -------------------------------------------------------------------------------- /src/helpers/private_headers/NSUserDefaults+Private.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface NSUserDefaults (Private) 4 | - (instancetype)_initWithSuiteName:(NSString *)suiteName container:(NSURL *)container; 5 | - (id)objectForKey:(NSString *)key inDomain:(NSString *)domain; 6 | - (void)setObject:(id)value forKey:(NSString *)key inDomain:(NSString *)domain; 7 | @end -------------------------------------------------------------------------------- /src/helpers/private_headers/SBSAccessibilityWindowHostingController.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface SBSAccessibilityWindowHostingController : NSObject 4 | - (void)registerWindowWithContextID:(unsigned)arg1 atLevel:(double)arg2; 5 | @end -------------------------------------------------------------------------------- /src/helpers/private_headers/SpringBoardServices.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | FOUNDATION_EXPORT mach_port_t SBSSpringBoardServerPort(); 4 | 5 | FOUNDATION_EXPORT void SBFrontmostApplicationDisplayIdentifier(mach_port_t port, char *result); 6 | NSString *SBSCopyFrontmostApplicationDisplayIdentifier(); 7 | FOUNDATION_EXPORT void SBGetScreenLockStatus(mach_port_t port, BOOL *lockStatus, BOOL *passcodeEnabled); 8 | FOUNDATION_EXPORT void SBSUndimScreen(); 9 | 10 | FOUNDATION_EXPORT int SBSLaunchApplicationWithIdentifierAndURLAndLaunchOptions(NSString *bundleIdentifier, NSURL *url, NSDictionary *appOptions, NSDictionary *launchOptions, BOOL suspended); 11 | FOUNDATION_EXPORT int SBSLaunchApplicationWithIdentifierAndLaunchOptions(NSString *bundleIdentifier, NSDictionary *appOptions, NSDictionary *launchOptions, BOOL suspended); 12 | FOUNDATION_EXPORT bool SBSOpenSensitiveURLAndUnlock(CFURLRef url, char flags); 13 | 14 | FOUNDATION_EXPORT NSString *const SBSApplicationLaunchOptionUnlockDeviceKey; -------------------------------------------------------------------------------- /src/helpers/private_headers/UIApplication+Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIApplication+Private.h 3 | // FakeTouch 4 | // 5 | // Created by Watanabe Toshinori on 2/6/19. 6 | // Copyright © 2019 Watanabe Toshinori. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "IOKit+SPI.h" 11 | 12 | @interface UIApplication (Private) 13 | - (UIEvent *)_touchesEvent; 14 | - (void)_run; 15 | - (void)suspend; 16 | - (void)_accessibilityInit; 17 | - (void)terminateWithSuccess; 18 | - (void)__completeAndRunAsPlugin; 19 | - (id)_systemAnimationFenceExemptQueue; 20 | - (void)_enqueueHIDEvent:(IOHIDEventRef)event; 21 | @end -------------------------------------------------------------------------------- /src/helpers/private_headers/UIApplicationRotationFollowingController.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface UIApplicationRotationFollowingController : UIViewController { 4 | bool _sizesWindowToScene; 5 | } 6 | 7 | @property (assign,nonatomic) bool sizesWindowToScene; 8 | 9 | - (unsigned long long)supportedInterfaceOrientations; 10 | - (bool)shouldAutorotate; 11 | - (id)__autorotationSanityCheckObjectFromSource:(id)arg1 selector:(SEL)arg2; 12 | - (id)initWithNibName:(id)arg1 bundle:(id)arg2; 13 | - (long long)_preferredInterfaceOrientationGivenCurrentOrientation:(long long)arg1; 14 | - (void)window:(id)arg1 setupWithInterfaceOrientation:(long long)arg2; 15 | - (bool)sizesWindowToScene; 16 | - (bool)shouldAutorotateToInterfaceOrientation:(long long)arg1; 17 | - (void)setSizesWindowToScene:(bool)arg1; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /src/helpers/private_headers/UIApplicationRotationFollowingControllerNoTouches.h: -------------------------------------------------------------------------------- 1 | #import "UIApplicationRotationFollowingController.h" 2 | 3 | @interface UIApplicationRotationFollowingControllerNoTouches : UIApplicationRotationFollowingController 4 | - (void)loadView; 5 | - (void)_prepareForRotationToOrientation:(UIInterfaceOrientation)arg1 duration:(NSTimeInterval)arg2; 6 | - (void)_rotateToOrientation:(UIInterfaceOrientation)arg1 duration:(NSTimeInterval)arg2; 7 | - (void)_finishRotationFromInterfaceOrientation:(UIInterfaceOrientation)arg1; 8 | @end 9 | -------------------------------------------------------------------------------- /src/helpers/private_headers/UIApplicationRotationFollowingWindow.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface UIApplicationRotationFollowingWindow : UIWindow 4 | 5 | + (BOOL)_isSystemWindow; 6 | - (id)init; 7 | - (void)dealloc; 8 | - (id)__autorotationSanityCheckObjectFromSource:(id)arg1 selector:(SEL)arg2; 9 | - (void)applicationWindowRotated:(id)arg1 ; 10 | - (void)_commonApplicationRotationFollowingWindowInit; 11 | - (id)_initWithFrame:(CGRect)arg1 attached:(BOOL)arg2; 12 | - (BOOL)_shouldControlAutorotation; 13 | - (BOOL)_shouldAutorotateToInterfaceOrientation:(long long)arg1; 14 | - (BOOL)isInterfaceAutorotationDisabled; 15 | - (void)_handleStatusBarOrientationChange:(id)arg1; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /src/helpers/private_headers/UIAutoRotatingWindow.h: -------------------------------------------------------------------------------- 1 | #import "UIApplicationRotationFollowingWindow.h" 2 | 3 | @interface UIAutoRotatingWindow : UIApplicationRotationFollowingWindow { 4 | UIInterfaceOrientation _interfaceOrientation; 5 | BOOL _unknownOrientation; 6 | } 7 | 8 | + (instancetype)sharedPopoverHostingWindow; 9 | - (void)commonInit; 10 | - (UIView *)hitTest:(CGPoint)arg1 withEvent:(UIEvent *)arg2; 11 | - (void)_didRemoveSubview:(UIView *)arg1; 12 | - (instancetype)_initWithFrame:(CGRect)arg1 attached:(BOOL)arg2; 13 | - (void)updateForOrientation:(UIInterfaceOrientation)arg1; 14 | - (instancetype)_initWithFrame:(CGRect)arg1 debugName:(NSString *)arg2 windowScene:(UIWindowScene *)arg3; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /src/helpers/private_headers/UIEvent+Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIEvent+Private.h 3 | // FakeTouch 4 | // 5 | // Created by Watanabe Toshinori on 2/6/19. 6 | // Copyright © 2019 Watanabe Toshinori. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIEvent (Private) 12 | - (void)_addTouch:(UITouch *)touch forDelayedDelivery:(BOOL)delayed; 13 | - (void)_clearTouches; 14 | @end -------------------------------------------------------------------------------- /src/helpers/private_headers/UIEventDispatcher.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface UIEventDispatcher : NSObject 4 | - (void)_installEventRunLoopSources:(CFRunLoopRef)arg1; 5 | @end -------------------------------------------------------------------------------- /src/helpers/private_headers/UIEventFetcher.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "IOKit+SPI.h" 3 | 4 | @class UIEventDispatcher; 5 | 6 | @interface UIEventFetcher : NSObject 7 | - (void)_receiveHIDEvent:(IOHIDEventRef)arg1; 8 | - (void)setEventFetcherSink:(UIEventDispatcher *)arg1; 9 | @end -------------------------------------------------------------------------------- /src/helpers/private_headers/UITouch+Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // UITouch+Private.h 3 | // FakeTouch 4 | // 5 | // Created by Watanabe Toshinori on 2/6/19. 6 | // Copyright © 2019 Watanabe Toshinori. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UITouch (Private) 12 | 13 | - (void)setWindow:(UIWindow *)window; 14 | - (void)setView:(UIView *)view; 15 | - (void)setIsTap:(BOOL)isTap; 16 | - (void)setTimestamp:(NSTimeInterval)timestamp; 17 | - (void)setPhase:(UITouchPhase)touchPhase; 18 | - (void)setGestureView:(UIView *)view; 19 | - (void)_setLocationInWindow:(CGPoint)location 20 | resetPrevious:(BOOL)resetPrevious; 21 | - (void)_setIsFirstTouchForView:(BOOL)firstTouchForView; 22 | - (void)_setIsTapToClick:(BOOL)isTapToClick; 23 | 24 | - (void)_setHidEvent:(IOHIDEventRef)event; 25 | 26 | @end -------------------------------------------------------------------------------- /src/helpers/private_headers/UITouch-KIFAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // UITouch-KIFAdditions.h 3 | // KIF 4 | // 5 | // Created by Eric Firestone on 5/20/11. 6 | // Licensed to Square, Inc. under one or more contributor license agreements. 7 | // See the LICENSE file distributed with this work for the terms under 8 | // which Square, Inc. licenses this file to you. 9 | // 10 | 11 | #import "IOHIDEvent+KIF.h" 12 | #import "UITouch+Private.h" 13 | #import 14 | 15 | @interface UITouch (KIFAdditions) 16 | 17 | - (instancetype)initAtPoint:(CGPoint)point 18 | inWindow:(UIWindow *)window 19 | onView:(UIView *)view; 20 | - (instancetype)initTouch; 21 | 22 | - (void)setLocationInWindow:(CGPoint)location; 23 | - (void)setPhaseAndUpdateTimestamp:(UITouchPhase)phase; 24 | 25 | @end -------------------------------------------------------------------------------- /src/helpers/private_headers/UITouch-KIFAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // UITouch-KIFAdditions.m 3 | // KIF 4 | // 5 | // Created by Eric Firestone on 5/20/11. 6 | // Licensed to Square, Inc. under one or more contributor license agreements. 7 | // See the LICENSE file distributed with this work for the terms under 8 | // which Square, Inc. licenses this file to you. 9 | // 10 | 11 | #import "UITouch-KIFAdditions.h" 12 | #import 13 | 14 | @implementation UITouch (KIFAdditions) 15 | 16 | - (instancetype)initAtPoint:(CGPoint)point 17 | inWindow:(UIWindow *)window 18 | onView:(UIView *)view; 19 | { 20 | self = [super init]; 21 | if (self == nil) 22 | return nil; 23 | 24 | // Create a fake tap touch 25 | [self setWindow:window]; // Wipes out some values. Needs to be first. 26 | [self _setLocationInWindow:point resetPrevious:YES]; 27 | 28 | UIView *hitTestView = view; 29 | 30 | [self setView:hitTestView]; 31 | [self setPhase:UITouchPhaseBegan]; 32 | if (![[NSProcessInfo processInfo] isiOSAppOnMac] && 33 | ![[NSProcessInfo processInfo] isMacCatalystApp]) 34 | { 35 | [self _setIsTapToClick:NO]; 36 | } 37 | else 38 | { 39 | [self _setIsFirstTouchForView:YES]; 40 | [self setIsTap:NO]; 41 | } 42 | 43 | [self setTimestamp:[[NSProcessInfo processInfo] systemUptime]]; 44 | 45 | if ([self respondsToSelector:@selector(setGestureView:)]) 46 | [self setGestureView:hitTestView]; 47 | 48 | [self kif_setHidEvent]; 49 | return self; 50 | } 51 | 52 | - (instancetype)initTouch; 53 | { 54 | self = [super init]; 55 | if (self == nil) 56 | return nil; 57 | 58 | NSArray *scenes = 59 | [[[UIApplication sharedApplication] connectedScenes] allObjects]; 60 | 61 | NSArray *windows = [[scenes objectAtIndex:0] windows]; 62 | UIWindow *window = [windows lastObject]; 63 | CGPoint point = CGPointMake(0, 0); 64 | 65 | [self setWindow:window]; // Wipes out some values. Needs to be first. 66 | [self _setLocationInWindow:point resetPrevious:YES]; 67 | 68 | UIView *hitTestView = [window hitTest:point withEvent:nil]; 69 | 70 | [self setView:hitTestView]; 71 | [self setPhase:UITouchPhaseEnded]; 72 | 73 | if (![[NSProcessInfo processInfo] isiOSAppOnMac] && 74 | ![[NSProcessInfo processInfo] isMacCatalystApp]) 75 | { 76 | [self _setIsTapToClick:NO]; 77 | } 78 | else 79 | { 80 | [self _setIsFirstTouchForView:YES]; 81 | [self setIsTap:NO]; 82 | } 83 | 84 | [self setTimestamp:[[NSProcessInfo processInfo] systemUptime]]; 85 | 86 | if ([self respondsToSelector:@selector(setGestureView:)]) 87 | [self setGestureView:hitTestView]; 88 | 89 | [self kif_setHidEvent]; 90 | return self; 91 | } 92 | 93 | - (void)setLocationInWindow:(CGPoint)location 94 | { 95 | [self setTimestamp:[[NSProcessInfo processInfo] systemUptime]]; 96 | [self _setLocationInWindow:location resetPrevious:NO]; 97 | } 98 | 99 | - (void)setPhaseAndUpdateTimestamp:(UITouchPhase)phase 100 | { 101 | [self setTimestamp:[[NSProcessInfo processInfo] systemUptime]]; 102 | [self setPhase:phase]; 103 | } 104 | 105 | - (void)kif_setHidEvent 106 | { 107 | IOHIDEventRef event = kif_IOHIDEventWithTouches(@[ self ]); 108 | [self _setHidEvent:event]; 109 | CFRelease(event); 110 | } 111 | 112 | @end -------------------------------------------------------------------------------- /src/helpers/private_headers/UIWindow+Private.h: -------------------------------------------------------------------------------- 1 | 2 | @interface UIWindow (Private) 3 | - (unsigned int)_contextId; 4 | @end -------------------------------------------------------------------------------- /src/helpers/private_headers/libproc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2006, 2007, 2010 Apple Inc. All rights reserved. 3 | * 4 | * @APPLE_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apple Public Source License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://www.opensource.apple.com/apsl/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPLE_LICENSE_HEADER_END@ 22 | */ 23 | #ifndef _LIBPROC_H_ 24 | #define _LIBPROC_H_ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include /* for audit_token_t */ 35 | 36 | #include 37 | 38 | #include 39 | #include 40 | 41 | /* 42 | * This header file contains private interfaces to obtain process information. 43 | * These interfaces are subject to change in future releases. 44 | */ 45 | 46 | /*! 47 | * @define PROC_LISTPIDSPATH_PATH_IS_VOLUME 48 | * @discussion This flag indicates that all processes that hold open 49 | * file references on the volume associated with the specified 50 | * path should be returned. 51 | */ 52 | #define PROC_LISTPIDSPATH_PATH_IS_VOLUME 1 53 | 54 | 55 | /*! 56 | * @define PROC_LISTPIDSPATH_EXCLUDE_EVTONLY 57 | * @discussion This flag indicates that file references that were opened 58 | * with the O_EVTONLY flag should be excluded from the matching 59 | * criteria. 60 | */ 61 | #define PROC_LISTPIDSPATH_EXCLUDE_EVTONLY 2 62 | 63 | __BEGIN_DECLS 64 | 65 | 66 | /*! 67 | * @function proc_listpidspath 68 | * @discussion A function which will search through the current 69 | * processes looking for open file references which match 70 | * a specified path or volume. 71 | * @param type types of processes to be searched (see proc_listpids) 72 | * @param typeinfo adjunct information for type 73 | * @param path file or volume path 74 | * @param pathflags flags to control which files should be considered 75 | * during the process search. 76 | * @param buffer a C array of int-sized values to be filled with 77 | * process identifiers that hold an open file reference 78 | * matching the specified path or volume. Pass NULL to 79 | * obtain the minimum buffer size needed to hold the 80 | * currently active processes. 81 | * @param buffersize the size (in bytes) of the provided buffer. 82 | * @result the number of bytes of data returned in the provided buffer; 83 | * -1 if an error was encountered; 84 | */ 85 | int proc_listpidspath(uint32_t type, 86 | uint32_t typeinfo, 87 | const char *path, 88 | uint32_t pathflags, 89 | void *buffer, 90 | int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); 91 | 92 | int proc_listpids(uint32_t type, uint32_t typeinfo, void *buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); 93 | int proc_listallpids(void * buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_1); 94 | int proc_listpgrppids(pid_t pgrpid, void * buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_1); 95 | int proc_listchildpids(pid_t ppid, void * buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_1); 96 | int proc_pidinfo(int pid, int flavor, uint64_t arg, void *buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); 97 | int proc_pidfdinfo(int pid, int fd, int flavor, void * buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); 98 | int proc_pidfileportinfo(int pid, uint32_t fileport, int flavor, void *buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3); 99 | int proc_name(int pid, void * buffer, uint32_t buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); 100 | int proc_regionfilename(int pid, uint64_t address, void * buffer, uint32_t buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); 101 | int proc_kmsgbuf(void * buffer, uint32_t buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); 102 | int proc_pidpath(int pid, void * buffer, uint32_t buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); 103 | int proc_pidpath_audittoken(audit_token_t *audittoken, void * buffer, uint32_t buffersize) API_AVAILABLE(macos(10.16), ios(14.0), watchos(7.0), tvos(14.0)); 104 | int proc_libversion(int *major, int * minor) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); 105 | 106 | /* 107 | * Return resource usage information for the given pid, which can be a live process or a zombie. 108 | * 109 | * Returns 0 on success; or -1 on failure, with errno set to indicate the specific error. 110 | */ 111 | int proc_pid_rusage(int pid, int flavor, rusage_info_t *buffer) __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0); 112 | 113 | /* 114 | * A process can use the following api to set its own process control 115 | * state on resoure starvation. The argument can have one of the PROC_SETPC_XX values 116 | */ 117 | #define PROC_SETPC_NONE 0 118 | #define PROC_SETPC_THROTTLEMEM 1 119 | #define PROC_SETPC_SUSPEND 2 120 | #define PROC_SETPC_TERMINATE 3 121 | 122 | int proc_setpcontrol(const int control) __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2); 123 | int proc_setpcontrol(const int control); 124 | 125 | int proc_track_dirty(pid_t pid, uint32_t flags); 126 | int proc_set_dirty(pid_t pid, bool dirty); 127 | int proc_get_dirty(pid_t pid, uint32_t *flags); 128 | int proc_clear_dirty(pid_t pid, uint32_t flags); 129 | 130 | int proc_terminate(pid_t pid, int *sig); 131 | 132 | /* 133 | * NO_SMT means that on an SMT CPU, this thread must be scheduled alone, 134 | * with the paired CPU idle. 135 | * 136 | * Set NO_SMT on the current proc (all existing and future threads) 137 | * This attribute is inherited on fork and exec 138 | */ 139 | int proc_set_no_smt(void) __API_AVAILABLE(macos(10.16)); 140 | 141 | /* Set NO_SMT on the current thread */ 142 | int proc_setthread_no_smt(void) __API_AVAILABLE(macos(10.16)); 143 | 144 | /* 145 | * CPU Security Mitigation APIs 146 | * 147 | * Set CPU security mitigation on the current proc (all existing and future threads) 148 | * This attribute is inherited on fork and exec 149 | */ 150 | int proc_set_csm(uint32_t flags) __API_AVAILABLE(macos(10.16)); 151 | 152 | /* Set CPU security mitigation on the current thread */ 153 | int proc_setthread_csm(uint32_t flags) __API_AVAILABLE(macos(10.16)); 154 | 155 | /* 156 | * flags for CPU Security Mitigation APIs 157 | * PROC_CSM_ALL should be used in most cases, 158 | * the individual flags are provided only for performance evaluation etc 159 | */ 160 | #define PROC_CSM_ALL 0x0001 /* Set all available mitigations */ 161 | #define PROC_CSM_NOSMT 0x0002 /* Set NO_SMT - see above */ 162 | #define PROC_CSM_TECS 0x0004 /* Execute VERW on every return to user mode */ 163 | 164 | #ifdef PRIVATE 165 | #include 166 | /* 167 | * Enumerate potential userspace pointers embedded in kernel data structures. 168 | * Currently inspects kqueues only. 169 | * 170 | * NOTE: returned "pointers" are opaque user-supplied values and thus not 171 | * guaranteed to address valid objects or be pointers at all. 172 | * 173 | * Returns the number of pointers found (which may exceed buffersize), or -1 on 174 | * failure and errno set appropriately. 175 | */ 176 | int proc_list_uptrs(pid_t pid, uint64_t *buffer, uint32_t buffersize); 177 | 178 | int proc_list_dynkqueueids(int pid, kqueue_id_t *buf, uint32_t bufsz); 179 | int proc_piddynkqueueinfo(int pid, int flavor, kqueue_id_t kq_id, void *buffer, 180 | int buffersize); 181 | #endif /* PRIVATE */ 182 | 183 | int proc_udata_info(int pid, int flavor, void *buffer, int buffersize); 184 | 185 | __END_DECLS 186 | 187 | #endif /*_LIBPROC_H_ */ 188 | -------------------------------------------------------------------------------- /src/helpers/ts/TSEventFetcher.h: -------------------------------------------------------------------------------- 1 | // 2 | // TSEventFetcher.h 3 | // TrollSpeed 4 | // 5 | // Created by Lessica on 2024/1/24. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface TSEventFetcher : NSObject 13 | + (NSInteger)receiveAXEventID:(NSInteger)pointId 14 | atGlobalCoordinate:(CGPoint)point 15 | withTouchPhase:(UITouchPhase)phase 16 | inWindow:(UIWindow *)window 17 | onView:(UIView *)view; 18 | @end 19 | 20 | NS_ASSUME_NONNULL_END -------------------------------------------------------------------------------- /src/helpers/ts/TSEventFetcher.mm: -------------------------------------------------------------------------------- 1 | // 2 | // TSEventFetcher.mm 3 | // TrollSpeed 4 | // 5 | // Created by Lessica on 2024/1/24. 6 | // 7 | 8 | #import 9 | #import 10 | #import 11 | #import "TSEventFetcher.h" 12 | #import "../private_headers/UIEvent+Private.h" 13 | #import "../private_headers/UITouch-KIFAdditions.h" 14 | #import "../private_headers/UIApplication+Private.h" 15 | 16 | static NSArray *_safeTouchAry = nil; 17 | static NSMutableArray *_touchAry = nil; 18 | static NSMutableArray *_livingTouchAry = nil; 19 | static CFRunLoopSourceRef _source = NULL; 20 | 21 | static UITouch *toRemove = nil; 22 | static UITouch *toStationarify = nil; 23 | 24 | static void __TSEventFetcherCallback(void *info) 25 | { 26 | static UIApplication *app = [UIApplication sharedApplication]; 27 | UIEvent *event = [app _touchesEvent]; 28 | 29 | // to retain objects from being released 30 | [event _clearTouches]; 31 | 32 | NSArray *myAry = _safeTouchAry; 33 | for (UITouch *aTouch in myAry) 34 | { 35 | switch (aTouch.phase) { 36 | case UITouchPhaseEnded: 37 | case UITouchPhaseCancelled: 38 | toRemove = aTouch; 39 | break; 40 | case UITouchPhaseBegan: 41 | toStationarify = aTouch; 42 | break; 43 | default: 44 | break; 45 | } 46 | 47 | [event _addTouch:aTouch forDelayedDelivery:NO]; 48 | } 49 | 50 | [app sendEvent:event]; 51 | } 52 | 53 | @implementation TSEventFetcher 54 | 55 | + (void)load 56 | { 57 | _livingTouchAry = [[NSMutableArray alloc] init]; 58 | _touchAry = [[NSMutableArray alloc] init]; 59 | 60 | for (NSInteger i = 0; i < 100; i++) 61 | { 62 | UITouch *touch = [[UITouch alloc] initTouch]; 63 | [touch setPhaseAndUpdateTimestamp:UITouchPhaseEnded]; 64 | [_touchAry addObject:touch]; 65 | } 66 | 67 | CFRunLoopSourceContext context; 68 | memset(&context, 0, sizeof(CFRunLoopSourceContext)); 69 | context.perform = __TSEventFetcherCallback; 70 | 71 | // content of context is copied 72 | _source = CFRunLoopSourceCreate(kCFAllocatorDefault, -2, &context); 73 | CFRunLoopRef loop = CFRunLoopGetMain(); 74 | CFRunLoopAddSource(loop, _source, kCFRunLoopCommonModes); 75 | } 76 | 77 | + (NSInteger)receiveAXEventID:(NSInteger)eventId 78 | atGlobalCoordinate:(CGPoint)coordinate 79 | withTouchPhase:(UITouchPhase)phase 80 | inWindow:(UIWindow *)window 81 | onView:(UIView *)view 82 | { 83 | BOOL deleted = NO; 84 | UITouch *touch = nil; 85 | BOOL needsCopy = NO; 86 | 87 | if (toRemove != nil) 88 | { 89 | touch = toRemove; 90 | toRemove = nil; 91 | [_livingTouchAry removeObjectIdenticalTo:touch]; 92 | deleted = YES; 93 | needsCopy = YES; 94 | } 95 | 96 | if (toStationarify != nil) 97 | { 98 | // in case this is changed during the operations 99 | touch = toStationarify; 100 | toStationarify = nil; 101 | if (touch.phase == UITouchPhaseBegan) 102 | [touch setPhaseAndUpdateTimestamp:UITouchPhaseStationary]; 103 | } 104 | 105 | eventId -= 1; 106 | 107 | // ideally should be phase began when this hit 108 | // but if by any means other phases come... well lets be forgiving 109 | touch = _touchAry[eventId]; 110 | BOOL oldState = [_livingTouchAry containsObject:touch]; 111 | BOOL newState = !oldState; 112 | if (newState) 113 | { 114 | if (phase == UITouchPhaseEnded || phase == UITouchPhaseCancelled) 115 | return deleted; 116 | touch = [[UITouch alloc] initAtPoint:coordinate inWindow:window onView:view]; 117 | [_livingTouchAry addObject:touch]; 118 | [_touchAry setObject:touch atIndexedSubscript:eventId]; 119 | needsCopy = YES; 120 | } 121 | else 122 | { 123 | if (touch.phase == UITouchPhaseBegan && phase == UITouchPhaseMoved) 124 | return deleted; 125 | [touch setLocationInWindow:coordinate]; 126 | } 127 | 128 | [touch setPhaseAndUpdateTimestamp:phase]; 129 | 130 | if (needsCopy) 131 | { 132 | CFTypeRef delayRelease = CFBridgingRetain(_safeTouchAry); 133 | _safeTouchAry = [[NSArray alloc] initWithArray:_livingTouchAry copyItems:NO]; 134 | CFBridgingRelease(delayRelease); 135 | } 136 | 137 | CFRunLoopSourceSignal(_source); 138 | return deleted; 139 | } 140 | 141 | @end -------------------------------------------------------------------------------- /src/helpers/ts/pac_helper.h: -------------------------------------------------------------------------------- 1 | #ifndef PTRAUTH_HELPERS_H 2 | #define PTRAUTH_HELPERS_H 3 | 4 | // Helpers for PAC archs. 5 | 6 | // If the compiler understands __arm64e__, assume it's paired with an SDK that has 7 | // ptrauth.h. Otherwise, it'll probably error if we try to include it so don't. 8 | #if __arm64e__ 9 | #include 10 | #endif 11 | 12 | #pragma clang diagnostic push 13 | #pragma clang diagnostic ignored "-Wunused-function" 14 | 15 | // Given a pointer to instructions, sign it so you can call it like a normal fptr. 16 | static void *make_sym_callable(void *ptr) { 17 | #if __arm64e__ 18 | if (!ptr) return ptr; 19 | ptr = ptrauth_sign_unauthenticated(ptrauth_strip(ptr, ptrauth_key_function_pointer), ptrauth_key_function_pointer, 0); 20 | #endif 21 | return ptr; 22 | } 23 | 24 | // Given a function pointer, strip the PAC so you can read the instructions. 25 | static void *make_sym_readable(void *ptr) { 26 | #if __arm64e__ 27 | if (!ptr) return ptr; 28 | ptr = ptrauth_strip(ptr, ptrauth_key_function_pointer); 29 | #endif 30 | return ptr; 31 | } 32 | 33 | #pragma clang diagnostic pop 34 | #endif // PTRAUTH_HELPERS_H -------------------------------------------------------------------------------- /src/hud/AnyBackdropView.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | NS_ASSUME_NONNULL_BEGIN 4 | 5 | @interface AnyBackdropView : UIView 6 | @end 7 | 8 | NS_ASSUME_NONNULL_END -------------------------------------------------------------------------------- /src/hud/AnyBackdropView.mm: -------------------------------------------------------------------------------- 1 | #import "AnyBackdropView.h" 2 | #import "../helpers/private_headers/CAFilter.h" 3 | 4 | @implementation AnyBackdropView 5 | 6 | + (Class)layerClass { 7 | return [NSClassFromString(@"CABackdropLayer") class]; 8 | } 9 | 10 | - (instancetype)initWithCoder:(NSCoder *)coder 11 | { 12 | self = [super initWithCoder:coder]; 13 | [self _updateFilters]; 14 | return self; 15 | } 16 | 17 | - (instancetype)initWithFrame:(CGRect)frame 18 | { 19 | self = [super initWithFrame:frame]; 20 | [self _updateFilters]; 21 | return self; 22 | } 23 | 24 | - (void)_updateFilters { 25 | // code from Lessica/TrollSpeed 26 | CAFilter *blurFilter = [CAFilter filterWithName:kCAFilterGaussianBlur]; 27 | [blurFilter setValue:@(50.0) forKey:@"inputRadius"]; // radius 50pt 28 | [blurFilter setValue:@YES forKey:@"inputNormalizeEdges"]; // do not use inputHardEdges 29 | 30 | CAFilter *brightnessFilter = [CAFilter filterWithName:kCAFilterColorBrightness]; 31 | [brightnessFilter setValue:@(-0.285) forKey:@"inputAmount"]; // -28.5% 32 | 33 | CAFilter *contrastFilter = [CAFilter filterWithName:kCAFilterColorContrast]; 34 | [contrastFilter setValue:@(1000.0) forKey:@"inputAmount"]; // 1000x 35 | 36 | CAFilter *saturateFilter = [CAFilter filterWithName:kCAFilterColorSaturate]; 37 | [saturateFilter setValue:@(0.0) forKey:@"inputAmount"]; 38 | 39 | CAFilter *colorInvertFilter = [CAFilter filterWithName:kCAFilterColorInvert]; 40 | 41 | [self.layer setFilters:@[ 42 | blurFilter, brightnessFilter, contrastFilter, 43 | saturateFilter, colorInvertFilter, 44 | ]]; 45 | } 46 | 47 | @end -------------------------------------------------------------------------------- /src/hud/HUDApp.mm: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | 5 | #import "HUDHelper.h" 6 | 7 | #import "../helpers/ts/TSEventFetcher.h" 8 | #import "../helpers/private_headers/AXEventRepresentation.h" 9 | #import "../helpers/private_headers/BackboardServices.h" 10 | #import "../helpers/private_headers/UIApplication+Private.h" 11 | 12 | static __used 13 | void _HUDEventCallback(void *target, void *refcon, IOHIDServiceRef service, IOHIDEventRef event) 14 | { 15 | static UIApplication *app = [UIApplication sharedApplication]; 16 | #if DEBUG 17 | os_log_debug(OS_LOG_DEFAULT, "_HUDEventCallback => %{public}@", event); 18 | #endif 19 | 20 | // iOS 15.1+ has a new API for handling HID events. 21 | if (@available(iOS 15.1, *)) {} 22 | else { 23 | [app _enqueueHIDEvent:event]; 24 | } 25 | 26 | BOOL shouldUseAXEvent = YES; // Always use AX events now... 27 | 28 | BOOL isExactly15 = NO; 29 | static NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion]; 30 | if (version.majorVersion == 15 && version.minorVersion == 0 && version.patchVersion == 0) { 31 | isExactly15 = YES; 32 | } 33 | 34 | if (@available(iOS 15.0, *)) { 35 | shouldUseAXEvent = !isExactly15; 36 | } else { 37 | shouldUseAXEvent = NO; 38 | } 39 | 40 | if (shouldUseAXEvent) 41 | { 42 | static Class AXEventRepresentationCls = nil; 43 | static dispatch_once_t onceToken; 44 | dispatch_once(&onceToken, ^{ 45 | [[NSBundle bundleWithPath:@"/System/Library/PrivateFrameworks/AccessibilityUtilities.framework"] load]; 46 | AXEventRepresentationCls = objc_getClass("AXEventRepresentation"); 47 | }); 48 | 49 | AXEventRepresentation *rep = [AXEventRepresentationCls representationWithHIDEvent:event hidStreamIdentifier:@"UIApplicationEvents"]; 50 | #if DEBUG 51 | os_log_debug(OS_LOG_DEFAULT, "_HUDEventCallback => %{public}@", rep.handInfo); 52 | #endif 53 | 54 | /* I don't like this. It's too hacky, but it works. */ 55 | { 56 | dispatch_async(dispatch_get_main_queue(), ^(void) { 57 | static UIWindow *keyWindow = nil; 58 | static dispatch_once_t onceToken; 59 | dispatch_once(&onceToken, ^{ 60 | keyWindow = [[app windows] firstObject]; 61 | }); 62 | 63 | UIView *keyView = [keyWindow hitTest:[rep location] withEvent:nil]; 64 | 65 | UITouchPhase phase = UITouchPhaseEnded; 66 | if ([rep isTouchDown]) 67 | phase = UITouchPhaseBegan; 68 | else if ([rep isMove]) 69 | phase = UITouchPhaseMoved; 70 | else if ([rep isCancel]) 71 | phase = UITouchPhaseCancelled; 72 | else if ([rep isLift] || [rep isInRange] || [rep isInRangeLift]) 73 | phase = UITouchPhaseEnded; 74 | 75 | NSInteger pointerId = [[[[rep handInfo] paths] firstObject] pathIdentity]; 76 | if (pointerId > 0) 77 | [TSEventFetcher receiveAXEventID:MIN(MAX(pointerId, 1), 98) atGlobalCoordinate:[rep location] withTouchPhase:phase inWindow:keyWindow onView:keyView]; 78 | }); 79 | } 80 | } 81 | } 82 | 83 | 84 | #pragma mark - 85 | 86 | static NSString *_cachesDirectoryPath = nil; 87 | static NSString *_hudPIDFilePath = nil; 88 | static NSString *GetPIDFilePath(void) 89 | { 90 | static dispatch_once_t onceToken; 91 | dispatch_once(&onceToken, ^{ 92 | _cachesDirectoryPath = 93 | [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject]; 94 | _hudPIDFilePath = [_cachesDirectoryPath stringByAppendingPathComponent:@"hud.pid"]; 95 | }); 96 | return _hudPIDFilePath; 97 | } 98 | 99 | #pragma mark - 100 | 101 | int main(int argc, char *argv[]) 102 | { 103 | @autoreleasepool 104 | { 105 | #if DEBUG 106 | os_log_debug(OS_LOG_DEFAULT, "launched argc %{public}d, argv[1] %{public}s", argc, argc > 1 ? argv[1] : "NULL"); 107 | #endif 108 | 109 | if (argc <= 1) 110 | return UIApplicationMain(argc, argv, @"MainApplication", @"MainApplicationDelegate"); 111 | 112 | if (strcmp(argv[1], "-hud") == 0) 113 | { 114 | pid_t pid = getpid(); 115 | pid_t pgid = getgid(); 116 | (void)pgid; 117 | #if DEBUG 118 | os_log_debug(OS_LOG_DEFAULT, "HUD pid %d, pgid %d", pid, pgid); 119 | #endif 120 | NSString *pidString = [NSString stringWithFormat:@"%d", pid]; 121 | [pidString writeToFile:GetPIDFilePath() 122 | atomically:YES 123 | encoding:NSUTF8StringEncoding 124 | error:nil]; 125 | 126 | [UIScreen initialize]; 127 | CFRunLoopGetCurrent(); 128 | 129 | GSInitialize(); 130 | BKSDisplayServicesStart(); 131 | UIApplicationInitialize(); 132 | 133 | UIApplicationInstantiateSingleton(objc_getClass("HUDMainApplication")); 134 | static id appDelegate = [[objc_getClass("HUDMainApplicationDelegate") alloc] init]; 135 | [UIApplication.sharedApplication setDelegate:appDelegate]; 136 | [UIApplication.sharedApplication _accessibilityInit]; 137 | 138 | [NSRunLoop currentRunLoop]; 139 | BKSHIDEventRegisterEventCallback(_HUDEventCallback); 140 | 141 | if (@available(iOS 15.0, *)) { 142 | GSEventInitialize(0); 143 | GSEventPushRunLoopMode(kCFRunLoopDefaultMode); 144 | } 145 | 146 | [UIApplication.sharedApplication __completeAndRunAsPlugin]; 147 | 148 | static int _springboardBootToken; 149 | notify_register_dispatch("SBSpringBoardDidLaunchNotification", &_springboardBootToken, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0l), ^(int token) { 150 | notify_cancel(token); 151 | 152 | // Re-enable HUD after SpringBoard is launched. 153 | SetHUDEnabled(YES); 154 | 155 | // Exit the current instance of HUD. 156 | #ifdef NOTIFY_DISMISSAL_HUD 157 | notify_post(NOTIFY_DISMISSAL_HUD); 158 | #endif 159 | kill(pid, SIGKILL); 160 | }); 161 | 162 | CFRunLoopRun(); 163 | return EXIT_SUCCESS; 164 | } 165 | else if (strcmp(argv[1], "-exit") == 0) 166 | { 167 | NSString *pidString = [NSString stringWithContentsOfFile:GetPIDFilePath() 168 | encoding:NSUTF8StringEncoding 169 | error:nil]; 170 | 171 | if (pidString) 172 | { 173 | pid_t pid = (pid_t)[pidString intValue]; 174 | kill(pid, SIGKILL); 175 | unlink(GetPIDFilePath().UTF8String); 176 | } 177 | 178 | return EXIT_SUCCESS; 179 | } 180 | else if (strcmp(argv[1], "-check") == 0) 181 | { 182 | NSString *pidString = [NSString stringWithContentsOfFile:GetPIDFilePath() 183 | encoding:NSUTF8StringEncoding 184 | error:nil]; 185 | 186 | if (pidString) 187 | { 188 | pid_t pid = (pid_t)[pidString intValue]; 189 | int killed = kill(pid, 0); 190 | return (killed == 0 ? EXIT_FAILURE : EXIT_SUCCESS); 191 | } 192 | else return EXIT_SUCCESS; // No PID file, so HUD is not running 193 | } 194 | } 195 | } -------------------------------------------------------------------------------- /src/hud/HUDHelper.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | NS_ASSUME_NONNULL_BEGIN 4 | 5 | OBJC_EXTERN BOOL IsHUDEnabled(void); 6 | OBJC_EXTERN void SetHUDEnabled(BOOL isEnabled); 7 | OBJC_EXTERN void waitForNotification(void (^onFinish)(), BOOL isEnabled); 8 | 9 | NS_ASSUME_NONNULL_END -------------------------------------------------------------------------------- /src/hud/HUDHelper.mm: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | 5 | #import "HUDHelper.h" 6 | 7 | extern "C" char **environ; 8 | 9 | #define POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE 1 10 | extern "C" int posix_spawnattr_set_persona_np(const posix_spawnattr_t* __restrict, uid_t, uint32_t); 11 | extern "C" int posix_spawnattr_set_persona_uid_np(const posix_spawnattr_t* __restrict, uid_t); 12 | extern "C" int posix_spawnattr_set_persona_gid_np(const posix_spawnattr_t* __restrict, uid_t); 13 | 14 | BOOL IsHUDEnabled(void) 15 | { 16 | static char *executablePath = NULL; 17 | uint32_t executablePathSize = 0; 18 | _NSGetExecutablePath(NULL, &executablePathSize); 19 | executablePath = (char *)calloc(1, executablePathSize); 20 | _NSGetExecutablePath(executablePath, &executablePathSize); 21 | 22 | posix_spawnattr_t attr; 23 | posix_spawnattr_init(&attr); 24 | 25 | posix_spawnattr_set_persona_np(&attr, 99, POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE); 26 | posix_spawnattr_set_persona_uid_np(&attr, 0); 27 | posix_spawnattr_set_persona_gid_np(&attr, 0); 28 | 29 | pid_t task_pid; 30 | const char *args[] = { executablePath, "-check", NULL }; 31 | posix_spawn(&task_pid, executablePath, NULL, &attr, (char **)args, environ); 32 | posix_spawnattr_destroy(&attr); 33 | 34 | #if DEBUG 35 | os_log_debug(OS_LOG_DEFAULT, "spawned %{public}s -check pid = %{public}d", executablePath, task_pid); 36 | #endif 37 | 38 | int status; 39 | do { 40 | if (waitpid(task_pid, &status, 0) != -1) 41 | { 42 | #if DEBUG 43 | os_log_debug(OS_LOG_DEFAULT, "child status %d", WEXITSTATUS(status)); 44 | #endif 45 | } 46 | } while (!WIFEXITED(status) && !WIFSIGNALED(status)); 47 | 48 | return WEXITSTATUS(status) != 0; 49 | } 50 | 51 | void SetHUDEnabled(BOOL isEnabled) 52 | { 53 | #ifdef NOTIFY_DISMISSAL_HUD 54 | notify_post(NOTIFY_DISMISSAL_HUD); 55 | #endif 56 | 57 | static char *executablePath = NULL; 58 | uint32_t executablePathSize = 0; 59 | _NSGetExecutablePath(NULL, &executablePathSize); 60 | executablePath = (char *)calloc(1, executablePathSize); 61 | _NSGetExecutablePath(executablePath, &executablePathSize); 62 | 63 | posix_spawnattr_t attr; 64 | posix_spawnattr_init(&attr); 65 | 66 | posix_spawnattr_set_persona_np(&attr, 99, POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE); 67 | posix_spawnattr_set_persona_uid_np(&attr, 0); 68 | posix_spawnattr_set_persona_gid_np(&attr, 0); 69 | 70 | if (isEnabled) 71 | { 72 | posix_spawnattr_setpgroup(&attr, 0); 73 | posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETPGROUP); 74 | 75 | pid_t task_pid; 76 | const char *args[] = { executablePath, "-hud", NULL }; 77 | posix_spawn(&task_pid, executablePath, NULL, &attr, (char **)args, environ); 78 | posix_spawnattr_destroy(&attr); 79 | 80 | #if DEBUG 81 | os_log_debug(OS_LOG_DEFAULT, "spawned %{public}s -hud pid = %{public}d", executablePath, task_pid); 82 | #endif 83 | } 84 | else 85 | { 86 | [NSThread sleepForTimeInterval:0.25]; 87 | 88 | pid_t task_pid; 89 | const char *args[] = { executablePath, "-exit", NULL }; 90 | posix_spawn(&task_pid, executablePath, NULL, &attr, (char **)args, environ); 91 | posix_spawnattr_destroy(&attr); 92 | 93 | #if DEBUG 94 | os_log_debug(OS_LOG_DEFAULT, "spawned %{public}s -exit pid = %{public}d", executablePath, task_pid); 95 | #endif 96 | 97 | int status; 98 | do { 99 | if (waitpid(task_pid, &status, 0) != -1) 100 | { 101 | #if DEBUG 102 | os_log_debug(OS_LOG_DEFAULT, "child status %d", WEXITSTATUS(status)); 103 | #endif 104 | } 105 | } while (!WIFEXITED(status) && !WIFSIGNALED(status)); 106 | } 107 | } 108 | 109 | void waitForNotification(void (^onFinish)(), BOOL isEnabled) { 110 | if (isEnabled) 111 | { 112 | dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); 113 | 114 | int token; 115 | notify_register_dispatch(NOTIFY_LAUNCHED_HUD, &token, dispatch_get_main_queue(), ^(int token) { 116 | notify_cancel(token); 117 | dispatch_semaphore_signal(semaphore); 118 | }); 119 | 120 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{ 121 | int timedOut = dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC))); 122 | dispatch_async(dispatch_get_main_queue(), ^{ 123 | if (timedOut) 124 | os_log_error(OS_LOG_DEFAULT, "Timed out waiting for HUD to launch"); 125 | 126 | onFinish(); 127 | }); 128 | }); 129 | } 130 | else 131 | { 132 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 133 | onFinish(); 134 | }); 135 | } 136 | } -------------------------------------------------------------------------------- /src/hud/HUDMainApplication.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | NS_ASSUME_NONNULL_BEGIN 4 | 5 | @interface HUDMainApplication : UIApplication 6 | @end 7 | 8 | NS_ASSUME_NONNULL_END -------------------------------------------------------------------------------- /src/hud/HUDMainApplication.mm: -------------------------------------------------------------------------------- 1 | // 2 | // HUDMainApplication.m 3 | // 4 | // 5 | // Created by lemin on 10/5/23. 6 | // 7 | 8 | #import 9 | #import 10 | #import 11 | #import 12 | #import 13 | 14 | #import "HUDMainApplication.h" 15 | 16 | #import "../helpers/private_headers/UIEventDispatcher.h" 17 | #import "../helpers/private_headers/UIEventFetcher.h" 18 | #import "../helpers/private_headers/UIApplication+Private.h" 19 | 20 | #import "../helpers/ts/pac_helper.h" 21 | 22 | static void DumpThreads(void) 23 | { 24 | char name[256]; 25 | mach_msg_type_number_t count; 26 | thread_act_array_t list; 27 | task_threads(mach_task_self(), &list, &count); 28 | for (int i = 0; i < count; ++i) 29 | { 30 | pthread_t pt = pthread_from_mach_thread_np(list[i]); 31 | if (pt) 32 | { 33 | name[0] = '\0'; 34 | #if DEBUG 35 | __unused int rc = pthread_getname_np(pt, name, sizeof name); 36 | os_log_debug(OS_LOG_DEFAULT, "mach thread %u: getname returned %d: %{public}s", list[i], rc, name); 37 | #endif 38 | } 39 | else 40 | { 41 | #if DEBUG 42 | os_log_debug(OS_LOG_DEFAULT, "mach thread %u: no pthread found", list[i]); 43 | #endif 44 | } 45 | } 46 | } 47 | 48 | @implementation HUDMainApplication 49 | 50 | - (instancetype)init 51 | { 52 | if (self = [super init]) 53 | { 54 | #if DEBUG 55 | os_log_debug(OS_LOG_DEFAULT, "- [HUDMainApplication init]"); 56 | #endif 57 | notify_post(NOTIFY_LAUNCHED_HUD); 58 | 59 | #ifdef NOTIFY_DISMISSAL_HUD 60 | { 61 | int token; 62 | notify_register_dispatch(NOTIFY_DISMISSAL_HUD, &token, dispatch_get_main_queue(), ^(int token) { 63 | notify_cancel(token); 64 | 65 | // Fade out the HUD window 66 | [UIView animateWithDuration:0.25f animations:^{ 67 | [[self.windows firstObject] setAlpha:0.0]; 68 | } completion:^(BOOL finished) { 69 | // Terminate the HUD app 70 | [self terminateWithSuccess]; 71 | }]; 72 | }); 73 | } 74 | #endif 75 | do { 76 | UIEventDispatcher *dispatcher = (UIEventDispatcher *)[self valueForKey:@"eventDispatcher"]; 77 | if (!dispatcher) 78 | { 79 | #if DEBUG 80 | os_log_error(OS_LOG_DEFAULT, "failed to get ivar _eventDispatcher"); 81 | #endif 82 | break; 83 | } 84 | 85 | #if DEBUG 86 | os_log_debug(OS_LOG_DEFAULT, "got ivar _eventDispatcher: %p", dispatcher); 87 | #endif 88 | 89 | if ([dispatcher respondsToSelector:@selector(_installEventRunLoopSources:)]) 90 | { 91 | CFRunLoopRef mainRunLoop = CFRunLoopGetMain(); 92 | [dispatcher _installEventRunLoopSources:mainRunLoop]; 93 | } 94 | else 95 | { 96 | IMP runMethodIMP = class_getMethodImplementation([self class], @selector(_run)); 97 | if (!runMethodIMP) 98 | { 99 | #if DEBUG 100 | os_log_error(OS_LOG_DEFAULT, "failed to get - [UIApplication _run] method"); 101 | #endif 102 | break; 103 | } 104 | 105 | uint32_t *runMethodPtr = (uint32_t *)make_sym_readable((void *)runMethodIMP); 106 | #if DEBUG 107 | os_log_debug(OS_LOG_DEFAULT, "- [UIApplication _run]: %p", runMethodPtr); 108 | #endif 109 | 110 | void (*orig_UIEventDispatcher__installEventRunLoopSources_)(id _Nonnull, SEL _Nonnull, CFRunLoopRef) = NULL; 111 | for (int i = 0; i < 0x140; i++) 112 | { 113 | // mov x2, x0 114 | // mov x0, x? 115 | if (runMethodPtr[i] != 0xaa0003e2 || (runMethodPtr[i + 1] & 0xff000000) != 0xaa000000) 116 | continue; 117 | 118 | // bl -[UIEventDispatcher _installEventRunLoopSources:] 119 | uint32_t blInst = runMethodPtr[i + 2]; 120 | uint32_t *blInstPtr = &runMethodPtr[i + 2]; 121 | if ((blInst & 0xfc000000) != 0x94000000) 122 | { 123 | #if DEBUG 124 | os_log_error(OS_LOG_DEFAULT, "not a BL instruction: 0x%x, address %p", blInst, blInstPtr); 125 | #endif 126 | continue; 127 | } 128 | 129 | #if DEBUG 130 | os_log_debug(OS_LOG_DEFAULT, "found BL instruction: 0x%x, address %p", blInst, blInstPtr); 131 | #endif 132 | 133 | int32_t blOffset = blInst & 0x03ffffff; 134 | if (blOffset & 0x02000000) 135 | blOffset |= 0xfc000000; 136 | blOffset <<= 2; 137 | 138 | #if DEBUG 139 | os_log_debug(OS_LOG_DEFAULT, "BL offset: 0x%x", blOffset); 140 | #endif 141 | 142 | uint64_t blAddr = (uint64_t)blInstPtr + blOffset; 143 | 144 | #if DEBUG 145 | os_log_debug(OS_LOG_DEFAULT, "BL target address: %p", (void *)blAddr); 146 | #endif 147 | 148 | // cbz x0, loc_????????? 149 | uint32_t cbzInst = *((uint32_t *)make_sym_readable((void *)blAddr)); 150 | if ((cbzInst & 0xff000000) != 0xb4000000) 151 | { 152 | #if DEBUG 153 | os_log_error(OS_LOG_DEFAULT, "not a CBZ instruction: 0x%x", cbzInst); 154 | #endif 155 | continue; 156 | } 157 | 158 | #if DEBUG 159 | os_log_debug(OS_LOG_DEFAULT, "found CBZ instruction: 0x%x, address %p", cbzInst, (void *)blAddr); 160 | #endif 161 | 162 | orig_UIEventDispatcher__installEventRunLoopSources_ = (void (*)(id _Nonnull __strong, SEL _Nonnull, CFRunLoopRef))make_sym_callable((void *)blAddr); 163 | } 164 | 165 | if (!orig_UIEventDispatcher__installEventRunLoopSources_) 166 | { 167 | #if DEBUG 168 | os_log_error(OS_LOG_DEFAULT, "failed to find -[UIEventDispatcher _installEventRunLoopSources:]"); 169 | #endif 170 | break; 171 | } 172 | 173 | #if DEBUG 174 | os_log_debug(OS_LOG_DEFAULT, "- [UIEventDispatcher _installEventRunLoopSources:]: %p", orig_UIEventDispatcher__installEventRunLoopSources_); 175 | #endif 176 | 177 | CFRunLoopRef mainRunLoop = CFRunLoopGetMain(); 178 | orig_UIEventDispatcher__installEventRunLoopSources_(dispatcher, @selector(_installEventRunLoopSources:), mainRunLoop); 179 | } 180 | 181 | #if DEBUG 182 | // Get image base with dyld, the image is /System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore. 183 | uint64_t imageUIKitCore = 0; 184 | { 185 | uint32_t imageCount = _dyld_image_count(); 186 | for (uint32_t i = 0; i < imageCount; i++) 187 | { 188 | const char *imageName = _dyld_get_image_name(i); 189 | if (imageName && !strcmp(imageName, "/System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore")) 190 | { 191 | imageUIKitCore = _dyld_get_image_vmaddr_slide(i); 192 | break; 193 | } 194 | } 195 | } 196 | 197 | os_log_debug(OS_LOG_DEFAULT, "UIKitCore: %p", (void *)imageUIKitCore); 198 | #endif 199 | 200 | UIEventFetcher *fetcher = [[objc_getClass("UIEventFetcher") alloc] init]; 201 | [dispatcher setValue:fetcher forKey:@"eventFetcher"]; 202 | 203 | if ([fetcher respondsToSelector:@selector(setEventFetcherSink:)]) { 204 | [fetcher setEventFetcherSink:dispatcher]; 205 | } 206 | else 207 | { 208 | /* Tested on iOS 15.1.1 and below */ 209 | [fetcher setValue:dispatcher forKey:@"eventFetcherSink"]; 210 | 211 | /* Print NSThread names */ 212 | DumpThreads(); 213 | 214 | #if DEBUG 215 | /* Force HIDTransformer to print logs */ 216 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogTouch" inDomain:@"com.apple.UIKit"]; 217 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogGesture" inDomain:@"com.apple.UIKit"]; 218 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogEventDispatch" inDomain:@"com.apple.UIKit"]; 219 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogGestureEnvironment" inDomain:@"com.apple.UIKit"]; 220 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogGestureExclusion" inDomain:@"com.apple.UIKit"]; 221 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogSystemGestureUpdate" inDomain:@"com.apple.UIKit"]; 222 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogGesturePerformance" inDomain:@"com.apple.UIKit"]; 223 | [[NSUserDefaults standardUserDefaults] setObject:@YES forKey:@"LogHIDTransformer" inDomain:@"com.apple.UIKit"]; 224 | [[NSUserDefaults standardUserDefaults] synchronize]; 225 | #endif 226 | } 227 | 228 | [self setValue:fetcher forKey:@"eventFetcher"]; 229 | } while (NO); 230 | } 231 | return self; 232 | } 233 | 234 | @end -------------------------------------------------------------------------------- /src/hud/HUDMainApplicationDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | NS_ASSUME_NONNULL_BEGIN 4 | 5 | @interface HUDMainApplicationDelegate : UIResponder 6 | @property (nonatomic, strong) UIWindow *window; 7 | @end 8 | 9 | NS_ASSUME_NONNULL_END -------------------------------------------------------------------------------- /src/hud/HUDMainApplicationDelegate.mm: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "HUDMainApplicationDelegate.h" 4 | #import "HUDMainWindow.h" 5 | #import "HUDRootViewController.h" 6 | 7 | #import "../helpers/private_headers/SBSAccessibilityWindowHostingController.h" 8 | #import "../helpers/private_headers/UIWindow+Private.h" 9 | 10 | @implementation HUDMainApplicationDelegate { 11 | HUDRootViewController *_rootViewController; 12 | SBSAccessibilityWindowHostingController *_windowHostingController; 13 | } 14 | 15 | - (instancetype)init 16 | { 17 | if (self = [super init]) 18 | { 19 | #if DEBUG 20 | os_log_debug(OS_LOG_DEFAULT, "- [HUDMainApplicationDelegate init]"); 21 | #endif 22 | } 23 | return self; 24 | } 25 | 26 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 27 | { 28 | #if DEBUG 29 | os_log_debug(OS_LOG_DEFAULT, "- [HUDMainApplicationDelegate application:%{public}@ didFinishLaunchingWithOptions:%{public}@]", application, launchOptions); 30 | #endif 31 | 32 | _rootViewController = [[HUDRootViewController alloc] init]; 33 | 34 | self.window = [[HUDMainWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 35 | [self.window setRootViewController:_rootViewController]; 36 | 37 | [self.window setWindowLevel:10000010.0]; 38 | [self.window setHidden:NO]; 39 | [self.window makeKeyAndVisible]; 40 | 41 | _windowHostingController = [[objc_getClass("SBSAccessibilityWindowHostingController") alloc] init]; 42 | unsigned int _contextId = [self.window _contextId]; 43 | double windowLevel = [self.window windowLevel]; 44 | 45 | #pragma clang diagnostic push 46 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 47 | // [_windowHostingController registerWindowWithContextID:_contextId atLevel:windowLevel]; 48 | NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"v@:Id"]; 49 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; 50 | [invocation setTarget:_windowHostingController]; 51 | [invocation setSelector:NSSelectorFromString(@"registerWindowWithContextID:atLevel:")]; 52 | [invocation setArgument:&_contextId atIndex:2]; 53 | [invocation setArgument:&windowLevel atIndex:3]; 54 | [invocation invoke]; 55 | #pragma clang diagnostic pop 56 | 57 | return YES; 58 | } 59 | 60 | @end -------------------------------------------------------------------------------- /src/hud/HUDMainWindow.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | NS_ASSUME_NONNULL_BEGIN 4 | 5 | @interface HUDMainWindow : UIWindow 6 | @end 7 | 8 | NS_ASSUME_NONNULL_END -------------------------------------------------------------------------------- /src/hud/HUDMainWindow.mm: -------------------------------------------------------------------------------- 1 | #import "HUDMainWindow.h" 2 | #import "HUDRootViewController.h" 3 | 4 | @implementation HUDMainWindow 5 | 6 | + (BOOL)_isSystemWindow { return YES; } 7 | - (BOOL)_isWindowServerHostingManaged { return NO; } 8 | - (BOOL)_ignoresHitTest { return YES; } 9 | - (BOOL)_isSecure { return YES; } 10 | - (BOOL)_shouldCreateContextAsSecure { return YES; } 11 | 12 | @end -------------------------------------------------------------------------------- /src/hud/HUDRootViewController.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | NS_ASSUME_NONNULL_BEGIN 4 | 5 | @interface HUDRootViewController: UIViewController 6 | - (void)resetLoopTimer; 7 | - (void)pauseLoopTimer; 8 | - (void)resumeLoopTimer; 9 | - (void)reloadUserDefaults; 10 | @end 11 | 12 | NS_ASSUME_NONNULL_END -------------------------------------------------------------------------------- /src/views/UIApplicationViews.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // 4 | // 5 | // Created by lemin on 10/11/23. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | // MARK: Root View 12 | struct RootView: View { 13 | var body: some View { 14 | TabView { 15 | // Home Page 16 | HomePageView() 17 | .tabItem { 18 | Label(NSLocalizedString("Home", comment: ""), systemImage: "house") 19 | } 20 | 21 | // Widget Customization 22 | WidgetCustomizationView() 23 | .tabItem { 24 | Label(NSLocalizedString("Customize", comment: ""), systemImage: "paintbrush") 25 | } 26 | 27 | // Settings 28 | SettingsView() 29 | .tabItem { 30 | Label(NSLocalizedString("Settings", comment: ""), systemImage: "gear") 31 | } 32 | } 33 | .onAppear { 34 | let tabBarAppearance = UITabBarAppearance() 35 | tabBarAppearance.configureWithDefaultBackground() 36 | if #available(iOS 15.0, *) { 37 | UITabBar.appearance().scrollEdgeAppearance = tabBarAppearance 38 | } else { 39 | // Fallback on earlier versions 40 | } 41 | 42 | do { 43 | try FileManager.default.contentsOfDirectory(atPath: "/var/mobile") 44 | // warn to turn on developer mode if iOS 16+ 45 | if #available(iOS 16.0, *), !UserDefaults.standard.bool(forKey: "hasWarnedOfDevMode", forPath: USER_DEFAULTS_PATH) { 46 | UIApplication.shared.confirmAlert(title: NSLocalizedString("Info", comment: ""), body: NSLocalizedString("Make sure you enable developer mode before using! This will not work otherwise.", comment: ""), onOK: { 47 | UserDefaults.standard.setValue(true, forKey: "hasWarnedOfDevMode", forPath: USER_DEFAULTS_PATH) 48 | }, noCancel: true) 49 | } 50 | return 51 | } catch { 52 | UIApplication.shared.alert(title: NSLocalizedString("Not Supported", comment: ""), body: NSLocalizedString("This app must be installed with TrollStore.", comment: "")) 53 | } 54 | } 55 | } 56 | } 57 | 58 | // MARK: Objc Bridging 59 | @objc 60 | open class ContentInterface: NSObject { 61 | @objc 62 | open func createUI() -> UIViewController { 63 | let contents = RootView() 64 | return HostingController(rootView: contents) 65 | } 66 | } 67 | 68 | struct RootView_Previews: PreviewProvider { 69 | static var previews: some View { 70 | RootView() 71 | } 72 | } 73 | 74 | class HostingController: UIHostingController where Content: View { 75 | @objc override var preferredStatusBarStyle: UIStatusBarStyle { 76 | return .lightContent 77 | } 78 | } -------------------------------------------------------------------------------- /src/views/navigation/HomePageView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomePageView.swift 3 | // Helium UI 4 | // 5 | // Created by lemin on 10/19/23. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | // MARK: Home Page View 12 | struct HomePageView: View { 13 | @State private var isNowEnabled: Bool = false 14 | @State private var buttonDisabled: Bool = false 15 | @State private var inProgress = false 16 | 17 | var body: some View { 18 | NavigationView { 19 | VStack(spacing: 10) { 20 | Spacer() 21 | 22 | // Activate HUD Button 23 | Button( 24 | action: { 25 | #if targetEnvironment(simulator) 26 | isNowEnabled.toggle() 27 | #else 28 | toggleHUD(!isNowEnabled) 29 | #endif 30 | }, 31 | label: { 32 | HStack(spacing: 10) { 33 | if inProgress { 34 | if #available(iOS 16.0, *) { 35 | ProgressView() 36 | .tint(isNowEnabled ? .red : .blue) 37 | } else { 38 | ProgressView() 39 | .foregroundColor(isNowEnabled ? .red : .blue) 40 | } 41 | } 42 | Text( 43 | isNowEnabled 44 | ? NSLocalizedString("Disable HUD", comment: "") 45 | : NSLocalizedString("Enable HUD", comment: "")) 46 | } 47 | } 48 | ) 49 | .buttonStyle(TintedButton(color: isNowEnabled ? .red : .blue)) 50 | .padding(5) 51 | .offset(y: isNowEnabled ? -10 : 0) 52 | Text( 53 | NSLocalizedString( 54 | "You can quit the app now.\nThe HUD will persist on your screen.", comment: "") 55 | ) 56 | .multilineTextAlignment(.center) 57 | .offset(y: isNowEnabled ? 5 : 15) 58 | .foregroundColor(.blue) 59 | .opacity(isNowEnabled ? 1 : 0) 60 | 61 | Spacer() 62 | // HUD Info Text 63 | Text( 64 | isNowEnabled 65 | ? NSLocalizedString("Status: Running", comment: "") 66 | : NSLocalizedString("Status: Stopped", comment: "") 67 | ) 68 | .font(.caption) 69 | .foregroundColor(isNowEnabled ? .blue : .red) 70 | .padding(.bottom, 10) 71 | .multilineTextAlignment(.center) 72 | } 73 | .disabled(buttonDisabled) 74 | .onAppear { 75 | #if !targetEnvironment(simulator) 76 | isNowEnabled = IsHUDEnabledBridger() 77 | #endif 78 | } 79 | .onOpenURL(perform: { url in 80 | let _ = FileManager.default 81 | // MARK: URL Schemes 82 | if url.absoluteString == "helium://toggle" { 83 | #if !targetEnvironment(simulator) 84 | toggleHUD(!isNowEnabled) 85 | #endif 86 | } else if url.absoluteString == "helium://on" { 87 | #if !targetEnvironment(simulator) 88 | toggleHUD(true) 89 | #endif 90 | } else if url.absoluteString == "helium://off" { 91 | #if !targetEnvironment(simulator) 92 | toggleHUD(false) 93 | #endif 94 | } 95 | }) 96 | .navigationTitle(Text(NSLocalizedString("Helium", comment: ""))) 97 | } 98 | .navigationViewStyle(StackNavigationViewStyle()) 99 | .animation(.timingCurve(0.25, 0.1, 0.35, 1.75).speed(1.2), value: isNowEnabled) 100 | .animation(.timingCurve(0.25, 0.1, 0.35, 1.75).speed(1.2), value: inProgress) 101 | } 102 | 103 | func toggleHUD(_ isActive: Bool) { 104 | inProgress.toggle() 105 | Haptic.shared.play(.medium) 106 | if isNowEnabled == isActive { return } 107 | print( 108 | !isActive 109 | ? NSLocalizedString("Closing HUD", comment: "") 110 | : NSLocalizedString("Opening HUD", comment: "")) 111 | SetHUDEnabledBridger(isActive) 112 | 113 | buttonDisabled = true 114 | waitForNotificationBridger( 115 | { 116 | isNowEnabled = isActive 117 | buttonDisabled = false 118 | inProgress.toggle() 119 | }, !isNowEnabled) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/views/navigation/SettingsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsView.swift 3 | // Helium UI 4 | // 5 | // Created by lemin on 10/19/23. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | let buildNumber: Int = 0 12 | let DEBUG_MODE_ENABLED = false 13 | let USER_DEFAULTS_PATH = "/var/mobile/Library/Preferences/com.leemin.helium.plist" 14 | 15 | // MARK: Settings View 16 | // TODO: This 17 | struct SettingsView: View { 18 | // Debug Variables 19 | @State var sideWidgetSize: Int = 100 20 | @State var centerWidgetSize: Int = 100 21 | 22 | // Preference Variables 23 | @State var apiKey: String = "" 24 | @State var dateLocale: String = "en_US" 25 | @State var hideSaveConfirmation: Bool = false 26 | @State var debugBorder: Bool = false 27 | 28 | var body: some View { 29 | NavigationView { 30 | List { 31 | // App Version/Build Number 32 | Section { 33 | 34 | } header: { 35 | Label(NSLocalizedString("Version ", comment:"") + "\(Bundle.main.releaseVersionNumber ?? NSLocalizedString("UNKNOWN", comment:"")) (\(buildNumber != 0 ? "\(buildNumber)" : NSLocalizedString("Release", comment:"")))", systemImage: "info") 36 | } 37 | 38 | // Preferences List 39 | Section { 40 | HStack { 41 | Text(NSLocalizedString("Date Locale", comment:"")) 42 | .bold() 43 | Spacer() 44 | Picker("", selection: $dateLocale) { 45 | Text("en_US").tag("en_US") 46 | Text("zh_CN").tag("zh_CN") 47 | } 48 | .pickerStyle(.menu) 49 | } 50 | 51 | HStack { 52 | Text(NSLocalizedString("Weather Api key", comment:"")) 53 | .bold() 54 | Spacer() 55 | TextField("", text: $apiKey) 56 | .textFieldStyle(RoundedBorderTextFieldStyle()) 57 | } 58 | 59 | HStack { 60 | Toggle(isOn: $hideSaveConfirmation) { 61 | Text(NSLocalizedString("Hide Save Confirmation Popup", comment:"")) 62 | .bold() 63 | .minimumScaleFactor(0.5) 64 | } 65 | } 66 | 67 | HStack { 68 | Toggle(isOn: $debugBorder) { 69 | Text(NSLocalizedString("Display Debug Border", comment:"")) 70 | .bold() 71 | .minimumScaleFactor(0.5) 72 | } 73 | } 74 | 75 | HStack { 76 | Text(NSLocalizedString("Helium Data", comment:"")) 77 | .bold() 78 | Spacer() 79 | Button(action: { 80 | do { 81 | try UserDefaults.standard.deleteUserDefaults(forPath: USER_DEFAULTS_PATH) 82 | UIApplication.shared.alert(title: NSLocalizedString("Successfully deleted user data!", comment:""), body: NSLocalizedString("Please restart the app to continue.", comment:"")) 83 | } catch { 84 | UIApplication.shared.alert(title: NSLocalizedString("Failed to delete user data!", comment:""), body: error.localizedDescription) 85 | } 86 | }) { 87 | Text(NSLocalizedString("Reset Data", comment:"")) 88 | .foregroundColor(.red) 89 | } 90 | } 91 | } header: { 92 | Label(NSLocalizedString("Preferences", comment:""), systemImage: "gear") 93 | } 94 | 95 | // Debug Settings 96 | if #available(iOS 15, *), DEBUG_MODE_ENABLED { 97 | Section { 98 | HStack { 99 | Text(NSLocalizedString("Side Widget Size", comment:"")) 100 | .bold() 101 | Spacer() 102 | TextField(NSLocalizedString("Side Size", comment:""), value: $sideWidgetSize, format: .number) 103 | .textFieldStyle(RoundedBorderTextFieldStyle()) 104 | .keyboardType(.decimalPad) 105 | .submitLabel(.done) 106 | } 107 | 108 | HStack { 109 | Text(NSLocalizedString("Center Widget Size", comment:"")) 110 | .bold() 111 | Spacer() 112 | TextField(NSLocalizedString("Center Size", comment:""), value: $centerWidgetSize, format: .number) 113 | .textFieldStyle(RoundedBorderTextFieldStyle()) 114 | .keyboardType(.decimalPad) 115 | .submitLabel(.done) 116 | } 117 | } header: { 118 | Label(NSLocalizedString("Debug Preferences", comment:""), systemImage: "ladybug") 119 | } 120 | } 121 | 122 | // Credits List 123 | Section { 124 | LinkCell(imageName: "leminlimez", url: "https://github.com/leminlimez", title: "LeminLimez", contribution: NSLocalizedString("Main Developer", comment: "leminlimez's contribution"), circle: true) 125 | LinkCell(imageName: "lessica", url: "https://github.com/Lessica/TrollSpeed", title: "Lessica", contribution: NSLocalizedString("TrollSpeed & Assistive Touch Logic", comment: "lessica's contribution"), circle: true) 126 | LinkCell(imageName: "fuuko", url: "https://github.com/AsakuraFuuko", title: "Fuuko", contribution: NSLocalizedString("Modder", comment: "Fuuko's contribution"), imageInBundle: true, circle: true) 127 | LinkCell(imageName: "bomberfish", url: "https://github.com/BomberFish", title: "BomberFish", contribution: NSLocalizedString("UI improvements", comment: "BomberFish's contribution"), imageInBundle: true, circle: true) 128 | } header: { 129 | Label(NSLocalizedString("Credits", comment:""), systemImage: "wrench.and.screwdriver") 130 | } 131 | } 132 | .toolbar { 133 | HStack { 134 | Button(action: { 135 | saveChanges() 136 | }) { 137 | Text(NSLocalizedString("Save", comment:"")) 138 | } 139 | } 140 | } 141 | .onAppear { 142 | loadSettings() 143 | } 144 | .navigationTitle(Text(NSLocalizedString("Settings", comment:""))) 145 | } 146 | .navigationViewStyle(StackNavigationViewStyle()) 147 | } 148 | 149 | func loadSettings() { 150 | dateLocale = UserDefaults.standard.string(forKey: "dateLocale", forPath: USER_DEFAULTS_PATH) ?? "en_US" 151 | apiKey = UserDefaults.standard.string(forKey: "apiKey", forPath: USER_DEFAULTS_PATH) ?? "" 152 | hideSaveConfirmation = UserDefaults.standard.bool(forKey: "hideSaveConfirmation", forPath: USER_DEFAULTS_PATH) 153 | debugBorder = UserDefaults.standard.bool(forKey: "debugBorder", forPath: USER_DEFAULTS_PATH) 154 | } 155 | 156 | func saveChanges() { 157 | UserDefaults.standard.setValue(apiKey, forKey: "apiKey", forPath: USER_DEFAULTS_PATH) 158 | UserDefaults.standard.setValue(dateLocale, forKey: "dateLocale", forPath: USER_DEFAULTS_PATH) 159 | UserDefaults.standard.setValue(hideSaveConfirmation, forKey: "hideSaveConfirmation", forPath: USER_DEFAULTS_PATH) 160 | UserDefaults.standard.setValue(debugBorder, forKey: "debugBorder", forPath: USER_DEFAULTS_PATH) 161 | UIApplication.shared.alert(title: NSLocalizedString("Save Changes", comment:""), body: NSLocalizedString("Settings saved successfully", comment:"")) 162 | DarwinNotificationCenter.default.post(name: NOTIFY_RELOAD_HUD) 163 | } 164 | 165 | // Link Cell code from Cowabunga 166 | struct LinkCell: View { 167 | var imageName: String 168 | var url: String 169 | var title: String 170 | var contribution: String 171 | var systemImage: Bool = false 172 | var imageInBundle: Bool = false 173 | var circle: Bool = false 174 | 175 | var body: some View { 176 | HStack(alignment: .center) { 177 | Group { 178 | if systemImage { 179 | Image(systemName: imageName) 180 | .resizable() 181 | .aspectRatio(contentMode: .fit) 182 | } else if imageInBundle { 183 | let url = Bundle.main.url(forResource: "credits/" + imageName, withExtension: "png") 184 | if url != nil { 185 | Image(uiImage: UIImage(contentsOfFile: url!.path)!) 186 | .resizable() 187 | .aspectRatio(contentMode: .fit) 188 | } 189 | } else { 190 | if imageName != "" { 191 | Image(imageName) 192 | .resizable() 193 | .aspectRatio(contentMode: .fit) 194 | } 195 | } 196 | } 197 | .cornerRadius(circle ? .infinity : 0) 198 | .frame(width: 24, height: 24) 199 | 200 | VStack { 201 | HStack { 202 | Button(action: { 203 | if url != "" { 204 | UIApplication.shared.open(URL(string: url)!) 205 | } 206 | }) { 207 | Text(title) 208 | .fontWeight(.bold) 209 | } 210 | .padding(.horizontal, 6) 211 | Spacer() 212 | } 213 | HStack { 214 | Text(contribution) 215 | .padding(.horizontal, 6) 216 | .font(.footnote) 217 | Spacer() 218 | } 219 | } 220 | } 221 | .foregroundColor(.blue) 222 | } 223 | } 224 | } 225 | 226 | struct SettingsView_Previews: PreviewProvider { 227 | static var previews: some View { 228 | SettingsView() 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /src/views/navigation/WidgetCustomizationView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WidgetCustomizationView.swift 3 | // 4 | // 5 | // Created by lemin on 10/13/23. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | // MARK: Widget Customization View 12 | struct WidgetCustomizationView: View { 13 | @StateObject var widgetManager: WidgetManager = .init() 14 | 15 | var body: some View { 16 | NavigationView { 17 | VStack { 18 | // List of Widget Sets 19 | List { 20 | ForEach($widgetManager.widgetSets) { widgetSet in 21 | NavigationLink(destination: EditWidgetSetView(widgetManager: widgetManager, widgetSet: widgetSet.wrappedValue)) { 22 | Text(widgetSet.title.wrappedValue) 23 | } 24 | } 25 | .onDelete { indexSet in 26 | indexSet.forEach { i in 27 | UIApplication.shared.confirmAlert(title: NSLocalizedString("Delete ", comment:"")+"\(widgetManager.widgetSets[i].title)", body: NSLocalizedString("Are you sure you want to delete the widget set ", comment:"") + "\"\(widgetManager.widgetSets[i].title)\"?", onOK: { 28 | widgetManager.removeWidgetSet(widgetSet: widgetManager.widgetSets[i]) 29 | }, noCancel: false) 30 | } 31 | } 32 | } 33 | .toolbar { 34 | HStack { 35 | // create a new widget set 36 | Button(action: { 37 | UIApplication.shared.inputAlert(title: NSLocalizedString("Enter Name", comment:""), body: NSLocalizedString("Choose a name for the widget set.", comment:""), confirmTitle: NSLocalizedString("Confirm", comment:""), placeholder: NSLocalizedString("Name", comment:""), onOK: { title in 38 | // make selection for anchor 39 | UIApplication.shared.optionsAlert(title: NSLocalizedString("Choose Side", comment:""), body: NSLocalizedString("Choose a side for the widget set to anchor to. This can be changed later.", comment:""), options: [NSLocalizedString("Left", comment:""), NSLocalizedString("Center", comment:""), NSLocalizedString("Right", comment:"")]) { anchorSide in 40 | var anchor: Int 41 | switch (anchorSide) { 42 | case NSLocalizedString("Left", comment:""): 43 | anchor = 0 44 | break 45 | case NSLocalizedString("Center", comment:""): 46 | anchor = 1 47 | break 48 | case NSLocalizedString("Right", comment:""): 49 | anchor = 2 50 | break 51 | default: 52 | anchor = 0 53 | break 54 | } 55 | widgetManager.createWidgetSet(title: title == "" ? NSLocalizedString("Untitled", comment:"") : title, anchor: anchor) 56 | } 57 | }, noCancel: false) 58 | }) { 59 | Image(systemName: "plus") 60 | } 61 | } 62 | } 63 | } 64 | .navigationTitle(Text(NSLocalizedString("Customize", comment:""))) 65 | } 66 | .navigationViewStyle(StackNavigationViewStyle()) 67 | } 68 | } 69 | 70 | // MARK: Widget Modify View 71 | //struct WidgetModifyView: View { 72 | // @StateObject var widgetManager: WidgetManager 73 | // @Binding var widgetIndex: Int 74 | // 75 | // var dismiss: () -> Void 76 | // 77 | // var body: some View { 78 | // return VStack { 79 | // if widgetIndex >= 0 && widgetIndex < widgetManager.widgetStructs.count { 80 | // // Widget Preferences 81 | // WidgetPreferencesView(widgetStruct: $widgetManager.widgetStructs[widgetIndex]) 82 | // VStack { 83 | // Spacer() 84 | // // Save Button 85 | // Button("Save") { 86 | // widgetManager.saveWidgetStructs() 87 | // dismiss() 88 | // } 89 | // .buttonStyle(TintedButton(color: .blue, fullWidth: true)) 90 | // .padding(.horizontal, 7) 91 | // // Delete Button 92 | // Button("Delete") { 93 | // widgetManager.removeWidget(id: widgetIndex) 94 | // dismiss() 95 | // } 96 | // .buttonStyle(TintedButton(color: .red, fullWidth: true)) 97 | // .padding(.horizontal, 7) 98 | // } 99 | // .padding(10) 100 | // } 101 | // } 102 | // } 103 | //} 104 | 105 | // MARK: Widget Add View 106 | struct WidgetAddView: View { 107 | @StateObject var widgetManager: WidgetManager 108 | @State var widgetSet: WidgetSetStruct 109 | @Binding var isOpen: Bool 110 | 111 | var onChoice: (WidgetIDStruct) -> () 112 | 113 | var body: some View { 114 | List { 115 | ForEach(WidgetModule.allCases, id: \.self) { id in 116 | Button(action: { 117 | let newWidget = widgetManager.addWidget(widgetSet: widgetSet, module: id) 118 | isOpen = false 119 | onChoice(newWidget) 120 | }) { 121 | WidgetChoiceView(widgetName: WidgetDetails.getWidgetName(id), exampleText: WidgetDetails.getWidgetExample(id)) 122 | } 123 | } 124 | } 125 | } 126 | } 127 | 128 | // MARK: Widget Choice View 129 | struct WidgetChoiceView: View { 130 | var widgetName: String 131 | var exampleText: String 132 | 133 | var body: some View { 134 | HStack { 135 | ZStack { 136 | Image(uiImage: UIImage(named: "wallpaper")!) 137 | .resizable() 138 | .aspectRatio(contentMode: .fill) 139 | .scaleEffect(1.5) 140 | .frame(width: 125, height: 50) 141 | .cornerRadius(12) 142 | .clipped() 143 | ZStack { 144 | Text(exampleText) 145 | .padding(.vertical, 5) 146 | .foregroundColor(.white) 147 | } 148 | .frame(width: 125, height: 50) 149 | } 150 | .padding(.trailing, 5) 151 | Text(widgetName) 152 | .foregroundColor(.primary) 153 | .bold() 154 | } 155 | } 156 | } 157 | 158 | struct WidgetCustomizationView_Previews: PreviewProvider { 159 | static var previews: some View { 160 | WidgetCustomizationView() 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/views/widget/WidgetConfigureView.swift: -------------------------------------------------------------------------------- 1 | //// 2 | //// WidgetConfigureView.swift 3 | //// Helium UI 4 | //// 5 | //// Created by lemin on 11/2/23. 6 | //// 7 | // 8 | //import Foundation 9 | //import SwiftUI 10 | // 11 | //let cardScale: CGFloat = 0.9 12 | // 13 | //struct WidgetSettingsFlipView: View { 14 | // var screenGeom: GeometryProxy 15 | // @StateObject var widgetManager: WidgetManager 16 | // 17 | // @Binding var flippedWidget: WidgetStruct? 18 | // 19 | // @Binding var canPressButtons: Bool 20 | // 21 | // @Binding var flipped: Bool 22 | // @Binding var animate3d: Bool 23 | // 24 | // var body: some View { 25 | // return GeometryReader {cardGeometry in 26 | // VStack { 27 | // if animate3d && flippedWidget != nil { 28 | // BackWidgetOptionView(screenGeom: screenGeom, widgetManager: widgetManager, flippedWidget: $flippedWidget, widgetIndex: widgetManager.getWidgetID(widget: flippedWidget!), canPressButtons: $canPressButtons, flipFunction: flipCard) 29 | // .frame( 30 | // width: animate3d ? (min(screenGeom.size.width, screenGeom.size.height)) : 0, 31 | // height: animate3d ? (min(screenGeom.size.width, screenGeom.size.height)) : 0 32 | // ) 33 | // } else { 34 | // Rectangle() 35 | // .opacity(0) 36 | // } 37 | // } 38 | // .scaleEffect(animate3d ? cardScale : 0) 39 | // .zIndex(animate3d ? 2 : 0) 40 | // .modifier(FlipEffect(flipped: $flipped, angle: animate3d ? 180 : 0, axis: (x: 0.0, y: 1))) 41 | // .aspectRatio(1.0, contentMode: .fit) 42 | // } 43 | // } 44 | // 45 | // func flipCard() { 46 | // withAnimation(Animation.easeInOut(duration: CardAnimationSpeed)) { 47 | // animate3d.toggle() 48 | // } 49 | // } 50 | //} 51 | // 52 | //// MARK: Back Widget View/Edit Widget View 53 | //struct BackWidgetOptionView: View { 54 | // var screenGeom: GeometryProxy 55 | // @StateObject var widgetManager: WidgetManager 56 | // 57 | // @Binding var flippedWidget: WidgetStruct? 58 | // @State var widgetIndex: Int 59 | // 60 | // @Binding var canPressButtons: Bool 61 | // 62 | // var flipFunction: () -> Void 63 | // 64 | // var body: some View { 65 | // return VStack(alignment: .center) { 66 | // Text("Configure Widget") 67 | // .bold() 68 | // .font(.title2) 69 | // .padding(.top, 25) 70 | // 71 | // WidgetModifyView(widgetManager: widgetManager, widgetIndex: $widgetIndex, dismiss: { 72 | // canPressButtons = false 73 | // withAnimation(Animation.easeInOut(duration: CardAnimationSpeed)) { 74 | // flippedWidget = nil 75 | // flipFunction() 76 | // } 77 | // DispatchQueue.main.asyncAfter(deadline: .now() + CardAnimationSpeed + 0.05) { 78 | // canPressButtons = true 79 | // } 80 | // }) 81 | // .padding(5) 82 | // 83 | // Spacer() 84 | // } 85 | // .background(Color(uiColor14: .secondarySystemBackground)) 86 | // .cornerRadius(16) 87 | // } 88 | //} 89 | -------------------------------------------------------------------------------- /src/views/widget/WidgetPreviewsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WidgetPreviewsView.swift 3 | // Helium UI 4 | // 5 | // Created by lemin on 10/16/23. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | struct WidgetPreviewsView: View { 12 | @Binding var widget: WidgetIDStruct 13 | @State var text: String = "" 14 | @State var image: Image? 15 | @State var previewColor: Color = .primary 16 | 17 | var body: some View { 18 | HStack { 19 | ZStack { 20 | Image(uiImage: UIImage(named: "wallpaper")!) 21 | .resizable() 22 | .aspectRatio(contentMode: .fill) 23 | .scaleEffect(1.5) 24 | .frame(width: 125, height: 50) 25 | .cornerRadius(12) 26 | .clipped() 27 | ZStack { 28 | if image == nil { 29 | Text(text) 30 | .padding(.vertical, 5) 31 | .foregroundColor(previewColor) 32 | .minimumScaleFactor(0.01) 33 | } else { 34 | Text(image!) 35 | .padding(.vertical, 5) 36 | .foregroundColor(previewColor) 37 | .minimumScaleFactor(0.01) 38 | } 39 | } 40 | .frame(width: 125, height: 50) 41 | } 42 | .padding(.trailing, 5) 43 | } 44 | .onAppear { 45 | updatePreview() 46 | } 47 | .onChange(of: widget.modified) { nv in 48 | if nv { 49 | updatePreview() 50 | } 51 | } 52 | } 53 | 54 | func updatePreview() { 55 | switch (widget.module) { 56 | case .dateWidget, .timeWidget: 57 | let dateFormat: String = widget.config["dateFormat"] as? String ?? (widget.module == .dateWidget ? NSLocalizedString("E MMM dd", comment:"") : "hh:mm") 58 | let dateFormatter = DateFormatter() 59 | let newDateFormat = LunarDate.getChineseCalendar(with: Date(), format: dateFormat) 60 | dateFormatter.dateFormat = newDateFormat 61 | let locale = UserDefaults.standard.string(forKey: "dateLocale", forPath: USER_DEFAULTS_PATH) ?? "en_US" 62 | dateFormatter.locale = Locale(identifier: locale) 63 | text = dateFormatter.string(from: Date()) 64 | // SAFEGUARD 65 | if (text == "") { 66 | text = NSLocalizedString("ERROR", comment:"") 67 | } 68 | case .network: 69 | let isUp: Bool = widget.config["isUp"] as? Bool ?? false 70 | let speedIcon: Int = widget.config["speedIcon"] as? Int ?? 0 71 | text = "\(isUp ? (speedIcon == 0 ? "▲" : "↑") : (speedIcon == 0 ? "▼" : "↓")) 30 KB/s" 72 | case .temperature: 73 | text = widget.config["useFahrenheit"] as? Bool ?? false ? "78.84ºF" : "26.02ºC" 74 | case .battery: 75 | let batteryValue: Int = widget.config["batteryValueType"] as? Int ?? 0 76 | switch (batteryValue) { 77 | case 0: 78 | text = "0 W" 79 | case 1: 80 | text = "0 mA" 81 | case 2: 82 | text = "0 mA" 83 | case 3: 84 | text = "25" 85 | default: 86 | text = "???" 87 | } 88 | case .textWidget: 89 | text = widget.config["text"] as? String ?? NSLocalizedString("Unknown", comment:"") 90 | break; 91 | case .weather: 92 | text = NSLocalizedString("Weather Preview", comment:"") 93 | break; 94 | case .currentCapacity: 95 | text = "50\(widget.config["showPercentage"] as? Bool ?? true ? "%" : "")" 96 | case .chargeSymbol: 97 | image = widget.config["filled"] as? Bool ?? true ? Image(systemName: "bolt.fill") : Image(systemName: "bolt") 98 | } 99 | widget.modified = false 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/views/widgetset/SelectPresetView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SelectPresetView.swift 3 | // 4 | // 5 | // Created by lemin on 12/15/23. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | //struct SelectPresetView: View { 12 | // @StateObject var widgetManager: WidgetManager 13 | // @State var widgetSet: WidgetSetStruct 14 | // @Binding var isOpen: Bool 15 | // 16 | // var onChoice: (PresetStruct) -> () 17 | // 18 | // var body: some View { 19 | // List { 20 | // ForEach(Preset.allCases, id: \.self) { id in 21 | // Button(action: { 22 | // let newWidget = widgetManager.addWidget(widgetSet: widgetSet, module: id) 23 | // isOpen = false 24 | // onChoice(newWidget) 25 | // }) { 26 | // Text(id.rawValue) 27 | // } 28 | // } 29 | // } 30 | // } 31 | //} 32 | -------------------------------------------------------------------------------- /src/widgets/DeviceScaleManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // DeviceScaleManager.h 3 | // 4 | // 5 | // Created by lemin on 10/11/23. 6 | // 7 | 8 | double getCenterWidgetVerticalOffset(void); 9 | double getSideWidgetSize(void); 10 | double getCenterWidgetSize(void); 11 | double getCenterWidgetVerticalOffset(void); 12 | -------------------------------------------------------------------------------- /src/widgets/DeviceScaleManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // DeviceScaleManager.m 3 | // 4 | // 5 | // Created by lemin on 10/11/23. 6 | // 7 | 8 | #import 9 | #import 10 | #import "DeviceScaleManager.h" 11 | 12 | // Small Notch/Dynamic Island Definitions 13 | #define SMALL_SIDE_WIDGET_SIZE 0.27435897 // Original Size (iPhone 13 Pro): 107 14 | #define SMALL_CENTER_WIDGET_SIZE 0.34615385 // Original Size (iPhone 13 Pro): 135 15 | 16 | // Large Notch Definitions 17 | #define LARGE_SIDE_WIDGET_SIZE 0.19466667 // Original Size (iPhone X): 73 18 | #define LARGE_CENTER_WIDGET_SIZE 0.50666667 // Original Size (iPhone X): 190 19 | 20 | NSString* getDeviceName(void) 21 | { 22 | struct utsname systemInfo; 23 | uname(&systemInfo); 24 | 25 | return [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding]; 26 | } 27 | 28 | /* 29 | Sizes: 30 | 0 = No Notch 31 | 1 = Small Notch 32 | 2 = Large Notch 33 | 3 = Dynamic Island 34 | */ 35 | NSInteger getDeviceSize(void) 36 | { 37 | NSString *model = getDeviceName(); 38 | 39 | // get the notch size 40 | if ([model rangeOfString: @"iPhone14"].location != NSNotFound) { 41 | // Small Notch 42 | return 1; 43 | } else if ( 44 | [model rangeOfString: @"iPhone10,3"].location != NSNotFound // account for iPhone 8 45 | || [model rangeOfString: @"iPhone10,6"].location != NSNotFound // which is also iPhone10,_ 46 | || [model rangeOfString: @"iPhone11"].location != NSNotFound 47 | || [model rangeOfString: @"iPhone12"].location != NSNotFound 48 | || [model rangeOfString: @"iPhone13"].location != NSNotFound 49 | ) { 50 | return 2; 51 | } else if ( 52 | [model rangeOfString: @"iPhone15"].location != NSNotFound 53 | || [model rangeOfString: @"iPhone16"].location != NSNotFound 54 | ) { 55 | return 3; 56 | } 57 | return 0; 58 | } 59 | 60 | double getSideWidgetSize(void) 61 | { 62 | 63 | NSInteger deviceSize = getDeviceSize(); 64 | 65 | if (deviceSize == 1 || deviceSize == 3) { 66 | // Small Notch/Dynamic Island 67 | return SMALL_SIDE_WIDGET_SIZE; 68 | } else if (deviceSize == 2) { 69 | // Large Notch 70 | return LARGE_SIDE_WIDGET_SIZE; 71 | } 72 | 73 | return LARGE_SIDE_WIDGET_SIZE; 74 | } 75 | 76 | double getCenterWidgetSize(void) 77 | { 78 | 79 | NSInteger deviceSize = getDeviceSize(); 80 | 81 | if (deviceSize == 1 || deviceSize == 3) { 82 | // Small Notch/Dynamic Island 83 | return SMALL_CENTER_WIDGET_SIZE; 84 | } else if (deviceSize == 2) { 85 | // Large Notch 86 | return LARGE_CENTER_WIDGET_SIZE; 87 | } 88 | 89 | return LARGE_CENTER_WIDGET_SIZE; 90 | } 91 | 92 | double getCenterWidgetVerticalOffset(void) 93 | { 94 | NSInteger deviceSize = getDeviceSize(); 95 | 96 | if (deviceSize == 3) { 97 | // Dynamic Island 98 | return 20; 99 | } 100 | 101 | return 0; 102 | } 103 | -------------------------------------------------------------------------------- /src/widgets/WidgetManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // WidgetManager.h 3 | // 4 | // 5 | // Created by lemin on 10/6/23. 6 | // 7 | 8 | NSAttributedString* formattedAttributedString(NSArray *identifiers, double fontSize, UIColor *textColor, NSString *apiKey, NSString *dateLocale); 9 | --------------------------------------------------------------------------------