├── .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 |
--------------------------------------------------------------------------------