├── .gitignore ├── .quiet.json.template ├── README-ja.md ├── README-zh.md ├── README.md ├── install.sh ├── launch.plist ├── run.swift └── update.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 105 | __pypackages__/ 106 | 107 | # Celery stuff 108 | celerybeat-schedule 109 | celerybeat.pid 110 | 111 | # SageMath parsed files 112 | *.sage.py 113 | 114 | # Environments 115 | .env 116 | .venv 117 | env/ 118 | venv/ 119 | ENV/ 120 | env.bak/ 121 | venv.bak/ 122 | 123 | # Spyder project settings 124 | .spyderproject 125 | .spyproject 126 | 127 | # Rope project settings 128 | .ropeproject 129 | 130 | # mkdocs documentation 131 | /site 132 | 133 | # mypy 134 | .mypy_cache/ 135 | .dmypy.json 136 | dmypy.json 137 | 138 | # Pyre type checker 139 | .pyre/ 140 | 141 | # pytype static type analyzer 142 | .pytype/ 143 | 144 | # Cython debug symbols 145 | cython_debug/ 146 | 147 | # PyCharm 148 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 149 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 150 | # and can be added to the global gitignore or merged into this file. For a more nuclear 151 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 152 | #.idea/ 153 | 154 | *.txt 155 | 156 | .quiet.json 157 | quiet 158 | -------------------------------------------------------------------------------- /.quiet.json.template: -------------------------------------------------------------------------------- 1 | { 2 | "default": "abc", 3 | "appConfig": { 4 | "Shuangpin": [ 5 | "Telegram", 6 | "WeChat" 7 | ], 8 | "abc": [ 9 | "Code" 10 | ] 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /README-ja.md: -------------------------------------------------------------------------------- 1 | # 静かな入力方法 2 | 3 | 静かな入力方法は、Mac OS Xの入力方法切り替えです。アプリケーションに応じて入力方法を自動的に切り替えることができます。 4 | 5 | [English](README.md) | [中文](README-zh.md) | [日本語](README-ja.md) 6 | 7 | ## 基本的なインストール方法 8 | 9 | | 方法 | コマンド | 10 | |:----------|:--------------------------------------------------------------------------------------------------| 11 | | **curl** | `sh -c "$(curl -fsSL https://raw.githubusercontent.com/Yufeikang/quiet_input_method/master/install.sh)"` | 12 | | **wget** | `sh -c "$(wget -O- https://raw.githubusercontent.com/Yufeikang/quiet_input_method/master/install.sh)"` | 13 | | **fetch** | `sh -c "$(fetch -o - https://raw.githubusercontent.com/Yufeikang/quiet_input_method/master/install.sh)"` | 14 | 15 | ## 手動インストール 16 | 17 | 1. `git`がインストールされていることを確認してください。もしインストールされていなければ、先にインストールしてください。 18 | 19 | 2. ターミナルを開き、プロジェクトをクローンしたいディレクトリに移動します。例えば: 20 | 21 | ``` 22 | cd ~/my_custom_directory 23 | ``` 24 | 25 | 3. デフォルトのアドレスを使用してプロジェクトのGitHubリポジトリをクローンします: 26 | 27 | ``` 28 | git clone --depth=1 --branch master https://github.com/Yufeikang/quiet_input_method.git 29 | ``` 30 | 31 | これにより、`quiet_input_method`という名前のディレクトリが作成されます。 32 | 33 | 4. クローンしたディレクトリに移動します: 34 | 35 | ``` 36 | cd quiet_input_method 37 | ``` 38 | 39 | 5. `sed`コマンドを使って、`launch.plist`ファイルの`SRC_DIR`を現在のディレクトリの値に置き換え、ファイルを`~/Library/LaunchAgents/quiet_input_method.plist`に保存します。 40 | 41 | ``` 42 | sed -e "s/SRC_DIR/$(pwd | sed 's/\//\\\//g')/g" launch.plist > ~/Library/LaunchAgents/quiet_input_method.plist 43 | ``` 44 | 45 | 6. `launchctl`コマンドを使って`quiet_input_method.plist`ファイルを読み込みます: 46 | 47 | ``` 48 | launchctl load ~/Library/LaunchAgents/quiet_input_method.plist 49 | ``` 50 | 51 | これらの手順が完了すると、静かな入力方法がインストールされ、正常に設定されるはずです。 52 | 53 | ## 設定例 54 | 55 | ファイル ~/.quiet.json 56 | 57 | ``` 58 | { 59 | "default": "abc", 60 | "appConfig": { 61 | "Shuangpin": [ 62 | "Telegram", 63 | "WeChat" 64 | ], 65 | "abc": [ 66 | "Code" 67 | ] 68 | } 69 | } 70 | 71 | ``` 72 | 73 | ## 更新と再起動 74 | 75 | ``` 76 | sh ./update.sh 77 | ``` 78 | -------------------------------------------------------------------------------- /README-zh.md: -------------------------------------------------------------------------------- 1 | # 安静输入法 2 | 3 | 安静输入法是 Mac OS X 上的输入法切换器。 它可以根据应用自动切换输入法。 4 | 5 | [English](README.md) | [中文](README-zh.md) | [日本語](README-ja.md) 6 | 7 | ## 基本安装 8 | 9 | | 方法 | 命令 | 10 | |:----------|:--------------------------------------------------------------------------------------------------| 11 | | **curl** | `sh -c "$(curl -fsSL https://raw.githubusercontent.com/Yufeikang/quiet_input_method/master/install.sh)"` | 12 | | **wget** | `sh -c "$(wget -O- https://raw.githubusercontent.com/Yufeikang/quiet_input_method/master/install.sh)"` | 13 | | **fetch** | `sh -c "$(fetch -o - https://raw.githubusercontent.com/Yufeikang/quiet_input_method/master/install.sh)"` | 14 | 15 | ## 手动安装 16 | 17 | 1. 确保已安装 `git` 。 如果没有,请先安装。 18 | 19 | 2. 打开终端并导航到要克隆项目的目录。 例如: 20 | 21 | ``` 22 | cd ~/my_custom_directory 23 | ``` 24 | 25 | 3. 使用默认地址克隆项目的 GitHub 仓库: 26 | 27 | ``` 28 | git clone --depth=1 --branch master https://github.com/Yufeikang/quiet_input_method.git 29 | ``` 30 | 31 | 这将创建一个名为 `quiet_input_method` 的目录。 32 | 33 | 4. 切换到克隆的目录: 34 | 35 | ``` 36 | cd quiet_input_method 37 | ``` 38 | 39 | 5. 使用 `sed` 命令将当前目录的值替换为 `launch.plist` 文件中的 `SRC_DIR` ,然后将文件保存到 40 | `~/Library/LaunchAgents/quiet_input_method.plist` 中。 41 | 42 | ``` 43 | sed -e "s/SRC_DIR/$(pwd | sed 's/\//\\\//g')/g" launch.plist > ~/Library/LaunchAgents/quiet_input_method.plist 44 | ``` 45 | 46 | 6. 使用 `launchctl` 命令加载 `quiet_input_method.plist` 文件: 47 | 48 | ``` 49 | launchctl load ~/Library/LaunchAgents/quiet_input_method.plist 50 | ``` 51 | 52 | 完成这些步骤后,静音输入法应安装并配置成功。 53 | 54 | ## 配置示例 55 | 56 | 文件 ~/.quiet.json 57 | 58 | ``` 59 | { 60 | "default": "abc", 61 | "appConfig": { 62 | "Shuangpin": [ 63 | "Telegram", 64 | "WeChat" 65 | ], 66 | "abc": [ 67 | "Code" 68 | ] 69 | } 70 | } 71 | 72 | ``` 73 | 74 | ## 更新和重启 75 | 76 | ``` 77 | sh ./update.sh 78 | ``` 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Quiet Input Method 2 | 3 | Quiet Input Method is a input method switcher on Mac OS X. It can automatically switch input method according to the application. 4 | 5 | [English](README.md) | [中文](README-zh.md) | [日本語](README-ja.md) 6 | 7 | ## Basic Installation 8 | 9 | | Method | Command | 10 | |:----------|:--------------------------------------------------------------------------------------------------| 11 | | **curl** | `sh -c "$(curl -fsSL https://raw.githubusercontent.com/Yufeikang/quiet_input_method/master/install.sh)"` | 12 | | **wget** | `sh -c "$(wget -O- https://raw.githubusercontent.com/Yufeikang/quiet_input_method/master/install.sh)"` | 13 | | **fetch** | `sh -c "$(fetch -o - https://raw.githubusercontent.com/Yufeikang/quiet_input_method/master/install.sh)"` | 14 | 15 | ## Manual Installation 16 | 17 | 1. Make sure `git` is installed. If it's not, please install it first. 18 | 19 | 2. Open the terminal and navigate to the directory where you want to clone the project. For example: 20 | 21 | ``` 22 | cd ~/my_custom_directory 23 | ``` 24 | 25 | 3. Clone the project's GitHub repository using the default address: 26 | 27 | ``` 28 | git clone --depth=1 --branch master https://github.com/Yufeikang/quiet_input_method.git 29 | ``` 30 | 31 | This will create a directory named `quiet_input_method`. 32 | 33 | 4. Change to the cloned directory: 34 | 35 | ``` 36 | cd quiet_input_method 37 | ``` 38 | 39 | 5. Use the `sed` command to replace `SRC_DIR` in the `launch.plist` file with the current directory's value and save the file to `~/Library/LaunchAgents/quiet_input_method.plist`. 40 | 41 | ``` 42 | sed -e "s/SRC_DIR/$(pwd | sed 's/\//\\\//g')/g" launch.plist > ~/Library/LaunchAgents/quiet_input_method.plist 43 | ``` 44 | 45 | 6. Use the `launchctl` command to load the `quiet_input_method.plist` file: 46 | 47 | ``` 48 | launchctl load ~/Library/LaunchAgents/quiet_input_method.plist 49 | ``` 50 | 51 | After completing these steps, the quiet_input_method should be installed and configured successfully. 52 | 53 | ## Config Example 54 | 55 | file ~/.quiet.json 56 | 57 | ``` 58 | { 59 | "default": "abc", 60 | "appConfig": { 61 | "Shuangpin": [ 62 | "Telegram", 63 | "WeChat" 64 | ], 65 | "abc": [ 66 | "Code" 67 | ] 68 | } 69 | } 70 | ``` 71 | 72 | ## Update & restart 73 | 74 | ``` 75 | sh ./update.sh 76 | ``` 77 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | # Default settings 6 | DIR=${DIR:-~/.quiet_input_method} 7 | REPO=${REPO:-Yufeikang/quiet_input_method} 8 | REMOTE=${REMOTE:-https://github.com/${REPO}.git} 9 | BRANCH=${BRANCH:-master} 10 | 11 | 12 | command_exists() { 13 | command -v "$@" >/dev/null 2>&1 14 | } 15 | 16 | fmt_error() { 17 | echo ${RED}"Error: $@" >&2 18 | } 19 | 20 | fmt_underline() { 21 | echo "$(printf '\033[4m')$@$(printf '\033[24m')" 22 | } 23 | 24 | fmt_code() { 25 | echo "\`$(printf '\033[38;5;247m')$@\`" 26 | } 27 | 28 | setup_color() { 29 | # Only use colors if connected to a terminal 30 | if [ -t 1 ]; then 31 | RED=$(printf '\033[31m') 32 | GREEN=$(printf '\033[32m') 33 | YELLOW=$(printf '\033[33m') 34 | BLUE=$(printf '\033[34m') 35 | BOLD=$(printf '\033[1m') 36 | RESET=$(printf '\033[m') 37 | else 38 | RED="" 39 | GREEN="" 40 | YELLOW="" 41 | BLUE="" 42 | BOLD="" 43 | RESET="" 44 | fi 45 | } 46 | 47 | setup_src() { 48 | umask g-w,o-w 49 | 50 | echo "${BLUE}Cloning ..." 51 | 52 | command_exists git || { 53 | fmt_error "git is not installed" 54 | exit 1 55 | } 56 | 57 | git clone -c core.eol=lf -c core.autocrlf=false \ 58 | -c fsck.zeroPaddedFilemode=ignore \ 59 | -c fetch.fsck.zeroPaddedFilemode=ignore \ 60 | -c receive.fsck.zeroPaddedFilemode=ignore \ 61 | --depth=1 --branch "$BRANCH" "$REMOTE" "$DIR" || { 62 | fmt_error "git clone of repo failed" 63 | exit 1 64 | } 65 | 66 | echo 67 | } 68 | 69 | setup_launch() { 70 | sed -e "s/SRC_DIR/${DIR//\//\\/}/g" $DIR/launch.plist > ~/Library/LaunchAgents/quiet_input_method.plist 71 | launchctl load ~/Library/LaunchAgents/quiet_input_method.plist 72 | } 73 | 74 | build() { 75 | echo "${BLUE}Building ..." 76 | 77 | xcrun -sdk macosx swiftc $DIR/run.swift -o $DIR/quiet 78 | } 79 | 80 | main() { 81 | setup_color 82 | 83 | if [ -d "$DIR" ]; then 84 | echo "${YELLOW}The \$DIR folder already exists ($DIR)" 85 | fi 86 | 87 | 88 | setup_src 89 | build 90 | setup_launch 91 | 92 | printf "$GREEN" 93 | 94 | } 95 | 96 | main "$@" 97 | -------------------------------------------------------------------------------- /launch.plist: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | Label 7 | QuietInputMethod 8 | ProgramArguments 9 | 10 | SRC_DIR/quiet 11 | 12 | KeepAlive 13 | 14 | RunAtLoad 15 | 16 | StandardOutPath 17 | SRC_DIR/log.txt 18 | StandardErrorPath 19 | SRC_DIR/error.txt 20 | WorkingDirectory 21 | SRC_DIR 22 | 23 | -------------------------------------------------------------------------------- /run.swift: -------------------------------------------------------------------------------- 1 | import AppKit 2 | import Carbon 3 | import Foundation 4 | 5 | func getInputMethodList() -> [String] { 6 | let list = TISCreateInputSourceList(nil, false).takeRetainedValue() as! [TISInputSource] 7 | var inputMethodList = [String]() 8 | for source in list { 9 | if let name = TISGetInputSourceProperty(source, kTISPropertyInputSourceID) { 10 | let sourceName = Unmanaged.fromOpaque(name).takeUnretainedValue() as String 11 | inputMethodList.append(sourceName) 12 | } 13 | } 14 | return inputMethodList 15 | } 16 | 17 | func getSelectedInputMethod() -> String { 18 | let currentSource = TISCopyCurrentKeyboardInputSource().takeRetainedValue() 19 | let name = TISGetInputSourceProperty(currentSource, kTISPropertyInputSourceID) 20 | let sourceName = Unmanaged.fromOpaque(name!).takeUnretainedValue() as String 21 | return sourceName 22 | } 23 | 24 | func switchInputMethod(to inputMethod: String) { 25 | let inputSources = TISCreateInputSourceList(nil, false).takeRetainedValue() as! [TISInputSource] 26 | for inputSource in inputSources { 27 | if let inputSourceID = TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceID) { 28 | let inputSourceIDString = 29 | Unmanaged.fromOpaque(inputSourceID).takeUnretainedValue() as String 30 | if inputSourceIDString.range(of: inputMethod, options: .caseInsensitive) != nil { 31 | print("Switching to \(inputSourceIDString)") 32 | TISSelectInputSource(inputSource) 33 | return 34 | } 35 | } 36 | } 37 | print("Not found \(inputMethod), maybe not installed?") 38 | 39 | } 40 | 41 | // Check if config.json file exists 42 | let fileManager = FileManager.default 43 | let configFilePath = 44 | "\(ProcessInfo.processInfo.environment["HOME"]!)/.quiet_input_method/.quiet.json" 45 | var configData: Data? 46 | if fileManager.fileExists(atPath: configFilePath) { 47 | configData = fileManager.contents(atPath: configFilePath) 48 | } 49 | 50 | // Default config 51 | var DEFAULT_CONFIG = """ 52 | { 53 | "default": "abc", 54 | "appConfig": { 55 | "Shuangpin": [ 56 | "Telegram", 57 | "WeChat" 58 | ], 59 | "abc": [ 60 | "Code" 61 | ] 62 | } 63 | } 64 | """ 65 | 66 | // Convert json to dict 67 | func jsonToDict(jsonData: Data) -> [String: Any] { 68 | let dict = 69 | try? JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers) as! [String: Any] 70 | return dict ?? [:] 71 | } 72 | 73 | // Load config from file or use default config 74 | var config = jsonToDict(jsonData: configData ?? DEFAULT_CONFIG.data(using: .utf8)!) 75 | 76 | func getInputMethodByConfig(appName: String) -> String { 77 | for (key, value) in config["appConfig"] as! [String: [String]] { 78 | 79 | let appList = value 80 | if appList.contains(appName) { 81 | return key 82 | } 83 | } 84 | // if default config exists, use it 85 | if let defaultInputMethod = config["default"] as? String { 86 | return defaultInputMethod 87 | } 88 | return "Unknown" 89 | } 90 | 91 | // Listen for app switch events and print the current app 92 | let workspace = NSWorkspace.shared 93 | var currentApp: NSRunningApplication? 94 | 95 | workspace.notificationCenter.addObserver( 96 | forName: NSWorkspace.didActivateApplicationNotification, object: nil, queue: nil 97 | ) { notification in 98 | if let app = notification.userInfo?[NSWorkspace.applicationUserInfoKey] as? NSRunningApplication { 99 | if app != currentApp { 100 | currentApp = app 101 | let appName = app.localizedName ?? "Unknown" 102 | let inputMethod = getInputMethodByConfig(appName: appName) 103 | if inputMethod == "Unknown" { 104 | return 105 | } 106 | print("Current app: \(app.localizedName ?? "Unknown") to \(inputMethod)") 107 | switchInputMethod(to: inputMethod) 108 | } 109 | } 110 | } 111 | 112 | print("Listening for app switch events...") 113 | 114 | // list all input methods 115 | for inputMethod in getInputMethodList() { 116 | print(inputMethod) 117 | } 118 | 119 | RunLoop.main.run() 120 | -------------------------------------------------------------------------------- /update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | git pull origin master 4 | 5 | DIR=${DIR:-~/.quiet_input_method} 6 | sed -e "s/SRC_DIR/${DIR//\//\\/}/g" $DIR/launch.plist >~/Library/LaunchAgents/quiet_input_method.plist 7 | 8 | # check bin not exists, to build 9 | if [ ! -f "$DIR/bin/quiet" ]; then 10 | echo "${BLUE}Building ..." 11 | xcrun -sdk macosx swiftc $DIR/run.swift -o $DIR/quiet 12 | fi 13 | 14 | launchctl unload ~/Library/LaunchAgents/quiet_input_method.plist 15 | launchctl load ~/Library/LaunchAgents/quiet_input_method.plist 16 | --------------------------------------------------------------------------------