本文目录导读:

- Linux (X11) - 使用 setxkbmap
- Linux (Wayland) - 使用 ydotool
- Windows - PowerShell 脚本
- macOS - AppleScript + Shell
- 更高级的方案 - 使用 Python
- 使用方法
- 注意事项
Linux (X11) - 使用 setxkbmap
根据应用窗口切换
#!/bin/bash
# 监听窗口焦点变化并切换键盘布局
# 定义应用对应的键盘布局
declare -A APP_LAYOUTS
APP_LAYOUTS["firefox"]="us"
APP_LAYOUTS["code"]="us"
APP_LAYOUTS["terminal"]="de"
APP_LAYOUTS["slack"]="fr"
# 获取当前活动窗口的信息
get_active_window_info() {
xdotool getactivewindow getwindowname
}
# 切换键盘布局
switch_layout() {
local app_name="$1"
local layout="${APP_LAYOUTS[$app_name]}"
if [[ -n "$layout" ]]; then
setxkbmap "$layout"
echo "切换到 $layout 布局"
fi
}
# 主循环
while true; do
current_app=$(get_active_window_info)
# 提取应用程序名称(小写)
app_lower=$(echo "$current_app" | tr '[:upper:]' '[:lower:]')
# 检查是否有匹配的应用
for app in "${!APP_LAYOUTS[@]}"; do
if [[ "$app_lower" == *"$app"* ]]; then
switch_layout "$app"
break
fi
done
sleep 0.5
done
Linux (Wayland) - 使用 ydotool
#!/bin/bash
# Wayland 环境下的键盘布局切换
# 安装依赖
# sudo apt install ydotool
switch_layout_wayland() {
local layout="$1"
case "$layout" in
"us")
gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'us')]"
;;
"de")
gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'de')]"
;;
"fr")
gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'fr')]"
;;
esac
}
# 根据时间切换(示例)
while true; do
hour=$(date +%H)
if [ "$hour" -ge 9 ] && [ "$hour" -lt 18 ]; then
# 工作时间使用英文布局
switch_layout_wayland "us"
else
# 休息时间使用母语布局
switch_layout_wayland "de"
fi
sleep 3600 # 每小时检查一次
done
Windows - PowerShell 脚本
# 自动切换键盘布局脚本
Add-Type @"
using System;
using System.Runtime.InteropServices;
public class KeyboardLayout {
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("user32.dll")]
public static extern IntPtr GetKeyboardLayout(uint idThread);
[DllImport("user32.dll")]
public static extern IntPtr ActivateKeyboardLayout(IntPtr hkl, uint flags);
}
"@
function Get-ActiveProcessName {
$hwnd = [KeyboardLayout]::GetForegroundWindow()
$null = [KeyboardLayout]::GetWindowThreadProcessId($hwnd, [ref]$pid)
return (Get-Process -Id $pid).ProcessName
}
function Set-KeyboardLayout {
param([string]$LayoutName)
# 布局ID映射
$layouts = @{
"US" = 0x04090409
"DE" = 0x04070407
"FR" = 0x040C040C
}
if ($layouts.ContainsKey($LayoutName)) {
$hkl = [IntPtr]::new($layouts[$LayoutName])
[KeyboardLayout]::ActivateKeyboardLayout($hkl, 0)
}
}
# 主循环
while ($true) {
$processName = Get-ActiveProcessName
switch -Wildcard ($processName) {
"chrome*" { Set-KeyboardLayout "US" }
"firefox*" { Set-KeyboardLayout "US" }
"notepad*" { Set-KeyboardLayout "US" }
"devenv*" { Set-KeyboardLayout "US" }
"*word*" { Set-KeyboardLayout "FR" }
"*excel*" { Set-KeyboardLayout "FR" }
default { Set-KeyboardLayout "DE" }
}
Start-Sleep -Milliseconds 500
}
macOS - AppleScript + Shell
#!/bin/bash
# macOS 键盘布局切换脚本
# 获取当前活动应用
get_active_app() {
osascript -e 'tell application "System Events" to get name of first process whose frontmost is true'
}
# 切换输入源
switch_input_source() {
local layout="$1"
case "$layout" in
"US")
osascript -e 'tell application "System Events" to tell process "SystemUIServer" to click menu bar item 1 of menu bar 2'
;;
"German")
osascript -e 'tell application "System Events" to tell process "SystemUIServer" to click menu bar item 2 of menu bar 2'
;;
"French")
osascript -e 'tell application "System Events" to tell process "SystemUIServer" to click menu bar item 3 of menu bar 2'
;;
esac
}
# 配置应用-布局映射
declare -A APP_LAYOUTS
APP_LAYOUTS["Safari"]="US"
APP_LAYOUTS["Chrome"]="US"
APP_LAYOUTS["Terminal"]="US"
APP_LAYOUTS["Xcode"]="US"
APP_LAYOUTS["TextEdit"]="German"
APP_LAYOUTS["Pages"]="French"
while true; do
current_app=$(get_active_app)
if [[ -n "${APP_LAYOUTS[$current_app]}" ]]; then
switch_input_source "${APP_LAYOUTS[$current_app]}"
fi
sleep 0.5
done
更高级的方案 - 使用 Python
#!/usr/bin/env python3
"""
跨平台键盘布局自动切换器
"""
import platform
import time
import subprocess
import re
class KeyboardLayoutSwitcher:
def __init__(self):
self.system = platform.system()
self.setup_layout_mapping()
def setup_layout_mapping(self):
"""定义应用和布局的映射关系"""
self.layout_map = {
# 应用名关键词: 布局名
'browser': 'us', # 浏览器
'terminal': 'us', # 终端
'code': 'us', # 编程IDE
'office': 'fr', # 办公软件
'chat': 'de', # 聊天软件
'default': 'us' # 默认布局
}
# 具体应用映射
self.app_map = {
'firefox': 'browser',
'chrome': 'browser',
'safari': 'browser',
'terminal': 'terminal',
'iterm2': 'terminal',
'vscode': 'code',
'code': 'code',
'word': 'office',
'excel': 'office',
'slack': 'chat',
'telegram': 'chat',
'discord': 'chat',
}
def get_active_window_name(self):
"""获取当前活动窗口名称"""
if self.system == 'Linux':
try:
result = subprocess.run(
['xdotool', 'getactivewindow', 'getwindowname'],
capture_output=True, text=True
)
return result.stdout.strip().lower()
except:
return ''
elif self.system == 'Darwin': # macOS
try:
result = subprocess.run(
['osascript', '-e',
'tell application "System Events" to get name of first process whose frontmost is true'],
capture_output=True, text=True
)
return result.stdout.strip().lower()
except:
return ''
elif self.system == 'Windows':
try:
import win32gui
window = win32gui.GetForegroundWindow()
return win32gui.GetWindowText(window).lower()
except:
return ''
return ''
def switch_layout(self, layout):
"""切换键盘布局"""
if self.system == 'Linux':
subprocess.run(['setxkbmap', layout])
elif self.system == 'Darwin':
# macOS 切换输入源
script = f'''
tell application "System Events"
tell process "SystemUIServer"
click menu bar item 1 of menu bar 2
end tell
end tell
'''
subprocess.run(['osascript', '-e', script])
elif self.system == 'Windows':
# Windows 切换布局
import win32api
import win32con
layouts = {
'us': 0x04090409,
'de': 0x04070407,
'fr': 0x040C040C
}
if layout in layouts:
win32api.LoadKeyboardLayout(f'{layouts[layout]:08X}', win32con.KLF_ACTIVATE)
print(f"切换到 {layout} 布局")
def determine_layout(self, window_name):
"""根据窗口名称确定应使用的布局"""
window_lower = window_name.lower()
# 检查具体应用
for app, category in self.app_map.items():
if app in window_lower:
return self.layout_map.get(category, self.layout_map['default'])
# 检查关键词
for keyword, layout in self.layout_map.items():
if keyword in window_lower and keyword != 'default':
return layout
return self.layout_map['default']
def run(self):
"""主运行循环"""
print("键盘布局自动切换器已启动...")
last_layout = None
while True:
try:
window_name = self.get_active_window_name()
if window_name:
target_layout = self.determine_layout(window_name)
if target_layout != last_layout:
self.switch_layout(target_layout)
last_layout = target_layout
time.sleep(0.5) # 每0.5秒检查一次
except KeyboardInterrupt:
print("\n程序已停止")
break
except Exception as e:
print(f"错误: {e}")
time.sleep(2)
if __name__ == "__main__":
switcher = KeyboardLayoutSwitcher()
switcher.run()
使用方法
Linux 用户:
# 保存脚本为 switch_layout.sh chmod +x switch_layout.sh ./switch_layout.sh # 直接运行 # 或设置为开机自启动 # 添加到 ~/.bashrc 或使用 systemd
Windows 用户:
# 保存为 .ps1 文件 # 以管理员身份运行 PowerShell Set-ExecutionPolicy RemoteSigned .\keyboard_switcher.ps1
macOS 用户:
# 保存脚本 chmod +x keyboard_switcher.sh ./keyboard_switcher.sh
注意事项
- 权限问题:某些系统可能需要管理员权限
- 性能影响:建议将检查间隔设为0.5-1秒
- 自定义映射:根据自己的使用习惯修改应用-布局映射
- 调试建议:先使用
echo测试窗口检测功能
选择适合你系统的版本进行定制即可!