From 4753fe40be4af5ab38005f2dec85dda7d8be8c44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=84=E9=BB=9B=E6=B7=B3=E5=8D=8E?= Date: Sun, 1 Dec 2024 10:05:45 +0800 Subject: [PATCH 1/9] refactor: separate InputController --- InputController/InputController.h | 19 ++ InputController/module.modulemap | 6 + Squirrel.xcodeproj/project.pbxproj | 273 +++++++++++++++++++++- sources/FileManager+Extension.swift | 20 ++ sources/GlobalContext.swift | 214 +++++++++++++++++ sources/InputSource.swift | 4 +- sources/Main.swift | 19 +- sources/SquirrelApplicationDelegate.swift | 248 +++----------------- sources/SquirrelInputController.swift | 60 +++-- 9 files changed, 579 insertions(+), 284 deletions(-) create mode 100644 InputController/InputController.h create mode 100644 InputController/module.modulemap create mode 100644 sources/FileManager+Extension.swift create mode 100644 sources/GlobalContext.swift diff --git a/InputController/InputController.h b/InputController/InputController.h new file mode 100644 index 000000000..4d95d61e1 --- /dev/null +++ b/InputController/InputController.h @@ -0,0 +1,19 @@ +// +// InputController.h +// InputController +// +// Created by mi on 2024/12/1. +// + +#import + +//! Project version number for InputController. +FOUNDATION_EXPORT double InputControllerVersionNumber; + +//! Project version string for InputController. +FOUNDATION_EXPORT const unsigned char InputControllerVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + +#import +#import diff --git a/InputController/module.modulemap b/InputController/module.modulemap new file mode 100644 index 000000000..fbfa33181 --- /dev/null +++ b/InputController/module.modulemap @@ -0,0 +1,6 @@ +module InputController { + umbrella header "InputController.h" + + export * + module * { export * } +} diff --git a/Squirrel.xcodeproj/project.pbxproj b/Squirrel.xcodeproj/project.pbxproj index 8f9f93f42..e143958f3 100644 --- a/Squirrel.xcodeproj/project.pbxproj +++ b/Squirrel.xcodeproj/project.pbxproj @@ -3,10 +3,23 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 70; objects = { /* Begin PBXBuildFile section */ + 0DD1CB702CFBF51400E6C847 /* SquirrelConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = B39771242BED899F0093A49B /* SquirrelConfig.swift */; }; + 0DD1CB712CFBF52200E6C847 /* SquirrelTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = B39771262BED9B250093A49B /* SquirrelTheme.swift */; }; + 0DD1CB722CFBF52D00E6C847 /* SquirrelPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B397712A2BEE4C160093A49B /* SquirrelPanel.swift */; }; + 0DD1CB732CFBF53A00E6C847 /* FileManager+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DEE70632CFBF0FF0062CC96 /* FileManager+Extension.swift */; }; + 0DD1CB742CFBF60700E6C847 /* SquirrelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B39771282BEDAF4A0093A49B /* SquirrelView.swift */; }; + 0DD1CB762CFBFB6500E6C847 /* librime.1.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 44CD640915E2633D0021234E /* librime.1.dylib */; }; + 0DD1CBD12CFC00CE00E6C847 /* librime.1.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 44CD640915E2633D0021234E /* librime.1.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 0DEE705C2CFBEDAC0062CC96 /* SquirrelInputController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B38E9B942BEAFEFD0036ABEF /* SquirrelInputController.swift */; }; + 0DEE705D2CFBEE170062CC96 /* BridgingFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B35D2FE72BF00839009D156B /* BridgingFunctions.swift */; }; + 0DEE705E2CFBEE2D0062CC96 /* MacOSKeyCodes.swift in Sources */ = {isa = PBXBuildFile; fileRef = B39771222BECEA150093A49B /* MacOSKeyCodes.swift */; }; + 0DEE70642CFBF1080062CC96 /* FileManager+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DEE70632CFBF0FF0062CC96 /* FileManager+Extension.swift */; }; + 0DEE70662CFBF2070062CC96 /* GlobalContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DEE70652CFBF2030062CC96 /* GlobalContext.swift */; }; + 0DEE70672CFBF26E0062CC96 /* GlobalContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DEE70652CFBF2030062CC96 /* GlobalContext.swift */; }; 2C6B9F9D2BCD086700E327DF /* librime-lua.dylib in Copy Rime plugins */ = {isa = PBXBuildFile; fileRef = 2C6B9F9A2BCD086700E327DF /* librime-lua.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 2C6B9F9E2BCD086700E327DF /* librime-octagram.dylib in Copy Rime plugins */ = {isa = PBXBuildFile; fileRef = 2C6B9F9B2BCD086700E327DF /* librime-octagram.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 2C6B9F9F2BCD086700E327DF /* librime-predict.dylib in Copy Rime plugins */ = {isa = PBXBuildFile; fileRef = 2C6B9F9C2BCD086700E327DF /* librime-predict.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; @@ -90,6 +103,16 @@ /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ + 0DD1CBD02CFC00C600E6C847 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 0DD1CBD12CFC00CE00E6C847 /* librime.1.dylib in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 2C6B9F992BCD083D00E327DF /* Copy Rime plugins */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -202,6 +225,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 0DEE704F2CFBEC6C0062CC96 /* InputController.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = InputController.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 0DEE70632CFBF0FF0062CC96 /* FileManager+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileManager+Extension.swift"; sourceTree = ""; }; + 0DEE70652CFBF2030062CC96 /* GlobalContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalContext.swift; sourceTree = ""; }; 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; @@ -273,17 +299,17 @@ A4B8E1B20F645B870094E08B /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = ""; }; B3216E5B2BF438F800E292D2 /* rime.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = rime.pdf; path = resources/rime.pdf; sourceTree = ""; }; B32B80772BE7FAA200FCF3BC /* Squirrel.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; name = Squirrel.entitlements; path = resources/Squirrel.entitlements; sourceTree = ""; }; - B35D2FE72BF00839009D156B /* BridgingFunctions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = BridgingFunctions.swift; path = sources/BridgingFunctions.swift; sourceTree = ""; }; - B38E9B8F2BE9AE1E0036ABEF /* Squirrel-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "Squirrel-Bridging-Header.h"; path = "sources/Squirrel-Bridging-Header.h"; sourceTree = ""; }; - B38E9B902BE9AE1E0036ABEF /* SquirrelApplicationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SquirrelApplicationDelegate.swift; path = sources/SquirrelApplicationDelegate.swift; sourceTree = ""; }; - B38E9B942BEAFEFD0036ABEF /* SquirrelInputController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SquirrelInputController.swift; path = sources/SquirrelInputController.swift; sourceTree = ""; }; - B39771222BECEA150093A49B /* MacOSKeyCodes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = MacOSKeyCodes.swift; path = sources/MacOSKeyCodes.swift; sourceTree = ""; }; - B39771242BED899F0093A49B /* SquirrelConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SquirrelConfig.swift; path = sources/SquirrelConfig.swift; sourceTree = ""; }; - B39771262BED9B250093A49B /* SquirrelTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SquirrelTheme.swift; path = sources/SquirrelTheme.swift; sourceTree = ""; }; - B39771282BEDAF4A0093A49B /* SquirrelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SquirrelView.swift; path = sources/SquirrelView.swift; sourceTree = ""; }; - B397712A2BEE4C160093A49B /* SquirrelPanel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SquirrelPanel.swift; path = sources/SquirrelPanel.swift; sourceTree = ""; }; - B397712C2BEEB39D0093A49B /* InputSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = InputSource.swift; path = sources/InputSource.swift; sourceTree = ""; }; - B397712E2BEECBED0093A49B /* Main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Main.swift; path = sources/Main.swift; sourceTree = ""; }; + B35D2FE72BF00839009D156B /* BridgingFunctions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BridgingFunctions.swift; sourceTree = ""; }; + B38E9B8F2BE9AE1E0036ABEF /* Squirrel-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Squirrel-Bridging-Header.h"; sourceTree = ""; }; + B38E9B902BE9AE1E0036ABEF /* SquirrelApplicationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SquirrelApplicationDelegate.swift; sourceTree = ""; }; + B38E9B942BEAFEFD0036ABEF /* SquirrelInputController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SquirrelInputController.swift; sourceTree = ""; }; + B39771222BECEA150093A49B /* MacOSKeyCodes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacOSKeyCodes.swift; sourceTree = ""; }; + B39771242BED899F0093A49B /* SquirrelConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SquirrelConfig.swift; sourceTree = ""; }; + B39771262BED9B250093A49B /* SquirrelTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SquirrelTheme.swift; sourceTree = ""; }; + B39771282BEDAF4A0093A49B /* SquirrelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SquirrelView.swift; sourceTree = ""; }; + B397712A2BEE4C160093A49B /* SquirrelPanel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SquirrelPanel.swift; sourceTree = ""; }; + B397712C2BEEB39D0093A49B /* InputSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputSource.swift; sourceTree = ""; }; + B397712E2BEECBED0093A49B /* Main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Main.swift; sourceTree = ""; }; B39771302BEEE4540093A49B /* InfoPlist.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; name = InfoPlist.xcstrings; path = resources/InfoPlist.xcstrings; sourceTree = ""; }; B39771322BEEE4540093A49B /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; name = Localizable.xcstrings; path = resources/Localizable.xcstrings; sourceTree = ""; }; D26434542706A15100857391 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; @@ -291,7 +317,29 @@ F45E005E2B8CA81C00179B75 /* UserNotifications.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotifications.framework; path = System/Library/Frameworks/UserNotifications.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + 0DEE70592CFBEC6C0062CC96 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + publicHeaders = ( + InputController.h, + ); + target = 0DEE704E2CFBEC6C0062CC96 /* InputController */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 0DEE70502CFBEC6C0062CC96 /* InputController */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (0DEE70592CFBEC6C0062CC96 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = InputController; sourceTree = ""; }; +/* End PBXFileSystemSynchronizedRootGroup section */ + /* Begin PBXFrameworksBuildPhase section */ + 0DEE704C2CFBEC6C0062CC96 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0DD1CB762CFBFB6500E6C847 /* librime.1.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 8D11072E0486CEB800E47090 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -310,6 +358,7 @@ isa = PBXGroup; children = ( B38E9B902BE9AE1E0036ABEF /* SquirrelApplicationDelegate.swift */, + 0DEE70652CFBF2030062CC96 /* GlobalContext.swift */, B38E9B942BEAFEFD0036ABEF /* SquirrelInputController.swift */, B39771222BECEA150093A49B /* MacOSKeyCodes.swift */, B397712C2BEEB39D0093A49B /* InputSource.swift */, @@ -319,9 +368,11 @@ B39771282BEDAF4A0093A49B /* SquirrelView.swift */, B39771242BED899F0093A49B /* SquirrelConfig.swift */, B35D2FE72BF00839009D156B /* BridgingFunctions.swift */, + 0DEE70632CFBF0FF0062CC96 /* FileManager+Extension.swift */, B38E9B8F2BE9AE1E0036ABEF /* Squirrel-Bridging-Header.h */, ); name = Sources; + path = sources; sourceTree = ""; }; 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = { @@ -349,6 +400,7 @@ isa = PBXGroup; children = ( 44F1EB381431F8270015FD04 /* Squirrel.app */, + 0DEE704F2CFBEC6C0062CC96 /* InputController.framework */, ); name = Products; sourceTree = ""; @@ -363,6 +415,7 @@ 44DA7A4214DD598900C1ED3B /* SharedSupport */, 080E96DDFE201D6D7F000001 /* Sources */, 29B97317FDCFA39411CA2CEA /* Resources */, + 0DEE70502CFBEC6C0062CC96 /* InputController */, 29B97323FDCFA39411CA2CEA /* Frameworks */, 19C28FACFE9D520D11CA2CBB /* Products */, ); @@ -370,6 +423,7 @@ name = Squirrel; sourceTree = ""; tabWidth = 2; + usesTabs = 0; }; 29B97317FDCFA39411CA2CEA /* Resources */ = { isa = PBXGroup; @@ -485,7 +539,41 @@ }; /* End PBXGroup section */ +/* Begin PBXHeadersBuildPhase section */ + 0DEE704A2CFBEC6C0062CC96 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + /* Begin PBXNativeTarget section */ + 0DEE704E2CFBEC6C0062CC96 /* InputController */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0DEE705A2CFBEC6C0062CC96 /* Build configuration list for PBXNativeTarget "InputController" */; + buildPhases = ( + 0DEE704A2CFBEC6C0062CC96 /* Headers */, + 0DEE704B2CFBEC6C0062CC96 /* Sources */, + 0DEE704C2CFBEC6C0062CC96 /* Frameworks */, + 0DEE704D2CFBEC6C0062CC96 /* Resources */, + 0DD1CBD02CFC00C600E6C847 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 0DEE70502CFBEC6C0062CC96 /* InputController */, + ); + name = InputController; + packageProductDependencies = ( + ); + productName = InputController; + productReference = 0DEE704F2CFBEC6C0062CC96 /* InputController.framework */; + productType = "com.apple.product-type.framework"; + }; 8D1107260486CEB800E47090 /* Squirrel */ = { isa = PBXNativeTarget; buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "Squirrel" */; @@ -520,6 +608,9 @@ BuildIndependentTargetsInParallel = YES; LastUpgradeCheck = 1540; TargetAttributes = { + 0DEE704E2CFBEC6C0062CC96 = { + CreatedOnToolsVersion = 16.0; + }; 8D1107260486CEB800E47090 = { LastSwiftMigration = 1530; }; @@ -542,11 +633,19 @@ projectRoot = ""; targets = ( 8D1107260486CEB800E47090 /* Squirrel */, + 0DEE704E2CFBEC6C0062CC96 /* InputController */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 0DEE704D2CFBEC6C0062CC96 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 8D1107290486CEB800E47090 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -563,10 +662,28 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 0DEE704B2CFBEC6C0062CC96 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0DEE70672CFBF26E0062CC96 /* GlobalContext.swift in Sources */, + 0DD1CB742CFBF60700E6C847 /* SquirrelView.swift in Sources */, + 0DD1CB732CFBF53A00E6C847 /* FileManager+Extension.swift in Sources */, + 0DEE705C2CFBEDAC0062CC96 /* SquirrelInputController.swift in Sources */, + 0DEE705D2CFBEE170062CC96 /* BridgingFunctions.swift in Sources */, + 0DD1CB722CFBF52D00E6C847 /* SquirrelPanel.swift in Sources */, + 0DEE705E2CFBEE2D0062CC96 /* MacOSKeyCodes.swift in Sources */, + 0DD1CB712CFBF52200E6C847 /* SquirrelTheme.swift in Sources */, + 0DD1CB702CFBF51400E6C847 /* SquirrelConfig.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 8D11072C0486CEB800E47090 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 0DEE70662CFBF2070062CC96 /* GlobalContext.swift in Sources */, + 0DEE70642CFBF1080062CC96 /* FileManager+Extension.swift in Sources */, B39771232BECEA150093A49B /* MacOSKeyCodes.swift in Sources */, B39771292BEDAF4A0093A49B /* SquirrelView.swift in Sources */, B38E9B952BEAFEFD0036ABEF /* SquirrelInputController.swift in Sources */, @@ -583,6 +700,127 @@ /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ + 0DEE70572CFBEC6C0062CC96 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/lib", + ); + MACOSX_DEPLOYMENT_TARGET = 15.0; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = im.rime.inputmethod.Squirrel.InputController; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 0DEE70582CFBEC6C0062CC96 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/lib", + ); + MACOSX_DEPLOYMENT_TARGET = 15.0; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = im.rime.inputmethod.Squirrel.InputController; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; C01FCF4B08A954540054247B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -745,6 +983,7 @@ MACOSX_DEPLOYMENT_TARGET = 13.0; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; + SWIFT_INCLUDE_PATHS = "$(HEADER_SEARCH_PATHS_QUOTED_FOR_PROJECT_1)/** $(HEADER_SEARCH_PATHS_QUOTED_FOR_PROJECT_2)/**"; SWIFT_STRICT_CONCURRENCY = targeted; SWIFT_VERSION = 5.0; SYSTEM_HEADER_SEARCH_PATHS = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Tk.framework/Headers; @@ -802,6 +1041,7 @@ ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_INCLUDE_PATHS = "$(HEADER_SEARCH_PATHS_QUOTED_FOR_PROJECT_1)/** $(HEADER_SEARCH_PATHS_QUOTED_FOR_PROJECT_2)/**"; SWIFT_STRICT_CONCURRENCY = targeted; SWIFT_VERSION = 5.0; SYSTEM_HEADER_SEARCH_PATHS = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Tk.framework/Headers; @@ -811,6 +1051,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 0DEE705A2CFBEC6C0062CC96 /* Build configuration list for PBXNativeTarget "InputController" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0DEE70572CFBEC6C0062CC96 /* Debug */, + 0DEE70582CFBEC6C0062CC96 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "Squirrel" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/sources/FileManager+Extension.swift b/sources/FileManager+Extension.swift new file mode 100644 index 000000000..88a4e9d6b --- /dev/null +++ b/sources/FileManager+Extension.swift @@ -0,0 +1,20 @@ +// +// FileManager+Extension.swift +// Squirrel +// +// Created by mi on 2024/12/1. +// + +import Foundation + +extension FileManager { + func createDirIfNotExist(path: URL) { + if !fileExists(atPath: path.path()) { + do { + try createDirectory(at: path, withIntermediateDirectories: true) + } catch { + print("Error creating user data directory: \(path.path())") + } + } + } +} diff --git a/sources/GlobalContext.swift b/sources/GlobalContext.swift new file mode 100644 index 000000000..b4448a6a0 --- /dev/null +++ b/sources/GlobalContext.swift @@ -0,0 +1,214 @@ +// +// GlobalContext.swift +// Squirrel +// +// Created by mi on 2024/12/1. +// + +import Foundation +import UserNotifications + +final class GlobalContext { + static let shared = GlobalContext() + + let rimeAPI: RimeApi_stdbool = rime_get_api_stdbool().pointee + var config: SquirrelConfig? + var panel: SquirrelPanel? + var enableNotifications = false + + static let notificationIdentifier = "SquirrelNotification" + + enum Path { + static let userDir = if let pwuid = getpwuid(getuid()) { + URL(fileURLWithFileSystemRepresentation: pwuid.pointee.pw_dir, isDirectory: true, relativeTo: nil).appending(components: "Library", "Rime") + } else { + try! FileManager.default.url(for: .libraryDirectory, in: .userDomainMask, appropriateFor: nil, create: false).appendingPathComponent("Rime", isDirectory: true) + } + static let appDir = "/Library/Input Library/Squirrel.app".withCString { dir in + URL(fileURLWithFileSystemRepresentation: dir, isDirectory: false, relativeTo: nil) + } + static let logDir = FileManager.default.temporaryDirectory.appending(component: "rime.squirrel", directoryHint: .isDirectory) + } + + func setupRime() { + FileManager.default.createDirIfNotExist(path: Path.userDir) + FileManager.default.createDirIfNotExist(path: Path.logDir) + // swiftlint:disable identifier_name + let notification_handler: @convention(c) (UnsafeMutableRawPointer?, RimeSessionId, UnsafePointer?, UnsafePointer?) -> Void = notificationHandler + let context_object = Unmanaged.passUnretained(self).toOpaque() + // swiftlint:enable identifier_name + rimeAPI.set_notification_handler(notification_handler, context_object) + + var squirrelTraits = RimeTraits.rimeStructInit() + squirrelTraits.setCString(Bundle.main.sharedSupportPath!, to: \.shared_data_dir) + squirrelTraits.setCString(Path.userDir.path(), to: \.user_data_dir) + squirrelTraits.setCString(Path.logDir.path(), to: \.log_dir) + squirrelTraits.setCString("Squirrel", to: \.distribution_code_name) + squirrelTraits.setCString("鼠鬚管", to: \.distribution_name) + squirrelTraits.setCString(Bundle.main.object(forInfoDictionaryKey: kCFBundleVersionKey as String) as! String, to: \.distribution_version) + squirrelTraits.setCString("rime.squirrel", to: \.app_name) + rimeAPI.setup(&squirrelTraits) + } + + func startRime(fullCheck: Bool) { + print("Initializing la rime...") + rimeAPI.initialize(nil) + // check for configuration updates + if rimeAPI.start_maintenance(fullCheck) { + // update squirrel config + // print("[DEBUG] maintenance suceeds") + _ = rimeAPI.deploy_config_file("squirrel.yaml", "config_version") + } else { + // print("[DEBUG] maintenance fails") + } + } + + func loadSettings() { + config = SquirrelConfig() + if !config!.openBaseConfig() { + return + } + + enableNotifications = config!.getString("show_notifications_when") != "never" + if let panel = panel, let config = self.config { + panel.load(config: config, forDarkMode: false) + panel.load(config: config, forDarkMode: true) + } + } + + func loadSettings(for schemaID: String) { + if schemaID.count == 0 || schemaID.first == "." { + return + } + let schema = SquirrelConfig() + if let panel = panel, let config = self.config { + if schema.open(schemaID: schemaID, baseConfig: config) && schema.has(section: "style") { + panel.load(config: schema, forDarkMode: false) + panel.load(config: schema, forDarkMode: true) + } else { + panel.load(config: config, forDarkMode: false) + panel.load(config: config, forDarkMode: true) + } + } + schema.close() + } + + func deploy() { + print("Start maintenance...") + self.shutdownRime() + self.startRime(fullCheck: true) + self.loadSettings() + } + + // prevent freezing the system + func problematicLaunchDetected() -> Bool { + var detected = false + let logFile = FileManager.default.temporaryDirectory.appendingPathComponent("squirrel_launch.json", conformingTo: .json) + // print("[DEBUG] archive: \(logFile)") + do { + let archive = try Data(contentsOf: logFile, options: [.uncached]) + let decoder = JSONDecoder() + decoder.dateDecodingStrategy = .millisecondsSince1970 + let previousLaunch = try decoder.decode(Date.self, from: archive) + if previousLaunch.timeIntervalSinceNow >= -2 { + detected = true + } + } catch let error as NSError where error.domain == NSCocoaErrorDomain && error.code == NSFileReadNoSuchFileError { + + } catch { + print("Error occurred during processing launch time archive: \(error.localizedDescription)") + return detected + } + do { + let encoder = JSONEncoder() + encoder.dateEncodingStrategy = .millisecondsSince1970 + let record = try encoder.encode(Date.now) + try record.write(to: logFile) + } catch { + print("Error occurred during saving launch time to archive: \(error.localizedDescription)") + } + return detected + } + + func showStatusMessage(msgTextLong: String?, msgTextShort: String?) { + if !(msgTextLong ?? "").isEmpty || !(msgTextShort ?? "").isEmpty { + panel?.updateStatus(long: msgTextLong ?? "", short: msgTextShort ?? "") + } + } + + func shutdownRime() { + config?.close() + rimeAPI.finalize() + } +} + + +func showMessage(msgText: String?) { + let center = UNUserNotificationCenter.current() + center.requestAuthorization(options: [.alert, .provisional]) { _, error in + if let error = error { + print("User notification authorization error: \(error.localizedDescription)") + } + } + center.getNotificationSettings { settings in + if (settings.authorizationStatus == .authorized || settings.authorizationStatus == .provisional) && settings.alertSetting == .enabled { + let content = UNMutableNotificationContent() + content.title = NSLocalizedString("Squirrel", comment: "") + if let msgText = msgText { + content.subtitle = msgText + } + content.interruptionLevel = .active + let request = UNNotificationRequest(identifier: GlobalContext.notificationIdentifier, content: content, trigger: nil) + center.add(request) { error in + if let error = error { + print("User notification request error: \(error.localizedDescription)") + } + } + } + } +} + +private func notificationHandler(contextObject: UnsafeMutableRawPointer?, sessionId: RimeSessionId, messageTypeC: UnsafePointer?, messageValueC: UnsafePointer?) { + let context: GlobalContext = Unmanaged.fromOpaque(contextObject!).takeUnretainedValue() + + let messageType = messageTypeC.map { String(cString: $0) } + let messageValue = messageValueC.map { String(cString: $0) } + if messageType == "deploy" { + switch messageValue { + case "start": + showMessage(msgText: NSLocalizedString("deploy_start", comment: "")) + case "success": + showMessage(msgText: NSLocalizedString("deploy_success", comment: "")) + case "failure": + showMessage(msgText: NSLocalizedString("deploy_failure", comment: "")) + default: + break + } + return + } + // off + if !context.enableNotifications { + return + } + + if messageType == "schema", let messageValue = messageValue, let schemaName = try? /^[^\/]*\/(.*)$/.firstMatch(in: messageValue)?.output.1 { + context.showStatusMessage(msgTextLong: String(schemaName), msgTextShort: String(schemaName)) + return + } else if messageType == "option" { + let state = messageValue?.first != "!" + let optionName = if state { + messageValue + } else { + String(messageValue![messageValue!.index(after: messageValue!.startIndex)...]) + } + if let optionName = optionName { + optionName.withCString { name in + let stateLabelLong = context.rimeAPI.get_state_label_abbreviated(sessionId, name, state, false) + let stateLabelShort = context.rimeAPI.get_state_label_abbreviated(sessionId, name, state, true) + let longLabel = stateLabelLong.str.map { String(cString: $0) } + let shortLabel = stateLabelShort.str.map { String(cString: $0) } + context.showStatusMessage(msgTextLong: longLabel, msgTextShort: shortLabel) + } + } + } +} diff --git a/sources/InputSource.swift b/sources/InputSource.swift index a0c80add3..562bd6592 100644 --- a/sources/InputSource.swift +++ b/sources/InputSource.swift @@ -47,8 +47,8 @@ final class SquirrelInstaller { // Already registered. return } - TISRegisterInputSource(SquirrelApp.appDir as CFURL) - print("Registered input source from \(SquirrelApp.appDir)") + TISRegisterInputSource(GlobalContext.Path.appDir as CFURL) + print("Registered input source from \(GlobalContext.Path.appDir)") } func enable(modes: [InputMode] = []) { diff --git a/sources/Main.swift b/sources/Main.swift index dca533ba3..27e209c98 100644 --- a/sources/Main.swift +++ b/sources/Main.swift @@ -10,15 +10,6 @@ import InputMethodKit @main struct SquirrelApp { - static let userDir = if let pwuid = getpwuid(getuid()) { - URL(fileURLWithFileSystemRepresentation: pwuid.pointee.pw_dir, isDirectory: true, relativeTo: nil).appending(components: "Library", "Rime") - } else { - try! FileManager.default.url(for: .libraryDirectory, in: .userDomainMask, appropriateFor: nil, create: false).appendingPathComponent("Rime", isDirectory: true) - } - static let appDir = "/Library/Input Library/Squirrel.app".withCString { dir in - URL(fileURLWithFileSystemRepresentation: dir, isDirectory: false, relativeTo: nil) - } - static let logDir = FileManager.default.temporaryDirectory.appending(component: "rime.squirrel", directoryHint: .isDirectory) // swiftlint:disable:next cyclomatic_complexity static func main() { @@ -69,7 +60,7 @@ struct SquirrelApp { return true case "--build": // Notification - SquirrelApplicationDelegate.showMessage(msgText: NSLocalizedString("deploy_update", comment: "")) + showMessage(msgText: NSLocalizedString("deploy_update", comment: "")) // Build all schemas in current directory var builderTraits = RimeTraits.rimeStructInit() builderTraits.setCString("rime.squirrel-builder", to: \.app_name) @@ -108,7 +99,7 @@ struct SquirrelApp { // opencc will be configured with relative dictionary paths FileManager.default.changeCurrentDirectoryPath(main.sharedSupportPath!) - if NSApp.squirrelAppDelegate.problematicLaunchDetected() { + if GlobalContext.shared.problematicLaunchDetected() { print("Problematic launch detected!") let args = ["Problematic launch detected! Squirrel may be suffering a crash due to improper configuration. Revert previous modifications to see if the problem recurs."] let task = Process() @@ -118,9 +109,9 @@ struct SquirrelApp { task.arguments = args try? task.run() } else { - NSApp.squirrelAppDelegate.setupRime() - NSApp.squirrelAppDelegate.startRime(fullCheck: false) - NSApp.squirrelAppDelegate.loadSettings() + GlobalContext.shared.setupRime() + GlobalContext.shared.startRime(fullCheck: false) + GlobalContext.shared.loadSettings() print("Squirrel reporting!") } diff --git a/sources/SquirrelApplicationDelegate.swift b/sources/SquirrelApplicationDelegate.swift index c60376040..20fe0faac 100644 --- a/sources/SquirrelApplicationDelegate.swift +++ b/sources/SquirrelApplicationDelegate.swift @@ -12,12 +12,7 @@ import AppKit final class SquirrelApplicationDelegate: NSObject, NSApplicationDelegate, SPUStandardUserDriverDelegate, UNUserNotificationCenterDelegate { static let rimeWikiURL = URL(string: "https://github.com/rime/home/wiki")! static let updateNotificationIdentifier = "SquirrelUpdateNotification" - static let notificationIdentifier = "SquirrelNotification" - let rimeAPI: RimeApi_stdbool = rime_get_api_stdbool().pointee - var config: SquirrelConfig? - var panel: SquirrelPanel? - var enableNotifications = false let updateController = SPUStandardUpdaterController(startingUpdater: true, updaterDelegate: nil, userDriverDelegate: nil) var supportsGentleScheduledUpdateReminders: Bool { true @@ -53,7 +48,7 @@ final class SquirrelApplicationDelegate: NSObject, NSApplicationDelegate, SPUSta } func applicationWillFinishLaunching(_ notification: Notification) { - panel = SquirrelPanel(position: .zero) + GlobalContext.shared.panel = SquirrelPanel(position: .zero) addObservers() } @@ -61,158 +56,7 @@ final class SquirrelApplicationDelegate: NSObject, NSApplicationDelegate, SPUSta // swiftlint:disable:next notification_center_detachment NotificationCenter.default.removeObserver(self) DistributedNotificationCenter.default().removeObserver(self) - panel?.hide() - } - - func deploy() { - print("Start maintenance...") - self.shutdownRime() - self.startRime(fullCheck: true) - self.loadSettings() - } - - func syncUserData() { - print("Sync user data") - _ = rimeAPI.sync_user_data() - } - - func openLogFolder() { - NSWorkspace.shared.open(SquirrelApp.logDir) - } - - func openRimeFolder() { - NSWorkspace.shared.open(SquirrelApp.userDir) - } - - func checkForUpdates() { - if updateController.updater.canCheckForUpdates { - print("Checking for updates") - updateController.updater.checkForUpdates() - } else { - print("Cannot check for updates") - } - } - - func openWiki() { - NSWorkspace.shared.open(Self.rimeWikiURL) - } - - static func showMessage(msgText: String?) { - let center = UNUserNotificationCenter.current() - center.requestAuthorization(options: [.alert, .provisional]) { _, error in - if let error = error { - print("User notification authorization error: \(error.localizedDescription)") - } - } - center.getNotificationSettings { settings in - if (settings.authorizationStatus == .authorized || settings.authorizationStatus == .provisional) && settings.alertSetting == .enabled { - let content = UNMutableNotificationContent() - content.title = NSLocalizedString("Squirrel", comment: "") - if let msgText = msgText { - content.subtitle = msgText - } - content.interruptionLevel = .active - let request = UNNotificationRequest(identifier: Self.notificationIdentifier, content: content, trigger: nil) - center.add(request) { error in - if let error = error { - print("User notification request error: \(error.localizedDescription)") - } - } - } - } - } - - func setupRime() { - createDirIfNotExist(path: SquirrelApp.userDir) - createDirIfNotExist(path: SquirrelApp.logDir) - // swiftlint:disable identifier_name - let notification_handler: @convention(c) (UnsafeMutableRawPointer?, RimeSessionId, UnsafePointer?, UnsafePointer?) -> Void = notificationHandler - let context_object = Unmanaged.passUnretained(self).toOpaque() - // swiftlint:enable identifier_name - rimeAPI.set_notification_handler(notification_handler, context_object) - - var squirrelTraits = RimeTraits.rimeStructInit() - squirrelTraits.setCString(Bundle.main.sharedSupportPath!, to: \.shared_data_dir) - squirrelTraits.setCString(SquirrelApp.userDir.path(), to: \.user_data_dir) - squirrelTraits.setCString(SquirrelApp.logDir.path(), to: \.log_dir) - squirrelTraits.setCString("Squirrel", to: \.distribution_code_name) - squirrelTraits.setCString("鼠鬚管", to: \.distribution_name) - squirrelTraits.setCString(Bundle.main.object(forInfoDictionaryKey: kCFBundleVersionKey as String) as! String, to: \.distribution_version) - squirrelTraits.setCString("rime.squirrel", to: \.app_name) - rimeAPI.setup(&squirrelTraits) - } - - func startRime(fullCheck: Bool) { - print("Initializing la rime...") - rimeAPI.initialize(nil) - // check for configuration updates - if rimeAPI.start_maintenance(fullCheck) { - // update squirrel config - // print("[DEBUG] maintenance suceeds") - _ = rimeAPI.deploy_config_file("squirrel.yaml", "config_version") - } else { - // print("[DEBUG] maintenance fails") - } - } - - func loadSettings() { - config = SquirrelConfig() - if !config!.openBaseConfig() { - return - } - - enableNotifications = config!.getString("show_notifications_when") != "never" - if let panel = panel, let config = self.config { - panel.load(config: config, forDarkMode: false) - panel.load(config: config, forDarkMode: true) - } - } - - func loadSettings(for schemaID: String) { - if schemaID.count == 0 || schemaID.first == "." { - return - } - let schema = SquirrelConfig() - if let panel = panel, let config = self.config { - if schema.open(schemaID: schemaID, baseConfig: config) && schema.has(section: "style") { - panel.load(config: schema, forDarkMode: false) - panel.load(config: schema, forDarkMode: true) - } else { - panel.load(config: config, forDarkMode: false) - panel.load(config: config, forDarkMode: true) - } - } - schema.close() - } - - // prevent freezing the system - func problematicLaunchDetected() -> Bool { - var detected = false - let logFile = FileManager.default.temporaryDirectory.appendingPathComponent("squirrel_launch.json", conformingTo: .json) - // print("[DEBUG] archive: \(logFile)") - do { - let archive = try Data(contentsOf: logFile, options: [.uncached]) - let decoder = JSONDecoder() - decoder.dateDecodingStrategy = .millisecondsSince1970 - let previousLaunch = try decoder.decode(Date.self, from: archive) - if previousLaunch.timeIntervalSinceNow >= -2 { - detected = true - } - } catch let error as NSError where error.domain == NSCocoaErrorDomain && error.code == NSFileReadNoSuchFileError { - - } catch { - print("Error occurred during processing launch time archive: \(error.localizedDescription)") - return detected - } - do { - let encoder = JSONEncoder() - encoder.dateEncodingStrategy = .millisecondsSince1970 - let record = try encoder.encode(Date.now) - try record.write(to: logFile) - } catch { - print("Error occurred during saving launch time to archive: \(error.localizedDescription)") - } - return detected + GlobalContext.shared.panel?.hide() } // add an awakeFromNib item so that we can set the action method. Note that @@ -229,98 +73,58 @@ final class SquirrelApplicationDelegate: NSObject, NSApplicationDelegate, SPUSta func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply { print("Squirrel is quitting.") - rimeAPI.cleanup_all_sessions() + GlobalContext.shared.rimeAPI.cleanup_all_sessions() return .terminateNow } - } -private func notificationHandler(contextObject: UnsafeMutableRawPointer?, sessionId: RimeSessionId, messageTypeC: UnsafePointer?, messageValueC: UnsafePointer?) { - let delegate: SquirrelApplicationDelegate = Unmanaged.fromOpaque(contextObject!).takeUnretainedValue() - - let messageType = messageTypeC.map { String(cString: $0) } - let messageValue = messageValueC.map { String(cString: $0) } - if messageType == "deploy" { - switch messageValue { - case "start": - SquirrelApplicationDelegate.showMessage(msgText: NSLocalizedString("deploy_start", comment: "")) - case "success": - SquirrelApplicationDelegate.showMessage(msgText: NSLocalizedString("deploy_success", comment: "")) - case "failure": - SquirrelApplicationDelegate.showMessage(msgText: NSLocalizedString("deploy_failure", comment: "")) - default: - break - } - return +extension SquirrelApplicationDelegate: MenuActions { + @objc func syncUserData() { + print("Sync user data") + _ = GlobalContext.shared.rimeAPI.sync_user_data() } - // off - if !delegate.enableNotifications { - return + + @objc func openLogFolder() { + NSWorkspace.shared.open(GlobalContext.Path.logDir) } - if messageType == "schema", let messageValue = messageValue, let schemaName = try? /^[^\/]*\/(.*)$/.firstMatch(in: messageValue)?.output.1 { - delegate.showStatusMessage(msgTextLong: String(schemaName), msgTextShort: String(schemaName)) - return - } else if messageType == "option" { - let state = messageValue?.first != "!" - let optionName = if state { - messageValue - } else { - String(messageValue![messageValue!.index(after: messageValue!.startIndex)...]) - } - if let optionName = optionName { - optionName.withCString { name in - let stateLabelLong = delegate.rimeAPI.get_state_label_abbreviated(sessionId, name, state, false) - let stateLabelShort = delegate.rimeAPI.get_state_label_abbreviated(sessionId, name, state, true) - let longLabel = stateLabelLong.str.map { String(cString: $0) } - let shortLabel = stateLabelShort.str.map { String(cString: $0) } - delegate.showStatusMessage(msgTextLong: longLabel, msgTextShort: shortLabel) - } - } + @objc func openRimeFolder() { + NSWorkspace.shared.open(GlobalContext.Path.userDir) } -} -private extension SquirrelApplicationDelegate { - func showStatusMessage(msgTextLong: String?, msgTextShort: String?) { - if !(msgTextLong ?? "").isEmpty || !(msgTextShort ?? "").isEmpty { - panel?.updateStatus(long: msgTextLong ?? "", short: msgTextShort ?? "") + @objc func checkForUpdates() { + if updateController.updater.canCheckForUpdates { + print("Checking for updates") + updateController.updater.checkForUpdates() + } else { + print("Cannot check for updates") } } - func shutdownRime() { - config?.close() - rimeAPI.finalize() + @objc func openWiki() { + NSWorkspace.shared.open(Self.rimeWikiURL) } +} +private extension SquirrelApplicationDelegate { func workspaceWillPowerOff(_: Notification) { print("Finalizing before logging out.") - self.shutdownRime() + GlobalContext.shared.shutdownRime() } func rimeNeedsReload(_: Notification) { print("Reloading rime on demand.") - self.deploy() + GlobalContext.shared.deploy() } func rimeNeedsSync(_: Notification) { print("Sync rime on demand.") self.syncUserData() } - - func createDirIfNotExist(path: URL) { - let fileManager = FileManager.default - if !fileManager.fileExists(atPath: path.path()) { - do { - try fileManager.createDirectory(at: path, withIntermediateDirectories: true) - } catch { - print("Error creating user data directory: \(path.path())") - } - } - } } extension NSApplication { - var squirrelAppDelegate: SquirrelApplicationDelegate { - self.delegate as! SquirrelApplicationDelegate + var squirrelAppDelegate: SquirrelApplicationDelegate? { + self.delegate as? SquirrelApplicationDelegate } } diff --git a/sources/SquirrelInputController.swift b/sources/SquirrelInputController.swift index b835f423f..45e12b59e 100644 --- a/sources/SquirrelInputController.swift +++ b/sources/SquirrelInputController.swift @@ -7,6 +7,18 @@ import InputMethodKit +@objc protocol MenuActions { + @objc func syncUserData() + @objc func openLogFolder() + + + @objc func openRimeFolder() + + @objc func checkForUpdates() + + @objc func openWiki() +} + final class SquirrelInputController: IMKInputController { private static let keyRollOver = 50 private static var unknownAppCnt: UInt = 0 @@ -170,7 +182,7 @@ final class SquirrelInputController: IMKInputController { override func activateServer(_ sender: Any!) { self.client ?= sender as? IMKTextInput // print("[DEBUG] activateServer:") - var keyboardLayout = NSApp.squirrelAppDelegate.config?.getString("keyboard_layout") ?? "" + var keyboardLayout = GlobalContext.shared.config?.getString("keyboard_layout") ?? "" if keyboardLayout == "last" || keyboardLayout == "" { keyboardLayout = "" } else if keyboardLayout == "default" { @@ -199,7 +211,7 @@ final class SquirrelInputController: IMKInputController { } override func hidePalettes() { - NSApp.squirrelAppDelegate.panel?.hide() + GlobalContext.shared.panel?.hide() super.hidePalettes() } @@ -229,15 +241,15 @@ final class SquirrelInputController: IMKInputController { let deploy = NSMenuItem(title: NSLocalizedString("Deploy", comment: "Menu item"), action: #selector(deploy), keyEquivalent: "`") deploy.target = self deploy.keyEquivalentModifierMask = [.control, .option] - let sync = NSMenuItem(title: NSLocalizedString("Sync user data", comment: "Menu item"), action: #selector(syncUserData), keyEquivalent: "") + let sync = NSMenuItem(title: NSLocalizedString("Sync user data", comment: "Menu item"), action: #selector(MenuActions.syncUserData), keyEquivalent: "") sync.target = self - let logDir = NSMenuItem(title: NSLocalizedString("Logs...", comment: "Menu item"), action: #selector(openLogFolder), keyEquivalent: "") + let logDir = NSMenuItem(title: NSLocalizedString("Logs...", comment: "Menu item"), action: #selector(MenuActions.openLogFolder), keyEquivalent: "") logDir.target = self - let setting = NSMenuItem(title: NSLocalizedString("Settings...", comment: "Menu item"), action: #selector(openRimeFolder), keyEquivalent: "") + let setting = NSMenuItem(title: NSLocalizedString("Settings...", comment: "Menu item"), action: #selector(MenuActions.openRimeFolder), keyEquivalent: "") setting.target = self - let wiki = NSMenuItem(title: NSLocalizedString("Rime Wiki...", comment: "Menu item"), action: #selector(openWiki), keyEquivalent: "") + let wiki = NSMenuItem(title: NSLocalizedString("Rime Wiki...", comment: "Menu item"), action: #selector(MenuActions.openWiki), keyEquivalent: "") wiki.target = self - let update = NSMenuItem(title: NSLocalizedString("Check for updates...", comment: "Menu item"), action: #selector(checkForUpdates), keyEquivalent: "") + let update = NSMenuItem(title: NSLocalizedString("Check for updates...", comment: "Menu item"), action: #selector(MenuActions.checkForUpdates), keyEquivalent: "") update.target = self let menu = NSMenu() @@ -252,27 +264,7 @@ final class SquirrelInputController: IMKInputController { } @objc func deploy() { - NSApp.squirrelAppDelegate.deploy() - } - - @objc func syncUserData() { - NSApp.squirrelAppDelegate.syncUserData() - } - - @objc func openLogFolder() { - NSApp.squirrelAppDelegate.openLogFolder() - } - - @objc func openRimeFolder() { - NSApp.squirrelAppDelegate.openRimeFolder() - } - - @objc func checkForUpdates() { - NSApp.squirrelAppDelegate.checkForUpdates() - } - - @objc func openWiki() { - NSApp.squirrelAppDelegate.openWiki() + GlobalContext.shared.deploy() } deinit { @@ -317,7 +309,7 @@ private extension SquirrelInputController { timer.invalidate() } chordDuration = 0.1 - if let duration = NSApp.squirrelAppDelegate.config?.getDouble("chord_duration"), duration > 0 { + if let duration = GlobalContext.shared.config?.getDouble("chord_duration"), duration > 0 { chordDuration = duration } chordTimer = Timer.scheduledTimer(withTimeInterval: chordDuration, repeats: false, block: onChordTimer) @@ -352,7 +344,7 @@ private extension SquirrelInputController { if currentApp == "" { return } - if let appOptions = NSApp.squirrelAppDelegate.config?.getAppOptions(currentApp) { + if let appOptions = GlobalContext.shared.config?.getAppOptions(currentApp) { for (key, value) in appOptions { print("set app option: \(key) = \(value)") rimeAPI.set_option(session, key, value) @@ -373,7 +365,7 @@ private extension SquirrelInputController { // TODO add special key event preprocessing here // with linear candidate list, arrow keys may behave differently. - if let panel = NSApp.squirrelAppDelegate.panel { + if let panel = GlobalContext.shared.panel { if panel.linear != rimeAPI.get_option(session, "_linear") { rimeAPI.set_option(session, "_linear", panel.linear) } @@ -434,9 +426,9 @@ private extension SquirrelInputController { // swiftlint:disable:next identifier_name if let schema_id = status.schema_id, schemaId == "" || schemaId != String(cString: schema_id) { schemaId = String(cString: schema_id) - NSApp.squirrelAppDelegate.loadSettings(for: schemaId) + GlobalContext.shared.loadSettings(for: schemaId) // inline preedit - if let panel = NSApp.squirrelAppDelegate.panel { + if let panel = GlobalContext.shared.panel { inlinePreedit = (panel.inlinePreedit && !rimeAPI.get_option(session, "no_inline")) || rimeAPI.get_option(session, "inline") inlineCandidate = panel.inlineCandidate && !rimeAPI.get_option(session, "no_inline") // if not inline, embed soft cursor in preedit string @@ -557,7 +549,7 @@ private extension SquirrelInputController { guard let client = client else { return } var inputPos = NSRect() client.attributes(forCharacterIndex: 0, lineHeightRectangle: &inputPos) - if let panel = NSApp.squirrelAppDelegate.panel { + if let panel = GlobalContext.shared.panel { panel.position = inputPos panel.inputController = self panel.update(preedit: preedit, selRange: selRange, caretPos: caretPos, candidates: candidates, comments: comments, labels: labels, From b2bfc1b5dff9537c4e027546bc4255c6b06e6f4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=84=E9=BB=9B=E6=B7=B3=E5=8D=8E?= Date: Sun, 1 Dec 2024 11:53:35 +0800 Subject: [PATCH 2/9] refactor: adding Demo to facilitate debug --- Demo/AppDelegate.swift | 43 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 58 ++ Demo/Assets.xcassets/Contents.json | 6 + Demo/Base.lproj/Main.storyboard | 754 ++++++++++++++++++ Demo/Demo.entitlements | 5 + Demo/TextView+Unused.swift | 67 ++ Demo/TextView.swift | 96 +++ Demo/ViewController.swift | 13 + Squirrel.xcodeproj/project.pbxproj | 173 +++- sources/GlobalContext.swift | 20 +- sources/SquirrelApplicationDelegate.swift | 3 +- sources/SquirrelInputController.swift | 58 +- 13 files changed, 1263 insertions(+), 44 deletions(-) create mode 100644 Demo/AppDelegate.swift create mode 100644 Demo/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 Demo/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Demo/Assets.xcassets/Contents.json create mode 100644 Demo/Base.lproj/Main.storyboard create mode 100644 Demo/Demo.entitlements create mode 100644 Demo/TextView+Unused.swift create mode 100644 Demo/TextView.swift create mode 100644 Demo/ViewController.swift diff --git a/Demo/AppDelegate.swift b/Demo/AppDelegate.swift new file mode 100644 index 000000000..078b26a92 --- /dev/null +++ b/Demo/AppDelegate.swift @@ -0,0 +1,43 @@ +// +// AppDelegate.swift +// Demo +// +// Created by mi on 2024/12/1. +// + +import Cocoa +import InputController + +@main +class AppDelegate: NSObject, NSApplicationDelegate { + func applicationDidFinishLaunching(_ aNotification: Notification) { + // Insert code here to initialize your application + + if GlobalContext.shared.problematicLaunchDetected() { + print("Problematic launch detected!") + let args = ["Problematic launch detected! Squirrel may be suffering a crash due to improper configuration. Revert previous modifications to see if the problem recurs."] + let task = Process() + task.executableURL = "/usr/bin/say".withCString { dir in + URL(fileURLWithFileSystemRepresentation: dir, isDirectory: false, relativeTo: nil) + } + task.arguments = args + try? task.run() + } else { + GlobalContext.shared.setupRime() + GlobalContext.shared.startRime(fullCheck: false) + GlobalContext.shared.loadSettings() + print("Squirrel reporting!") + } + } + + func applicationWillTerminate(_ aNotification: Notification) { + // Insert code here to tear down your application + } + + func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } + + +} + diff --git a/Demo/Assets.xcassets/AccentColor.colorset/Contents.json b/Demo/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 000000000..eb8789700 --- /dev/null +++ b/Demo/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Demo/Assets.xcassets/AppIcon.appiconset/Contents.json b/Demo/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..3f00db43e --- /dev/null +++ b/Demo/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,58 @@ +{ + "images" : [ + { + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Demo/Assets.xcassets/Contents.json b/Demo/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Demo/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Demo/Base.lproj/Main.storyboard b/Demo/Base.lproj/Main.storyboard new file mode 100644 index 000000000..bb3b5fe3a --- /dev/null +++ b/Demo/Base.lproj/Main.storyboard @@ -0,0 +1,754 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demo/Demo.entitlements b/Demo/Demo.entitlements new file mode 100644 index 000000000..0c67376eb --- /dev/null +++ b/Demo/Demo.entitlements @@ -0,0 +1,5 @@ + + + + + diff --git a/Demo/TextView+Unused.swift b/Demo/TextView+Unused.swift new file mode 100644 index 000000000..8634d7d7b --- /dev/null +++ b/Demo/TextView+Unused.swift @@ -0,0 +1,67 @@ +// +// Unused.swift +// Jyutping +// +// Created by mi on 2024/11/3. +// + +import Cocoa +import InputMethodKit + +extension Client { + func validAttributesForMarkedText() -> [Any]! { + fatalError() + } + + func selectedRange() -> NSRange { + fatalError() + } + + func markedRange() -> NSRange { + fatalError() + } + + func attributedSubstring(from range: NSRange) -> NSAttributedString! { + fatalError() + } + + func length() -> Int { + fatalError() + } + + func characterIndex(for point: NSPoint, tracking mappingMode: IMKLocationToOffsetMappingMode, inMarkedRange: UnsafeMutablePointer!) -> Int { + fatalError() + } + + func overrideKeyboard(withKeyboardNamed keyboardUniqueName: String!) { + fatalError() + } + + func selectMode(_ modeIdentifier: String!) { + fatalError() + } + + func supportsUnicode() -> Bool { + fatalError() + } + + func supportsProperty(_ property: TSMDocumentPropertyTag) -> Bool { + fatalError() + } + + func string(from range: NSRange, actualRange: NSRangePointer!) -> String! { + fatalError() + } + + func firstRect(forCharacterRange aRange: NSRange, actualRange: NSRangePointer!) -> NSRect { + fatalError() + } + + func windowLevel() -> CGWindowLevel { + fatalError() + } + + func uniqueClientIdentifierString() -> String! { + fatalError() + } +} diff --git a/Demo/TextView.swift b/Demo/TextView.swift new file mode 100644 index 000000000..04de8e130 --- /dev/null +++ b/Demo/TextView.swift @@ -0,0 +1,96 @@ +// +// TextView.swift +// Demo +// +// Created by mi on 2024/11/3. +// + +import Cocoa +import InputMethodKit + +extension NSTextView { + func currentCursorRect() -> NSRect? { + guard let selectedRange = self.selectedRanges.first as? NSRange else { + return nil + } + + var rect = NSRect.zero + self.layoutManager?.enumerateEnclosingRects( + forGlyphRange: selectedRange, + withinSelectedGlyphRange: selectedRange, + in: self.textContainer!, + using: { glyphRect, _ in + rect = glyphRect + } + ) + + return self.window?.convertToScreen(self.convert(rect, to: nil)) + } +} + +class Client: NSObject, IMKTextInput { + weak var textView: NSTextView? + init(textView: NSTextView?) { + self.textView = textView + } + + func bundleIdentifier() -> String! { + Bundle.main.bundleIdentifier + } + + func attributes(forCharacterIndex index: Int, lineHeightRectangle lineRect: UnsafeMutablePointer!) -> [AnyHashable : Any]! { + if let rect = textView?.currentCursorRect() { + lineRect.pointee = rect + } + return nil + } + var replacementRange: NSRange? + func setMarkedText(_ string: Any!, selectionRange: NSRange, replacementRange: NSRange) { + guard let textView else { + return + } + if let length = (string as? NSAttributedString)?.length, length == 0 { + if let replacementRange = self.replacementRange, replacementRange.length == 1 { + textView.insertText("", replacementRange: replacementRange) + } + self.replacementRange = nil + return + } + + guard let firstRange = textView.selectedRanges.first?.rangeValue else { + return + } + if self.replacementRange == nil { + self.replacementRange = firstRange + } + guard let replacementRange = self.replacementRange else { + return + } + + textView.setMarkedText(string as Any, selectedRange: selectionRange, replacementRange: replacementRange) + if let string = string as? String { + self.replacementRange?.length = string.count + } else if let string = string as? NSAttributedString { + self.replacementRange?.length = string.length + } + } + + func insertText(_ string: Any!, replacementRange: NSRange) { + guard let string = string as? String, let textView = textView, let replacementRange = self.replacementRange else { + return + } + textView.insertText(string, replacementRange: replacementRange) + } +} + +class TextView: NSTextView { + lazy var client = Client(textView: self) + lazy var inputController = (NSClassFromString("SquirrelInputController") as! IMKInputController.Type).init(server: nil, delegate: nil, client: client)! + + override func keyDown(with event: NSEvent) { + if inputController.handle(event, client: client) { + return + } + super.keyDown(with: event) + } +} diff --git a/Demo/ViewController.swift b/Demo/ViewController.swift new file mode 100644 index 000000000..996383220 --- /dev/null +++ b/Demo/ViewController.swift @@ -0,0 +1,13 @@ +// +// ViewController.swift +// Demo +// +// Created by mi on 2024/11/10. +// + +import Cocoa + +class ViewController: NSViewController { + @IBOutlet var textView: TextView! + +} diff --git a/Squirrel.xcodeproj/project.pbxproj b/Squirrel.xcodeproj/project.pbxproj index e143958f3..93eb2d892 100644 --- a/Squirrel.xcodeproj/project.pbxproj +++ b/Squirrel.xcodeproj/project.pbxproj @@ -13,7 +13,8 @@ 0DD1CB732CFBF53A00E6C847 /* FileManager+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DEE70632CFBF0FF0062CC96 /* FileManager+Extension.swift */; }; 0DD1CB742CFBF60700E6C847 /* SquirrelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B39771282BEDAF4A0093A49B /* SquirrelView.swift */; }; 0DD1CB762CFBFB6500E6C847 /* librime.1.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 44CD640915E2633D0021234E /* librime.1.dylib */; }; - 0DD1CBD12CFC00CE00E6C847 /* librime.1.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 44CD640915E2633D0021234E /* librime.1.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 0DD1CBD12CFC00CE00E6C847 /* librime.1.dylib in Copy Shared Frameworks */ = {isa = PBXBuildFile; fileRef = 44CD640915E2633D0021234E /* librime.1.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 0DD1CBE92CFC013200E6C847 /* InputController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0DEE704F2CFBEC6C0062CC96 /* InputController.framework */; }; 0DEE705C2CFBEDAC0062CC96 /* SquirrelInputController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B38E9B942BEAFEFD0036ABEF /* SquirrelInputController.swift */; }; 0DEE705D2CFBEE170062CC96 /* BridgingFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B35D2FE72BF00839009D156B /* BridgingFunctions.swift */; }; 0DEE705E2CFBEE2D0062CC96 /* MacOSKeyCodes.swift in Sources */ = {isa = PBXBuildFile; fileRef = B39771222BECEA150093A49B /* MacOSKeyCodes.swift */; }; @@ -103,14 +104,15 @@ /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ - 0DD1CBD02CFC00C600E6C847 /* CopyFiles */ = { + 0DD1CBD02CFC00C600E6C847 /* Copy Shared Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( - 0DD1CBD12CFC00CE00E6C847 /* librime.1.dylib in CopyFiles */, + 0DD1CBD12CFC00CE00E6C847 /* librime.1.dylib in Copy Shared Frameworks */, ); + name = "Copy Shared Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; 2C6B9F992BCD083D00E327DF /* Copy Rime plugins */ = { @@ -225,6 +227,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 0DD1CBD62CFC00ED00E6C847 /* Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Demo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 0DEE704F2CFBEC6C0062CC96 /* InputController.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = InputController.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 0DEE70632CFBF0FF0062CC96 /* FileManager+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileManager+Extension.swift"; sourceTree = ""; }; 0DEE70652CFBF2030062CC96 /* GlobalContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalContext.swift; sourceTree = ""; }; @@ -328,10 +331,19 @@ /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ + 0DD1CBD72CFC00ED00E6C847 /* Demo */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = Demo; sourceTree = ""; }; 0DEE70502CFBEC6C0062CC96 /* InputController */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (0DEE70592CFBEC6C0062CC96 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = InputController; sourceTree = ""; }; /* End PBXFileSystemSynchronizedRootGroup section */ /* Begin PBXFrameworksBuildPhase section */ + 0DD1CBD32CFC00ED00E6C847 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0DD1CBE92CFC013200E6C847 /* InputController.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 0DEE704C2CFBEC6C0062CC96 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -401,6 +413,7 @@ children = ( 44F1EB381431F8270015FD04 /* Squirrel.app */, 0DEE704F2CFBEC6C0062CC96 /* InputController.framework */, + 0DD1CBD62CFC00ED00E6C847 /* Demo.app */, ); name = Products; sourceTree = ""; @@ -416,6 +429,7 @@ 080E96DDFE201D6D7F000001 /* Sources */, 29B97317FDCFA39411CA2CEA /* Resources */, 0DEE70502CFBEC6C0062CC96 /* InputController */, + 0DD1CBD72CFC00ED00E6C847 /* Demo */, 29B97323FDCFA39411CA2CEA /* Frameworks */, 19C28FACFE9D520D11CA2CBB /* Products */, ); @@ -550,6 +564,30 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ + 0DD1CBD52CFC00ED00E6C847 /* Demo */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0DD1CBE22CFC00EE00E6C847 /* Build configuration list for PBXNativeTarget "Demo" */; + buildPhases = ( + 0DD1CBD22CFC00ED00E6C847 /* Sources */, + 0DD1CBD32CFC00ED00E6C847 /* Frameworks */, + 0DD1CBD42CFC00ED00E6C847 /* Resources */, + 44DA7A1614DD581B00C1ED3B /* Copy Shared Support Files */, + 4407F3CA14EC079A001329FE /* Copy opencc Files */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 0DD1CBD72CFC00ED00E6C847 /* Demo */, + ); + name = Demo; + packageProductDependencies = ( + ); + productName = Demo; + productReference = 0DD1CBD62CFC00ED00E6C847 /* Demo.app */; + productType = "com.apple.product-type.application"; + }; 0DEE704E2CFBEC6C0062CC96 /* InputController */ = { isa = PBXNativeTarget; buildConfigurationList = 0DEE705A2CFBEC6C0062CC96 /* Build configuration list for PBXNativeTarget "InputController" */; @@ -558,7 +596,7 @@ 0DEE704B2CFBEC6C0062CC96 /* Sources */, 0DEE704C2CFBEC6C0062CC96 /* Frameworks */, 0DEE704D2CFBEC6C0062CC96 /* Resources */, - 0DD1CBD02CFC00C600E6C847 /* CopyFiles */, + 0DD1CBD02CFC00C600E6C847 /* Copy Shared Frameworks */, ); buildRules = ( ); @@ -606,8 +644,12 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; + LastSwiftUpdateCheck = 1600; LastUpgradeCheck = 1540; TargetAttributes = { + 0DD1CBD52CFC00ED00E6C847 = { + CreatedOnToolsVersion = 16.0; + }; 0DEE704E2CFBEC6C0062CC96 = { CreatedOnToolsVersion = 16.0; }; @@ -634,11 +676,19 @@ targets = ( 8D1107260486CEB800E47090 /* Squirrel */, 0DEE704E2CFBEC6C0062CC96 /* InputController */, + 0DD1CBD52CFC00ED00E6C847 /* Demo */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 0DD1CBD42CFC00ED00E6C847 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 0DEE704D2CFBEC6C0062CC96 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -662,6 +712,13 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 0DD1CBD22CFC00ED00E6C847 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 0DEE704B2CFBEC6C0062CC96 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -700,6 +757,103 @@ /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ + 0DD1CBE32CFC00EE00E6C847 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = Demo/Demo.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INFOPLIST_KEY_NSMainStoryboardFile = Main; + INFOPLIST_KEY_NSPrincipalClass = NSApplication; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 15.0; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = im.rime.inputmethod.Squirrel.Demo; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 0DD1CBE42CFC00EE00E6C847 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = Demo/Demo.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INFOPLIST_KEY_NSMainStoryboardFile = Main; + INFOPLIST_KEY_NSPrincipalClass = NSApplication; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 15.0; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = im.rime.inputmethod.Squirrel.Demo; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; 0DEE70572CFBEC6C0062CC96 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -866,6 +1020,7 @@ "$(OTHER_CPLUSPLUSFLAGS_QUOTED_FOR_TARGET_2)", ); OTHER_LDFLAGS = "-lrime.1"; + OTHER_SWIFT_FLAGS = "-D InputMethod"; PRODUCT_BUNDLE_IDENTIFIER = im.rime.inputmethod.Squirrel; PRODUCT_NAME = Squirrel; SDKROOT = macosx; @@ -920,6 +1075,7 @@ "$(OTHER_CPLUSPLUSFLAGS_QUOTED_FOR_TARGET_2)", ); OTHER_LDFLAGS = "-lrime.1"; + OTHER_SWIFT_FLAGS = "-D InputMethod"; PRODUCT_BUNDLE_IDENTIFIER = im.rime.inputmethod.Squirrel; PRODUCT_NAME = Squirrel; SDKROOT = macosx; @@ -1051,6 +1207,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 0DD1CBE22CFC00EE00E6C847 /* Build configuration list for PBXNativeTarget "Demo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0DD1CBE32CFC00EE00E6C847 /* Debug */, + 0DD1CBE42CFC00EE00E6C847 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 0DEE705A2CFBEC6C0062CC96 /* Build configuration list for PBXNativeTarget "InputController" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/sources/GlobalContext.swift b/sources/GlobalContext.swift index b4448a6a0..6f0e8f6db 100644 --- a/sources/GlobalContext.swift +++ b/sources/GlobalContext.swift @@ -8,12 +8,12 @@ import Foundation import UserNotifications -final class GlobalContext { - static let shared = GlobalContext() +public final class GlobalContext { + public static let shared = GlobalContext() let rimeAPI: RimeApi_stdbool = rime_get_api_stdbool().pointee var config: SquirrelConfig? - var panel: SquirrelPanel? + lazy var panel = SquirrelPanel(position: .zero) var enableNotifications = false static let notificationIdentifier = "SquirrelNotification" @@ -30,7 +30,7 @@ final class GlobalContext { static let logDir = FileManager.default.temporaryDirectory.appending(component: "rime.squirrel", directoryHint: .isDirectory) } - func setupRime() { + public func setupRime() { FileManager.default.createDirIfNotExist(path: Path.userDir) FileManager.default.createDirIfNotExist(path: Path.logDir) // swiftlint:disable identifier_name @@ -50,7 +50,7 @@ final class GlobalContext { rimeAPI.setup(&squirrelTraits) } - func startRime(fullCheck: Bool) { + public func startRime(fullCheck: Bool) { print("Initializing la rime...") rimeAPI.initialize(nil) // check for configuration updates @@ -63,14 +63,14 @@ final class GlobalContext { } } - func loadSettings() { + public func loadSettings() { config = SquirrelConfig() if !config!.openBaseConfig() { return } enableNotifications = config!.getString("show_notifications_when") != "never" - if let panel = panel, let config = self.config { + if let config = self.config { panel.load(config: config, forDarkMode: false) panel.load(config: config, forDarkMode: true) } @@ -81,7 +81,7 @@ final class GlobalContext { return } let schema = SquirrelConfig() - if let panel = panel, let config = self.config { + if let config = self.config { if schema.open(schemaID: schemaID, baseConfig: config) && schema.has(section: "style") { panel.load(config: schema, forDarkMode: false) panel.load(config: schema, forDarkMode: true) @@ -101,7 +101,7 @@ final class GlobalContext { } // prevent freezing the system - func problematicLaunchDetected() -> Bool { + public func problematicLaunchDetected() -> Bool { var detected = false let logFile = FileManager.default.temporaryDirectory.appendingPathComponent("squirrel_launch.json", conformingTo: .json) // print("[DEBUG] archive: \(logFile)") @@ -132,7 +132,7 @@ final class GlobalContext { func showStatusMessage(msgTextLong: String?, msgTextShort: String?) { if !(msgTextLong ?? "").isEmpty || !(msgTextShort ?? "").isEmpty { - panel?.updateStatus(long: msgTextLong ?? "", short: msgTextShort ?? "") + panel.updateStatus(long: msgTextLong ?? "", short: msgTextShort ?? "") } } diff --git a/sources/SquirrelApplicationDelegate.swift b/sources/SquirrelApplicationDelegate.swift index 20fe0faac..5b6c6fdcf 100644 --- a/sources/SquirrelApplicationDelegate.swift +++ b/sources/SquirrelApplicationDelegate.swift @@ -48,7 +48,6 @@ final class SquirrelApplicationDelegate: NSObject, NSApplicationDelegate, SPUSta } func applicationWillFinishLaunching(_ notification: Notification) { - GlobalContext.shared.panel = SquirrelPanel(position: .zero) addObservers() } @@ -56,7 +55,7 @@ final class SquirrelApplicationDelegate: NSObject, NSApplicationDelegate, SPUSta // swiftlint:disable:next notification_center_detachment NotificationCenter.default.removeObserver(self) DistributedNotificationCenter.default().removeObserver(self) - GlobalContext.shared.panel?.hide() + GlobalContext.shared.panel.hide() } // add an awakeFromNib item so that we can set the action method. Note that diff --git a/sources/SquirrelInputController.swift b/sources/SquirrelInputController.swift index 45e12b59e..7f1cc615c 100644 --- a/sources/SquirrelInputController.swift +++ b/sources/SquirrelInputController.swift @@ -19,6 +19,7 @@ import InputMethodKit @objc func openWiki() } +@objc(SquirrelInputController) final class SquirrelInputController: IMKInputController { private static let keyRollOver = 50 private static var unknownAppCnt: UInt = 0 @@ -42,7 +43,7 @@ final class SquirrelInputController: IMKInputController { private var currentApp: String = "" // swiftlint:disable:next cyclomatic_complexity - override func handle(_ event: NSEvent!, client sender: Any!) -> Bool { + public override func handle(_ event: NSEvent!, client sender: Any!) -> Bool { guard let event = event else { return false } let modifiers = event.modifierFlags let changes = lastModifiers.symmetricDifference(modifiers) @@ -174,12 +175,12 @@ final class SquirrelInputController: IMKInputController { return true } - override func recognizedEvents(_ sender: Any!) -> Int { + public override func recognizedEvents(_ sender: Any!) -> Int { // print("[DEBUG] recognizedEvents:") return Int(NSEvent.EventTypeMask.Element(arrayLiteral: .keyDown, .flagsChanged).rawValue) } - override func activateServer(_ sender: Any!) { + public override func activateServer(_ sender: Any!) { self.client ?= sender as? IMKTextInput // print("[DEBUG] activateServer:") var keyboardLayout = GlobalContext.shared.config?.getString("keyboard_layout") ?? "" @@ -199,19 +200,23 @@ final class SquirrelInputController: IMKInputController { override init!(server: IMKServer!, delegate: Any!, client: Any!) { self.client = client as? IMKTextInput // print("[DEBUG] initWithServer: \(server ?? .init()) delegate: \(delegate ?? "nil") client:\(client ?? "nil")") +#if InputMethod super.init(server: server, delegate: delegate, client: client) +#else + super.init() +#endif createSession() } - override func deactivateServer(_ sender: Any!) { + public override func deactivateServer(_ sender: Any!) { // print("[DEBUG] deactivateServer: \(sender ?? "nil")") hidePalettes() commitComposition(sender) client = nil } - override func hidePalettes() { - GlobalContext.shared.panel?.hide() + public override func hidePalettes() { + GlobalContext.shared.panel.hide() super.hidePalettes() } @@ -225,7 +230,7 @@ final class SquirrelInputController: IMKInputController { insertText:replacementRange:. Additionally, this is the time to clean up if that is necessary. */ - override func commitComposition(_ sender: Any!) { + public override func commitComposition(_ sender: Any!) { self.client ?= sender as? IMKTextInput // print("[DEBUG] commitComposition: \(sender ?? "nil")") // commit raw input @@ -237,7 +242,7 @@ final class SquirrelInputController: IMKInputController { } } - override func menu() -> NSMenu! { + public override func menu() -> NSMenu! { let deploy = NSMenuItem(title: NSLocalizedString("Deploy", comment: "Menu item"), action: #selector(deploy), keyEquivalent: "`") deploy.target = self deploy.keyEquivalentModifierMask = [.control, .option] @@ -365,14 +370,13 @@ private extension SquirrelInputController { // TODO add special key event preprocessing here // with linear candidate list, arrow keys may behave differently. - if let panel = GlobalContext.shared.panel { - if panel.linear != rimeAPI.get_option(session, "_linear") { - rimeAPI.set_option(session, "_linear", panel.linear) - } - // with vertical text, arrow keys may behave differently. - if panel.vertical != rimeAPI.get_option(session, "_vertical") { - rimeAPI.set_option(session, "_vertical", panel.vertical) - } + let panel = GlobalContext.shared.panel + if panel.linear != rimeAPI.get_option(session, "_linear") { + rimeAPI.set_option(session, "_linear", panel.linear) + } + // with vertical text, arrow keys may behave differently. + if panel.vertical != rimeAPI.get_option(session, "_vertical") { + rimeAPI.set_option(session, "_vertical", panel.vertical) } let handled = rimeAPI.process_key(session, Int32(rimeKeycode), Int32(rimeModifiers)) @@ -428,12 +432,11 @@ private extension SquirrelInputController { schemaId = String(cString: schema_id) GlobalContext.shared.loadSettings(for: schemaId) // inline preedit - if let panel = GlobalContext.shared.panel { - inlinePreedit = (panel.inlinePreedit && !rimeAPI.get_option(session, "no_inline")) || rimeAPI.get_option(session, "inline") - inlineCandidate = panel.inlineCandidate && !rimeAPI.get_option(session, "no_inline") - // if not inline, embed soft cursor in preedit string - rimeAPI.set_option(session, "soft_cursor", !inlinePreedit) - } + let panel = GlobalContext.shared.panel + inlinePreedit = (panel.inlinePreedit && !rimeAPI.get_option(session, "no_inline")) || rimeAPI.get_option(session, "inline") + inlineCandidate = panel.inlineCandidate && !rimeAPI.get_option(session, "no_inline") + // if not inline, embed soft cursor in preedit string + rimeAPI.set_option(session, "soft_cursor", !inlinePreedit) } _ = rimeAPI.free_status(&status) } @@ -549,11 +552,10 @@ private extension SquirrelInputController { guard let client = client else { return } var inputPos = NSRect() client.attributes(forCharacterIndex: 0, lineHeightRectangle: &inputPos) - if let panel = GlobalContext.shared.panel { - panel.position = inputPos - panel.inputController = self - panel.update(preedit: preedit, selRange: selRange, caretPos: caretPos, candidates: candidates, comments: comments, labels: labels, - highlighted: highlighted, page: page, lastPage: lastPage, update: true) - } + let panel = GlobalContext.shared.panel + panel.position = inputPos + panel.inputController = self + panel.update(preedit: preedit, selRange: selRange, caretPos: caretPos, candidates: candidates, comments: comments, labels: labels, + highlighted: highlighted, page: page, lastPage: lastPage, update: true) } } From 8f06cf547d376e7296a9a34a60401a2920a624d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=84=E9=BB=9B=E6=B7=B3=E5=8D=8E?= Date: Sun, 1 Dec 2024 13:05:57 +0800 Subject: [PATCH 3/9] fix: menu's target is not working --- sources/SquirrelApplicationDelegate.swift | 16 ++++--- sources/SquirrelInputController.swift | 51 ++++++++++++++++------- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/sources/SquirrelApplicationDelegate.swift b/sources/SquirrelApplicationDelegate.swift index 5b6c6fdcf..494ffd473 100644 --- a/sources/SquirrelApplicationDelegate.swift +++ b/sources/SquirrelApplicationDelegate.swift @@ -78,20 +78,24 @@ final class SquirrelApplicationDelegate: NSObject, NSApplicationDelegate, SPUSta } extension SquirrelApplicationDelegate: MenuActions { - @objc func syncUserData() { + func deploy() { + GlobalContext.shared.deploy() + } + + func syncUserData() { print("Sync user data") _ = GlobalContext.shared.rimeAPI.sync_user_data() } - @objc func openLogFolder() { + func openLogFolder() { NSWorkspace.shared.open(GlobalContext.Path.logDir) } - @objc func openRimeFolder() { + func openRimeFolder() { NSWorkspace.shared.open(GlobalContext.Path.userDir) } - @objc func checkForUpdates() { + func checkForUpdates() { if updateController.updater.canCheckForUpdates { print("Checking for updates") updateController.updater.checkForUpdates() @@ -100,8 +104,8 @@ extension SquirrelApplicationDelegate: MenuActions { } } - @objc func openWiki() { - NSWorkspace.shared.open(Self.rimeWikiURL) + func openWiki() { + NSWorkspace.shared.open(SquirrelApplicationDelegate.rimeWikiURL) } } diff --git a/sources/SquirrelInputController.swift b/sources/SquirrelInputController.swift index 7f1cc615c..dc74557b0 100644 --- a/sources/SquirrelInputController.swift +++ b/sources/SquirrelInputController.swift @@ -8,15 +8,12 @@ import InputMethodKit @objc protocol MenuActions { - @objc func syncUserData() - @objc func openLogFolder() - - - @objc func openRimeFolder() - - @objc func checkForUpdates() - - @objc func openWiki() + func deploy() + func syncUserData() + func openLogFolder() + func openRimeFolder() + func checkForUpdates() + func openWiki() } @objc(SquirrelInputController) @@ -243,7 +240,8 @@ final class SquirrelInputController: IMKInputController { } public override func menu() -> NSMenu! { - let deploy = NSMenuItem(title: NSLocalizedString("Deploy", comment: "Menu item"), action: #selector(deploy), keyEquivalent: "`") + // Since the action will be called from IMKInputController.doCommandBySelector, the target will/must be self + let deploy = NSMenuItem(title: NSLocalizedString("Deploy", comment: "Menu item"), action: #selector(MenuActions.deploy), keyEquivalent: "`") deploy.target = self deploy.keyEquivalentModifierMask = [.control, .option] let sync = NSMenuItem(title: NSLocalizedString("Sync user data", comment: "Menu item"), action: #selector(MenuActions.syncUserData), keyEquivalent: "") @@ -268,15 +266,40 @@ final class SquirrelInputController: IMKInputController { return menu } - @objc func deploy() { - GlobalContext.shared.deploy() - } - deinit { destroySession() } } +extension SquirrelInputController: MenuActions { + private var handler: MenuActions? { + NSApplication.shared.delegate as? MenuActions + } + func deploy() { + handler?.deploy() + } + + func syncUserData() { + handler?.syncUserData() + } + + func openLogFolder() { + handler?.openLogFolder() + } + + func openRimeFolder() { + handler?.openRimeFolder() + } + + func checkForUpdates() { + handler?.checkForUpdates() + } + + func openWiki() { + handler?.openWiki() + } +} + private extension SquirrelInputController { func onChordTimer(_: Timer) { From 9357c2a7e5ee361fb2eb5a17eba21f363dc81d4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=84=E9=BB=9B=E6=B7=B3=E5=8D=8E?= Date: Sun, 1 Dec 2024 13:07:42 +0800 Subject: [PATCH 4/9] format --- Demo/AppDelegate.swift | 28 ++++---- Demo/TextView+Unused.swift | 98 +++++++++++++-------------- Demo/TextView.swift | 132 ++++++++++++++++++------------------- Demo/ViewController.swift | 4 +- 4 files changed, 131 insertions(+), 131 deletions(-) diff --git a/Demo/AppDelegate.swift b/Demo/AppDelegate.swift index 078b26a92..159abc85e 100644 --- a/Demo/AppDelegate.swift +++ b/Demo/AppDelegate.swift @@ -10,9 +10,9 @@ import InputController @main class AppDelegate: NSObject, NSApplicationDelegate { - func applicationDidFinishLaunching(_ aNotification: Notification) { - // Insert code here to initialize your application - + func applicationDidFinishLaunching(_ aNotification: Notification) { + // Insert code here to initialize your application + if GlobalContext.shared.problematicLaunchDetected() { print("Problematic launch detected!") let args = ["Problematic launch detected! Squirrel may be suffering a crash due to improper configuration. Revert previous modifications to see if the problem recurs."] @@ -28,16 +28,16 @@ class AppDelegate: NSObject, NSApplicationDelegate { GlobalContext.shared.loadSettings() print("Squirrel reporting!") } - } - - func applicationWillTerminate(_ aNotification: Notification) { - // Insert code here to tear down your application - } - - func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { - return true - } - - + } + + func applicationWillTerminate(_ aNotification: Notification) { + // Insert code here to tear down your application + } + + func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } + + } diff --git a/Demo/TextView+Unused.swift b/Demo/TextView+Unused.swift index 8634d7d7b..4bbf8bee5 100644 --- a/Demo/TextView+Unused.swift +++ b/Demo/TextView+Unused.swift @@ -9,58 +9,58 @@ import Cocoa import InputMethodKit extension Client { - func validAttributesForMarkedText() -> [Any]! { - fatalError() - } - - func selectedRange() -> NSRange { - fatalError() - } - - func markedRange() -> NSRange { - fatalError() - } - - func attributedSubstring(from range: NSRange) -> NSAttributedString! { - fatalError() - } - - func length() -> Int { - fatalError() - } - - func characterIndex(for point: NSPoint, tracking mappingMode: IMKLocationToOffsetMappingMode, inMarkedRange: UnsafeMutablePointer!) -> Int { - fatalError() - } - - func overrideKeyboard(withKeyboardNamed keyboardUniqueName: String!) { - fatalError() - } - - func selectMode(_ modeIdentifier: String!) { - fatalError() - } - - func supportsUnicode() -> Bool { - fatalError() - } - - func supportsProperty(_ property: TSMDocumentPropertyTag) -> Bool { - fatalError() - } - - func string(from range: NSRange, actualRange: NSRangePointer!) -> String! { - fatalError() - } - - func firstRect(forCharacterRange aRange: NSRange, actualRange: NSRangePointer!) -> NSRect { - fatalError() - } - + func validAttributesForMarkedText() -> [Any]! { + fatalError() + } + + func selectedRange() -> NSRange { + fatalError() + } + + func markedRange() -> NSRange { + fatalError() + } + + func attributedSubstring(from range: NSRange) -> NSAttributedString! { + fatalError() + } + + func length() -> Int { + fatalError() + } + + func characterIndex(for point: NSPoint, tracking mappingMode: IMKLocationToOffsetMappingMode, inMarkedRange: UnsafeMutablePointer!) -> Int { + fatalError() + } + + func overrideKeyboard(withKeyboardNamed keyboardUniqueName: String!) { + fatalError() + } + + func selectMode(_ modeIdentifier: String!) { + fatalError() + } + + func supportsUnicode() -> Bool { + fatalError() + } + + func supportsProperty(_ property: TSMDocumentPropertyTag) -> Bool { + fatalError() + } + + func string(from range: NSRange, actualRange: NSRangePointer!) -> String! { + fatalError() + } + + func firstRect(forCharacterRange aRange: NSRange, actualRange: NSRangePointer!) -> NSRect { + fatalError() + } + func windowLevel() -> CGWindowLevel { fatalError() } - + func uniqueClientIdentifierString() -> String! { fatalError() } diff --git a/Demo/TextView.swift b/Demo/TextView.swift index 04de8e130..7eb51e1b0 100644 --- a/Demo/TextView.swift +++ b/Demo/TextView.swift @@ -9,88 +9,88 @@ import Cocoa import InputMethodKit extension NSTextView { - func currentCursorRect() -> NSRect? { - guard let selectedRange = self.selectedRanges.first as? NSRange else { - return nil - } + func currentCursorRect() -> NSRect? { + guard let selectedRange = self.selectedRanges.first as? NSRange else { + return nil + } - var rect = NSRect.zero - self.layoutManager?.enumerateEnclosingRects( - forGlyphRange: selectedRange, - withinSelectedGlyphRange: selectedRange, - in: self.textContainer!, - using: { glyphRect, _ in - rect = glyphRect - } - ) + var rect = NSRect.zero + self.layoutManager?.enumerateEnclosingRects( + forGlyphRange: selectedRange, + withinSelectedGlyphRange: selectedRange, + in: self.textContainer!, + using: { glyphRect, _ in + rect = glyphRect + } + ) - return self.window?.convertToScreen(self.convert(rect, to: nil)) - } + return self.window?.convertToScreen(self.convert(rect, to: nil)) + } } class Client: NSObject, IMKTextInput { - weak var textView: NSTextView? - init(textView: NSTextView?) { - self.textView = textView - } + weak var textView: NSTextView? + init(textView: NSTextView?) { + self.textView = textView + } func bundleIdentifier() -> String! { Bundle.main.bundleIdentifier } - func attributes(forCharacterIndex index: Int, lineHeightRectangle lineRect: UnsafeMutablePointer!) -> [AnyHashable : Any]! { - if let rect = textView?.currentCursorRect() { - lineRect.pointee = rect - } - return nil - } - var replacementRange: NSRange? - func setMarkedText(_ string: Any!, selectionRange: NSRange, replacementRange: NSRange) { - guard let textView else { - return - } - if let length = (string as? NSAttributedString)?.length, length == 0 { - if let replacementRange = self.replacementRange, replacementRange.length == 1 { - textView.insertText("", replacementRange: replacementRange) - } - self.replacementRange = nil - return - } + func attributes(forCharacterIndex index: Int, lineHeightRectangle lineRect: UnsafeMutablePointer!) -> [AnyHashable : Any]! { + if let rect = textView?.currentCursorRect() { + lineRect.pointee = rect + } + return nil + } + var replacementRange: NSRange? + func setMarkedText(_ string: Any!, selectionRange: NSRange, replacementRange: NSRange) { + guard let textView else { + return + } + if let length = (string as? NSAttributedString)?.length, length == 0 { + if let replacementRange = self.replacementRange, replacementRange.length == 1 { + textView.insertText("", replacementRange: replacementRange) + } + self.replacementRange = nil + return + } - guard let firstRange = textView.selectedRanges.first?.rangeValue else { - return - } - if self.replacementRange == nil { - self.replacementRange = firstRange - } - guard let replacementRange = self.replacementRange else { - return - } + guard let firstRange = textView.selectedRanges.first?.rangeValue else { + return + } + if self.replacementRange == nil { + self.replacementRange = firstRange + } + guard let replacementRange = self.replacementRange else { + return + } textView.setMarkedText(string as Any, selectedRange: selectionRange, replacementRange: replacementRange) - if let string = string as? String { - self.replacementRange?.length = string.count - } else if let string = string as? NSAttributedString { - self.replacementRange?.length = string.length - } - } + if let string = string as? String { + self.replacementRange?.length = string.count + } else if let string = string as? NSAttributedString { + self.replacementRange?.length = string.length + } + } - func insertText(_ string: Any!, replacementRange: NSRange) { - guard let string = string as? String, let textView = textView, let replacementRange = self.replacementRange else { - return - } - textView.insertText(string, replacementRange: replacementRange) - } + func insertText(_ string: Any!, replacementRange: NSRange) { + guard let string = string as? String, let textView = textView, let replacementRange = self.replacementRange else { + return + } + textView.insertText(string, replacementRange: replacementRange) + } } class TextView: NSTextView { - lazy var client = Client(textView: self) - lazy var inputController = (NSClassFromString("SquirrelInputController") as! IMKInputController.Type).init(server: nil, delegate: nil, client: client)! + lazy var client = Client(textView: self) + lazy var inputController = (NSClassFromString("SquirrelInputController") as! IMKInputController.Type).init(server: nil, delegate: nil, client: client)! - override func keyDown(with event: NSEvent) { - if inputController.handle(event, client: client) { - return - } - super.keyDown(with: event) - } + override func keyDown(with event: NSEvent) { + if inputController.handle(event, client: client) { + return + } + super.keyDown(with: event) + } } diff --git a/Demo/ViewController.swift b/Demo/ViewController.swift index 996383220..d3b125759 100644 --- a/Demo/ViewController.swift +++ b/Demo/ViewController.swift @@ -8,6 +8,6 @@ import Cocoa class ViewController: NSViewController { - @IBOutlet var textView: TextView! - + @IBOutlet var textView: TextView! + } From 77c8717193d5efba5789134b091b6053bcc500a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=84=E9=BB=9B=E6=B7=B3=E5=8D=8E?= Date: Sun, 1 Dec 2024 13:17:15 +0800 Subject: [PATCH 5/9] remove public --- sources/SquirrelInputController.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sources/SquirrelInputController.swift b/sources/SquirrelInputController.swift index dc74557b0..4b3ca0a19 100644 --- a/sources/SquirrelInputController.swift +++ b/sources/SquirrelInputController.swift @@ -40,7 +40,7 @@ final class SquirrelInputController: IMKInputController { private var currentApp: String = "" // swiftlint:disable:next cyclomatic_complexity - public override func handle(_ event: NSEvent!, client sender: Any!) -> Bool { + override func handle(_ event: NSEvent!, client sender: Any!) -> Bool { guard let event = event else { return false } let modifiers = event.modifierFlags let changes = lastModifiers.symmetricDifference(modifiers) @@ -172,12 +172,12 @@ final class SquirrelInputController: IMKInputController { return true } - public override func recognizedEvents(_ sender: Any!) -> Int { + override func recognizedEvents(_ sender: Any!) -> Int { // print("[DEBUG] recognizedEvents:") return Int(NSEvent.EventTypeMask.Element(arrayLiteral: .keyDown, .flagsChanged).rawValue) } - public override func activateServer(_ sender: Any!) { + override func activateServer(_ sender: Any!) { self.client ?= sender as? IMKTextInput // print("[DEBUG] activateServer:") var keyboardLayout = GlobalContext.shared.config?.getString("keyboard_layout") ?? "" @@ -205,14 +205,14 @@ final class SquirrelInputController: IMKInputController { createSession() } - public override func deactivateServer(_ sender: Any!) { + override func deactivateServer(_ sender: Any!) { // print("[DEBUG] deactivateServer: \(sender ?? "nil")") hidePalettes() commitComposition(sender) client = nil } - public override func hidePalettes() { + override func hidePalettes() { GlobalContext.shared.panel.hide() super.hidePalettes() } @@ -227,7 +227,7 @@ final class SquirrelInputController: IMKInputController { insertText:replacementRange:. Additionally, this is the time to clean up if that is necessary. */ - public override func commitComposition(_ sender: Any!) { + override func commitComposition(_ sender: Any!) { self.client ?= sender as? IMKTextInput // print("[DEBUG] commitComposition: \(sender ?? "nil")") // commit raw input @@ -239,7 +239,7 @@ final class SquirrelInputController: IMKInputController { } } - public override func menu() -> NSMenu! { + override func menu() -> NSMenu! { // Since the action will be called from IMKInputController.doCommandBySelector, the target will/must be self let deploy = NSMenuItem(title: NSLocalizedString("Deploy", comment: "Menu item"), action: #selector(MenuActions.deploy), keyEquivalent: "`") deploy.target = self From 398c62e318f54bf4928d9f99c6ba018f8799a23d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=84=E9=BB=9B=E6=B7=B3=E5=8D=8E?= Date: Tue, 3 Dec 2024 21:45:18 +0800 Subject: [PATCH 6/9] fix build issue --- InputController/module.modulemap | 6 ------ Squirrel.xcodeproj/project.pbxproj | 6 ++---- 2 files changed, 2 insertions(+), 10 deletions(-) delete mode 100644 InputController/module.modulemap diff --git a/InputController/module.modulemap b/InputController/module.modulemap deleted file mode 100644 index fbfa33181..000000000 --- a/InputController/module.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -module InputController { - umbrella header "InputController.h" - - export * - module * { export * } -} diff --git a/Squirrel.xcodeproj/project.pbxproj b/Squirrel.xcodeproj/project.pbxproj index 93eb2d892..cb920668b 100644 --- a/Squirrel.xcodeproj/project.pbxproj +++ b/Squirrel.xcodeproj/project.pbxproj @@ -858,11 +858,10 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; @@ -922,11 +921,10 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; From 821ca422f6c19814fd8b01de60171b38617010e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=84=E9=BB=9B=E6=B7=B3=E5=8D=8E?= Date: Tue, 3 Dec 2024 21:54:56 +0800 Subject: [PATCH 7/9] downgrade required Xcode Version --- Squirrel.xcodeproj/project.pbxproj | 87 ++++++++++++++++++++++-------- 1 file changed, 65 insertions(+), 22 deletions(-) diff --git a/Squirrel.xcodeproj/project.pbxproj b/Squirrel.xcodeproj/project.pbxproj index cb920668b..c51d6eb5e 100644 --- a/Squirrel.xcodeproj/project.pbxproj +++ b/Squirrel.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 70; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -85,6 +85,13 @@ 7B5488C01D2DACDF0056A1BE /* luna_pinyin.schema.yaml in Copy Shared Support Files */ = {isa = PBXBuildFile; fileRef = 7B5488321D2DAAD10056A1BE /* luna_pinyin.schema.yaml */; }; 7B5488C11D2DACDF0056A1BE /* luna_quanpin.schema.yaml in Copy Shared Support Files */ = {isa = PBXBuildFile; fileRef = 7B5488331D2DAAD10056A1BE /* luna_quanpin.schema.yaml */; }; 7B5488C91D2DACDF0056A1BE /* symbols.yaml in Copy Shared Support Files */ = {isa = PBXBuildFile; fileRef = 7B54883B1D2DAAD10056A1BE /* symbols.yaml */; }; + 9168C20A2CFF446200348D56 /* TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9168C2072CFF446200348D56 /* TextView.swift */; }; + 9168C20B2CFF446200348D56 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9168C2092CFF446200348D56 /* ViewController.swift */; }; + 9168C20C2CFF446200348D56 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9168C2012CFF446200348D56 /* AppDelegate.swift */; }; + 9168C20D2CFF446200348D56 /* TextView+Unused.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9168C2082CFF446200348D56 /* TextView+Unused.swift */; }; + 9168C20E2CFF446200348D56 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9168C2042CFF446200348D56 /* Main.storyboard */; }; + 9168C20F2CFF446200348D56 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9168C2022CFF446200348D56 /* Assets.xcassets */; }; + 9168C2102CFF44DF00348D56 /* InputController.h in Headers */ = {isa = PBXBuildFile; fileRef = 9168C2002CFF445500348D56 /* InputController.h */; settings = {ATTRIBUTES = (Public, ); }; }; B3216E5C2BF438F800E292D2 /* rime.pdf in Resources */ = {isa = PBXBuildFile; fileRef = B3216E5B2BF438F800E292D2 /* rime.pdf */; }; B35D2FE82BF00839009D156B /* BridgingFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B35D2FE72BF00839009D156B /* BridgingFunctions.swift */; }; B38E9B912BE9AE1E0036ABEF /* SquirrelApplicationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B38E9B902BE9AE1E0036ABEF /* SquirrelApplicationDelegate.swift */; }; @@ -299,6 +306,14 @@ 7B5488331D2DAAD10056A1BE /* luna_quanpin.schema.yaml */ = {isa = PBXFileReference; lastKnownFileType = text; name = luna_quanpin.schema.yaml; path = data/plum/luna_quanpin.schema.yaml; sourceTree = ""; }; 7B54883B1D2DAAD10056A1BE /* symbols.yaml */ = {isa = PBXFileReference; lastKnownFileType = text; name = symbols.yaml; path = data/plum/symbols.yaml; sourceTree = ""; }; 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = resources/Info.plist; sourceTree = ""; }; + 9168C2002CFF445500348D56 /* InputController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InputController.h; sourceTree = ""; }; + 9168C2012CFF446200348D56 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 9168C2022CFF446200348D56 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 9168C2032CFF446200348D56 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Main.storyboard; sourceTree = ""; }; + 9168C2062CFF446200348D56 /* Demo.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Demo.entitlements; sourceTree = ""; }; + 9168C2072CFF446200348D56 /* TextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextView.swift; sourceTree = ""; }; + 9168C2082CFF446200348D56 /* TextView+Unused.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TextView+Unused.swift"; sourceTree = ""; }; + 9168C2092CFF446200348D56 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; A4B8E1B20F645B870094E08B /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = ""; }; B3216E5B2BF438F800E292D2 /* rime.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = rime.pdf; path = resources/rime.pdf; sourceTree = ""; }; B32B80772BE7FAA200FCF3BC /* Squirrel.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; name = Squirrel.entitlements; path = resources/Squirrel.entitlements; sourceTree = ""; }; @@ -320,21 +335,6 @@ F45E005E2B8CA81C00179B75 /* UserNotifications.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotifications.framework; path = System/Library/Frameworks/UserNotifications.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ -/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ - 0DEE70592CFBEC6C0062CC96 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { - isa = PBXFileSystemSynchronizedBuildFileExceptionSet; - publicHeaders = ( - InputController.h, - ); - target = 0DEE704E2CFBEC6C0062CC96 /* InputController */; - }; -/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ - -/* Begin PBXFileSystemSynchronizedRootGroup section */ - 0DD1CBD72CFC00ED00E6C847 /* Demo */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = Demo; sourceTree = ""; }; - 0DEE70502CFBEC6C0062CC96 /* InputController */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (0DEE70592CFBEC6C0062CC96 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = InputController; sourceTree = ""; }; -/* End PBXFileSystemSynchronizedRootGroup section */ - /* Begin PBXFrameworksBuildPhase section */ 0DD1CBD32CFC00ED00E6C847 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; @@ -387,6 +387,28 @@ path = sources; sourceTree = ""; }; + 0DD1CBD72CFC00ED00E6C847 /* Demo */ = { + isa = PBXGroup; + children = ( + 9168C2012CFF446200348D56 /* AppDelegate.swift */, + 9168C2022CFF446200348D56 /* Assets.xcassets */, + 9168C2052CFF446200348D56 /* Base.lproj */, + 9168C2062CFF446200348D56 /* Demo.entitlements */, + 9168C2072CFF446200348D56 /* TextView.swift */, + 9168C2082CFF446200348D56 /* TextView+Unused.swift */, + 9168C2092CFF446200348D56 /* ViewController.swift */, + ); + path = Demo; + sourceTree = ""; + }; + 0DEE70502CFBEC6C0062CC96 /* InputController */ = { + isa = PBXGroup; + children = ( + 9168C2002CFF445500348D56 /* InputController.h */, + ); + path = InputController; + sourceTree = ""; + }; 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = { isa = PBXGroup; children = ( @@ -551,6 +573,14 @@ name = plum; sourceTree = ""; }; + 9168C2052CFF446200348D56 /* Base.lproj */ = { + isa = PBXGroup; + children = ( + 9168C2042CFF446200348D56 /* Main.storyboard */, + ); + path = Base.lproj; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -558,6 +588,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 9168C2102CFF44DF00348D56 /* InputController.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -578,9 +609,6 @@ ); dependencies = ( ); - fileSystemSynchronizedGroups = ( - 0DD1CBD72CFC00ED00E6C847 /* Demo */, - ); name = Demo; packageProductDependencies = ( ); @@ -602,9 +630,6 @@ ); dependencies = ( ); - fileSystemSynchronizedGroups = ( - 0DEE70502CFBEC6C0062CC96 /* InputController */, - ); name = InputController; packageProductDependencies = ( ); @@ -649,6 +674,7 @@ TargetAttributes = { 0DD1CBD52CFC00ED00E6C847 = { CreatedOnToolsVersion = 16.0; + LastSwiftMigration = 1600; }; 0DEE704E2CFBEC6C0062CC96 = { CreatedOnToolsVersion = 16.0; @@ -686,6 +712,8 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9168C20E2CFF446200348D56 /* Main.storyboard in Resources */, + 9168C20F2CFF446200348D56 /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -716,6 +744,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9168C20A2CFF446200348D56 /* TextView.swift in Sources */, + 9168C20B2CFF446200348D56 /* ViewController.swift in Sources */, + 9168C20C2CFF446200348D56 /* AppDelegate.swift in Sources */, + 9168C20D2CFF446200348D56 /* TextView+Unused.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -756,6 +788,17 @@ }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXVariantGroup section */ + 9168C2042CFF446200348D56 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 9168C2032CFF446200348D56 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + /* Begin XCBuildConfiguration section */ 0DD1CBE32CFC00EE00E6C847 /* Debug */ = { isa = XCBuildConfiguration; From 6fd97a6fbb8ff6152226db385c68071b7e2a01c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=84=E9=BB=9B=E6=B7=B3=E5=8D=8E?= Date: Tue, 3 Dec 2024 23:24:32 +0800 Subject: [PATCH 8/9] fix build --- Squirrel.xcodeproj/project.pbxproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/Squirrel.xcodeproj/project.pbxproj b/Squirrel.xcodeproj/project.pbxproj index c51d6eb5e..d6a0769b9 100644 --- a/Squirrel.xcodeproj/project.pbxproj +++ b/Squirrel.xcodeproj/project.pbxproj @@ -1180,7 +1180,6 @@ MACOSX_DEPLOYMENT_TARGET = 13.0; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; - SWIFT_INCLUDE_PATHS = "$(HEADER_SEARCH_PATHS_QUOTED_FOR_PROJECT_1)/** $(HEADER_SEARCH_PATHS_QUOTED_FOR_PROJECT_2)/**"; SWIFT_STRICT_CONCURRENCY = targeted; SWIFT_VERSION = 5.0; SYSTEM_HEADER_SEARCH_PATHS = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Tk.framework/Headers; @@ -1238,7 +1237,6 @@ ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_INCLUDE_PATHS = "$(HEADER_SEARCH_PATHS_QUOTED_FOR_PROJECT_1)/** $(HEADER_SEARCH_PATHS_QUOTED_FOR_PROJECT_2)/**"; SWIFT_STRICT_CONCURRENCY = targeted; SWIFT_VERSION = 5.0; SYSTEM_HEADER_SEARCH_PATHS = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Tk.framework/Headers; From ae99c2b2f212fdcaa9eb316d14a51de3c2dc759b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=84=E9=BB=9B=E6=B7=B3=E5=8D=8E?= Date: Sat, 7 Dec 2024 16:11:13 +0800 Subject: [PATCH 9/9] fix demo has wrong input --- Demo/TextView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Demo/TextView.swift b/Demo/TextView.swift index 7eb51e1b0..7eb190b8a 100644 --- a/Demo/TextView.swift +++ b/Demo/TextView.swift @@ -80,6 +80,7 @@ class Client: NSObject, IMKTextInput { return } textView.insertText(string, replacementRange: replacementRange) + self.replacementRange = nil } }