Linux
1 - Ubuntu 配置
制作启动盘
balenaEtcher,支持 Windows、macOS 和 Linux 的跨平台启动盘制作工具,界面简洁,使用方便,支持多种镜像格式,如 ISO、IMG 等。
brew install balenaetcher下载好镜像后,使用 balenaEtcher 将其写入到 U 盘中,制作完成后就可以使用这个 U 盘来安装 Ubuntu 了。
安装完成后,配置清华源:
安装必要的软件包:
sudo apt update
sudo apt install -y build-essential curl git vim tmux fd-find ripgrep修改 hostname ,设置一个好记的名字:
sudo hostnamectl set-hostname devboxbashrc 配置
wget -O ~/.bash_aliases https://api.liujiacai.net/bashrc初始化
sudo apt install -y vim git openssh-server之后拷贝自己的公钥到服务器上,保证免密登录:
ssh-copy-id -i ~/.ssh/id_ed25519.pub dev
ssh-copy-id -i ~/.ssh/id_ecdsa.pub dev快捷键
与 mac 对应
gsettings set org.gnome.desktop.input-sources xkb-options "['ctrl:swap_lalt_lctl', 'ctrl:swapcaps']"先把 alt 和 ctrl 交互,再把 ctrl 与 caps 交互,最后的效果
键位 实际功能 Alt Ctrl Caps Ctrl Ctrl Alt
如果操作失误,可以通过这个命令来还原
gsettings reset org.gnome.desktop.input-sources xkb-options(setq x-alt-keysym 'control) ; 物理 Alt(系统是 Ctrl)→ Emacs Control
(setq x-super-keysym 'meta) ; 物理 Win → Emacs MetaCaps Lock 键改为 Ctrl 键
# 临时生效(重启后失效),不退出当前 shell 立即生效
setxkbmap -option "ctrl:swapcaps"
# 永久生效
echo 'XKBOPTIONS="ctrl:swapcaps"' | sudo tee -a /etc/default/keyboard
sudo dpkg-reconfigure keyboard-configuration禁用系统快捷键
鼠标
macOS 上有个经典痛点:触控板用自然滚动,外接鼠标用传统方向。GNOME 原生支持分别控制:
# 鼠标
gsettings set org.gnome.desktop.peripherals.mouse natural-scroll false
# 触控板
gsettings set org.gnome.desktop.peripherals.touchpad natural-scroll true禁用 Gnome 快捷键
# 禁用 Meta+Alt+<- 进行工作空间切换,方便 brave 设置上/下一个标签
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-left "[]"
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-right "[]"
# 禁用 Super 打开 Activities,默认值是 Super_L
gsettings set org.gnome.mutter overlay-key ''
# 禁用锁屏,默认是 ['<Super>l'],也可以配置成 ['<Control><Super>l']
gsettings set org.gnome.settings-daemon.plugins.media-keys screensaver "['']"
# 禁用 dash-to-dock 的 Super+数字,这样 Emacs 中的 Meta+数字键就可以用来切换窗口了
gsettings set org.gnome.shell.extensions.dash-to-dock hot-keys false
# 同时清掉 GNOME Shell 层的(双保险)
for i in $(seq 1 9); do
gsettings set org.gnome.shell.keybindings switch-to-application-$i "[]"
done
# 禁用 ibus 的 Emoji Choice/Annotation
gsettings set org.freedesktop.ibus.panel.emoji hotkey "['']"
# 禁用窗口平铺
gsettings set org.gnome.mutter.keybindings toggle-tiled-right "['']"
gsettings set org.gnome.mutter.keybindings toggle-tiled-left "['']"
# 24.04 需要,因为该版本引入了全新集成的 Tiling Assistant(平铺助手) 扩展
gsettings set org.gnome.shell.extensions.tiling-assistant tile-left-half "['']"
gsettings set org.gnome.shell.extensions.tiling-assistant tile-right-half "['']"
# 禁用 toggle the notification/message tray
gsettings set org.gnome.shell.keybindings toggle-message-tray "[]"
# disable the Super + D (Hide all normal windows / Show Desktop)
gsettings set org.gnome.desktop.wm.keybindings show-desktop "['']"
gsettings set org.gnome.shell.keybindings focus-active-notification "[]"
# 禁用 Super + s
gsettings set org.gnome.shell.keybindings toggle-quick-settings ['']
# 禁用 Super + p,切换多显示器输出模式
gsettings set org.gnome.mutter.keybindings switch-monitor []然后在 Brave 浏览器中设置 Meta + Ctrl + ArrowRight 为下一标签页, Meta + Ctrl + ArrowLeft 为上一标签页。
如果你不确定是哪个特定的配置项吞掉了快捷键,可以直接在终端用以下命令全局过滤当前系统中所有绑定了 <Super>n 或 <Alt>n 的 GNOME 设置项:
gsettings list-recursively | grep -E "(<Super>|<Alt>)[nN]"配置 bash tab 补全时大小写不敏感
# 永久生效
echo "set completion-ignore-case on" >> ~/.inputrc
# 不退出当前 shell 立即生效
bind -f ~/.inputrc
# 临时生效
bind "set completion-ignore-case on"关闭 GUI 登录
# 永久关闭 GUI
sudo systemctl set-default multi-user.target
# 恢复开机启动 GUI
sudo systemctl set-default graphical.target要让 Ubuntu 在不连接显示器时保持开机且不自动休眠,关键是配置电源管理以忽略显示器状态。
sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target禁用 WiFi/网卡的电源管理 (最常见原因)
如果你的网卡开启了省电模式,空闲时会自动切断连接。 排查命令:
iwconfig # 如果是 WiFi,查看 Power Management 是否为 on解决方法(禁用网卡省电):
sudo vim /etc/NetworkManager/conf.d/default-wifi-powersave-on.conf将 wifi.powersave = 3 改为 wifi.powersave = 2 (2 表示禁用)。如果是以太网(网线),执行:
# 网卡名通过 ip addr 查看
sudo ethtool -s [网卡名] wol d # 禁用唤醒省电模式显卡驱动导致系统卡死
根据 sudo journalctl -b -1 -n 100 命令查看上次死机前的日志,发现有如下信息:
Feb 17 18:00:12 devbox kernel: audit: type=1400 audit(1771322412.494:160): apparmor="DENIED" operation="open" class="file" profile="snap.firmware-updater.firmware-notifier" name="/p>
Feb 17 18:00:58 devbox kernel: workqueue: i915_hpd_poll_init_work [i915] hogged CPU for >10000us 1027 times, consider switching to WQ_UNBOUND可以看出,最后一行日志显示 i915 驱动占用 CPU 过高,导致系统卡死。解决方法是禁用 i915 驱动:
# 1. 创建黑名单文件
sudo bash -c 'cat > /etc/modprobe.d/blacklist-i915.conf << EOF
blacklist i915
blacklist intel_agp
EOF'
# 2. 更新 initramfs
sudo update-initramfs -u -k all
# 3. 重启
sudo reboot
# 4. i915 驱动(如果需要恢复显卡功能)
sudo rm /etc/modprobe.d/blacklist-i915.conf
sudo update-initramfs -u -k all常用命令
# 安装 uv 包管理器
# https://docs.astral.sh/uv/getting-started/installation/
curl -LsSf https://astral.sh/uv/install.sh | sh
# https://supervisord.org/installing.html
uv tool install supervisor2 - Debian 配置
这里配置以 12 为基础介绍
安装时需要联网的问题
即使使用离线安装镜像,安装过程中也会提示联网,原因是安装程序需要从网络上下载一些必要的软件包和更新,以确保系统的稳定性和安全性。解决方法有两种:
- 在安装过程中选择『不联网』,安装完成后再配置网络。
- 在安装过程中选择『联网』,但使用一个无效的网络配置,这样安装程序会尝试联网,但最终会失败,安装完成后再配置网络。
配置 locale
# Install locales package
apt-get install -y locales
# Uncomment en_US.UTF-8 for inclusion in generation
sed -i 's/^# *\(en_US.UTF-8\)/\1/' /etc/locale.gen
# Generate locale
/usr/sbin/locale-gen
# Export env vars
echo "export LC_ALL=en_US.UTF-8" >> ~/.bashrc
echo "export LANG=en_US.UTF-8" >> ~/.bashrc
echo "export LANGUAGE=en_US.UTF-8" >> ~/.bashrc
echo -e 'LANG="en_US.UTF-8"\nLANGUAGE="en_US:en"\n' > /etc/default/localemacOS 键位绑定
不同桌面有不同的配置方式:
kde
# Settings → Input Devices → Keyboard → Advanced → 勾选对应选项 Ctrl position → Caps Lock as Ctrl —— CapsLock 变 Ctrl Alt/Win key behavior → Alt and Meta are swapped —— Alt ↔ Supergnome
gsettings set org.gnome.desktop.input-sources xkb-options "['altwin:swap_alt_win', 'ctrl:swapcaps']"
快捷键
# 禁用 Meta+Alt+<- 进行工作空间切换,方便 brave 设置上/下一个标签
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-left "[]"
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-right "[]"
# 禁用 Super 打开 Activities,默认值是 Super_L
gsettings set org.gnome.mutter overlay-key ''
# 改用 Super+space 打开 Activities,类型 macOS 下 raycast 的唤醒
gsettings set org.gnome.shell.keybindings toggle-overview "['<Super>space']"
# 禁用锁屏,默认是 ['<Super>l'],也可以配置成 ['<Control><Super>l']
gsettings set org.gnome.settings-daemon.plugins.media-keys screensaver "[]"
# 禁用 dash-to-dock 的 Super+数字,这样 Emacs 中的 Meta+数字键就可以用来切换窗口了
gsettings set org.gnome.shell.extensions.dash-to-dock hot-keys false
# 同时清掉 GNOME Shell 层的(双保险)
for i in $(seq 1 9); do
gsettings set org.gnome.shell.keybindings switch-to-application-$i "[]"
done
# 禁用 ibus 的 Emoji Choice/Annotation
gsettings set org.freedesktop.ibus.panel.emoji hotkey "[]"
# 禁用 toggle the notification/message tray
gsettings set org.gnome.shell.keybindings toggle-message-tray "[]"
# disable the Super + D (Hide all normal windows / Show Desktop)
gsettings set org.gnome.desktop.wm.keybindings show-desktop "['']"
gsettings set org.gnome.shell.keybindings focus-active-notification "[]"
# 禁用 Super + s
gsettings set org.gnome.shell.keybindings toggle-quick-settings ['']
# 禁用 Super + p,切换多显示器输出模式
gsettings set org.gnome.mutter.keybindings switch-monitor []关闭休眠
GUI 模型
# 关掉插电时的自动挂起
gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-ac-type 'nothing'
# 关掉电池时的自动挂起(可选,电池供电时建议保留)
gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-battery-type 'nothing'TTY 模型
# sudo vi /etc/systemd/logind.conf
# 改成 ignore 保证一直开机
IdleAction=poweroff
IdleActionSec=10min
HandleSuspendKey=ignore
HandleHibernateKey=ignore
HandleLidSwitch=ignore
HandleLidSwitchExternalPower=ignore
HandleLidSwitchDocked=ignore之后在重启服务,
sudo systemctl restart systemd-logind同时配置 ssh 定期发送心跳,可以保证系统不进入 idle 状态:
Host dev
HostName 192.168.31.142
ServerAliveInterval 120- 参考 Gemini 回答:https://g.co/gemini/share/05b084f8eacc
配置 sudo 权限
# 先切到 root 用户
su root
# 默认是只读权限
chmod +w /etc/sudoers
# 之后添加
[user-name] ALL=(ALL:ALL) ALL
# 最后在改回去
chmod -w /etc/sudoers连接 wifi
使用 NetworkManager 的命令行工具 nmcli
nmcli d wifi connect WIFI_SSID password YOUR_PW ifname YOUR_IFACE节约资源
关闭 GUI 登录,默认 TTY
# 查看当前默认方式
systemctl get-default
# 修改成 TTY
systemctl set-default multi-user.target
# 修改成会 GUI
systemctl set-default graphical.targettracker-miner-fs-3
top 里面看这个内存占用最高,通过下面方式禁用
sudo systemctl --global mask tracker-xdg-portal-3.service
sudo systemctl --global mask tracker-miner-fs-3.service
killall tracker-miner-fs-3查看磁盘类型
grep ^ /sys/block/*/queue/rotational如果返回1则表示磁盘可旋转,那么就是HDD了;反之,如果返回0,则表示磁盘不可以旋转,那么就有可能是SSD了。
也可以用 fdisk 命令:
sudo fdisk --list安装 gnome 插件,支持显示桌面文件
sudo apt install -y gnome-shell-extension-desktop-icons-ng重启,在『插件』中配置
输入法
apt purge fcitx* ibus*
apt install fcitx5 fcitx5-chinese-addonslibreoffice 中文包
sudo apt-get install libreoffice
sudo apt-get install libreoffice-l10n-zh-cn libreoffice-help-zh-cn之后在 tool options 中配置中文即可
KDE 桌面
禁用切换应用时的窗口预览
# kwin-addons 通常包含 breeze, compact, small icons 等多种 tabbox 样式
sudo apt install kwin-addons
# ls /usr/share/kwin/tabbox/之后再去设置里配置 Task Switcher 可视化样式。
3 - 从一个粘贴失效的 Bug 深入 Linux 键盘事件栈
从 GNOME 迁移到 KDE Plasma/Wayland 后,用 xremap 把
Alt+V映射成Ctrl+V,在 Brave 浏览器的文本框里失效,但地址栏正常。同样的配置在 GNOME 下运行了数月毫无问题。
这个现象让人困惑:窗口识别正确,权限配置正确,日志无报错,其他应用一切正常——唯独 Brave 文本框不响应。排查过程涉及 Linux 输入子系统、Wayland 协议、合成器差异、Chromium 架构,以及一个一行配置的最终解法。
一、Linux 键盘事件的完整旅程
理解这个问题,首先要清楚一次按键从硬件到应用的完整路径。
1.1 evdev 层:内核与用户空间的边界
当你按下一个键,键盘固件产生扫描码(scancode),内核驱动将其转换为 evdev 事件,写入 /dev/input/eventN:
EV_KEY KEY_LEFTALT 1 # press
EV_SYN SYN_REPORT 0
EV_KEY KEY_V 1 # press
EV_SYN SYN_REPORT 0
EV_KEY KEY_V 0 # release
EV_SYN SYN_REPORT 0
EV_KEY KEY_LEFTALT 0 # release
EV_SYN SYN_REPORT 0
每个事件包含三个字段:类型(type)、编码(code)、值(value)。EV_SYN 是同步信号,标志着一组原子事件的结束。注意:evdev 这一层只有原始的 keycode,没有任何"修饰键状态"的概念,那是上层的工作。
1.2 xremap 的工作层:uinput 虚拟设备
xremap 通过 uinput 内核模块创建一个虚拟输入设备。它从真实键盘的 evdev 节点读取原始事件,按照配置规则变换后,写入虚拟设备。整个过程对 Wayland 合成器完全透明——合成器看到的是虚拟设备发出的事件,不知道中间经过了一层变换。
xremap 的配置分两个阶段处理:
modmap:纯粹的 key-to-key 映射,在硬件 keycode 层面操作。例如:
KEY_LEFTALT: KEY_LEFTMETA # 物理 Alt → Super
KEY_LEFTMETA: KEY_LEFTALT # 物理 Super → Alt
keymap:组合键映射,在 modmap 变换之后的键值上匹配。例如:
Win-v: ctrl-v # modmap 后的 Win(即物理 Alt)+ v → Ctrl+v
1.3 Wayland 合成器:modifier 状态机
Wayland 合成器(KWin、Mutter)从 evdev 或 uinput 读取事件后,维护一个 XKB 状态机来跟踪当前的修饰键状态。XKB(X Keyboard Extension)是一套键盘描述语言,定义了 modifier 的语义:
depressed modifiers = 当前物理按下的修饰键的位掩码
latched modifiers = 已锁存但未释放的修饰键(如 Sticky Keys)
locked modifiers = 已锁定的修饰键(如 Caps Lock)
每当 modifier 键的状态发生变化,合成器会向焦点窗口发送 wl_keyboard.modifiers 事件:
wl_keyboard.modifiers(serial, mods_depressed, mods_latched, mods_locked, group)
关键在于:modifier 状态是异步通知的,客户端需要自己维护一份 XKB 状态副本来解读后续的 key 事件。
二、问题复现:wev 日志的解读
用 wev(Wayland event viewer)在受影响的文本框中按 Alt+V,得到以下输出:
[wl_keyboard] key: key: 37; state: 1 (pressed)
sym: Control_L (65507), utf8: ''
[wl_keyboard] modifiers:
depressed: 00000044: Control Mod4 ← Control + Super 同时在按
latched: 00000000
locked: 00000000
[wl_keyboard] key: key: 133; state: 0 (released)
sym: Super_L (65515), utf8: ''
[wl_keyboard] modifiers:
depressed: 00000004: Control ← Super 才刚释放
latched: 00000000
locked: 00000000
[wl_keyboard] key: key: 55; state: 1 (pressed)
sym: v (118), utf8: '' ← v 按下时 Mod4 已清
等等,这个日志看起来是正确的——v 按下时 depressed 只有 Control,Mod4 已经释放了。
但这是 wev 窗口里观察到的结果。wev 是一个普通 Wayland 客户端,它收到的事件是合成器按顺序分发的,modifier release 确实在 v press 之前到达。
问题发生在 Brave 浏览器里,Brave 打开了 chrome://keyboard-internals 让我们可以直接观察它收到的事件序列——它看到的是 Ctrl+Super+V,而不是 Ctrl+V。
这两者之间的差异,揭示了这个 Bug 的真正所在。
三、根因:合成器事件分发的时序竞争
3.1 uinput 注入的事件序列
xremap 在处理 Win-v → ctrl-v 时,需要:
- 注入
Control_L press - 释放原始的
Super_L(消费掉原始键) - 注入
v press - 注入
v release - 释放
Control_L
实际通过 uinput 写入的事件流大致是:
EV_KEY Control_L 1 (press)
EV_SYN
EV_KEY Super_L 0 (release) ← xremap 发出 Super 的 release
EV_SYN
EV_KEY v 1 (press)
EV_SYN
3.2 合成器的处理流程
KWin 从 uinput 设备读取这些事件,用 libinput 处理后,更新内部 XKB 状态并分发给焦点窗口。关键问题在于:
XKB 状态更新和 wl_keyboard.modifiers 的发送是否在 v press 之前完成?
合成器的事件处理通常在一个 event loop 里,大致流程是:
读取 evdev 事件 → libinput 处理 → 更新 XKB 状态 → 发送 wl_keyboard 事件
在理想情况下,Super_L release 处理完毕(XKB 状态里 Mod4 清掉)之后,再处理 v press。但这里有一个微妙的问题:
xremap 通过 uinput 写入事件时,是批量写入的——Control_L press、Super_L release、v press 几乎同时写入内核缓冲区,两个 EV_SYN 之间的间隔可能只有几微秒。合成器在同一个 event loop tick 里一次性处理这一批事件时,modifier 状态更新和 key 事件分发的顺序取决于合成器的具体实现。
3.3 Mutter vs KWin 的关键差异
这里有一个决定性的证据:GNOME 的 Mutter 在 2014 年合并了一个 patch,专门处理这个问题:
wayland-keyboard: Send modifiers after the key event
The key event should be interpreted by clients with the modifier state
as it was before the event itself just as in X11 input events.
Achieving this in wayland is a matter of sending the key event first
and the modifiers after (if needed).
This isn't really specified in the wayland protocol but it matches
weston's behavior and should avoid corner cases in clients.
Mutter 的策略是:先发 key 事件,再发 modifier 变化通知。也就是说,当 Super_L release 到来时:
- Mutter 先把 key release 事件发给客户端
- 然后再发 modifier 状态变化
这样客户端在处理 Super_L release 时,modifier 状态还是旧的(Super 还在按);在处理后续 v press 时,收到的 modifier 快照里 Mod4 已经清掉了。
KWin 没有这个特殊处理。在 KWin 下,Super_L release 导致的 modifier 状态变化和 v press 的事件分发顺序不确定(或者说,对于批量写入的 uinput 事件,modifier 状态可能在 v press 之前就更新了,但客户端收到 modifier 通知的时机与 key 事件的时机产生了竞争)。
结果是:Brave 在某些时序下收到 v press 时,它的本地 XKB 状态副本显示 depressed = Control | Mod4,即 Ctrl+Super+V。
四、为什么只有 Brave 受影响
4.1 Qt/GTK 对 modifier 的宽松处理
Qt 在处理快捷键时,会对 modifier mask 做一定的过滤。QKeySequence 匹配时,会忽略一些"无关"的修饰键(特别是 Mod4/Super,在大多数应用场景下不是有意义的修饰键)。GTK 的行为类似。
这意味着即使这些 toolkit 收到了 Ctrl+Super+V,它们仍然能匹配到 Ctrl+V 对应的 paste action。
4.2 Chromium 的严格匹配:ui::KeyEvent
Chromium 在 Wayland 下使用自己的 Ozone 平台层处理输入,其核心是 ui::KeyEvent。Chromium 的事件处理链大致是:
Wayland 事件 → OzonePlatformWayland → WaylandKeyboard
→ ui::KeyEvent → AcceleratorManager → 快捷键匹配
Chromium 的 AcceleratorManager 对 modifier 做精确匹配:快捷键 Ctrl+V 对应的 modifier mask 是 EF_CONTROL_DOWN,当实际收到的 event flags 是 EF_CONTROL_DOWN | EF_COMMAND_DOWN(Super 对应 Command),两者不等,paste 动作不触发。
4.3 为什么地址栏(Omnibox)能用
Brave 的地址栏(Omnibox)是 Chromium 里一个特殊的输入控件,它有独立的键盘事件处理路径,对修饰键的匹配比普通 <textarea> 更宽松——Omnibox 会接受带额外修饰键的输入,只要核心键值匹配,就执行对应操作。这是 Chromium 为了支持各种操作系统快捷键习惯而做的兼容性处理。
4.4 为什么 GNOME 下没问题
如上文所述,Mutter 的事件发送顺序保证了客户端在处理 v press 时,看到的 modifier 状态是正确的。KWin 没有这个保证,所以 modifier 残留只在 KDE 下出现。
五、诊断过程
排查这类问题,工具链至关重要。
第一步:确认窗口识别
xremap --watch=config ~/.config/xremap/config.yml
输出:
active window: caption: '...', class: 'brave-browser'
窗口识别正确,排除 application filter 匹配失败。
第二步:确认 uinput 权限
ls -la /dev/uinput # crw-rw----+ 1 root input
groups | grep input # 用户在 input 组
权限正常,排除注入失败。
第三步:用 wev 观察实际事件
wev
在 wev 窗口里按 Alt+V,看到 Control_L press → Super_L release → v press,序列正确。但这是 wev 窗口的视角,不代表 Brave 看到的相同。
第四步:在 Brave 内部验证
打开 chrome://keyboard-internals,按 Alt+V,看到 Brave 接收到的 modifier 是 Ctrl+Super。确认问题:Brave 在 v press 时刻看到了 Mod4 残留。
六、解决方案:keypress_delay_ms
xremap 官方文档(以及 issue #179)记录了这个已知问题:
Some applications have trouble understanding synthesized key events, especially on Wayland.
keypress_delay_mscan be used to workaround the issue.
keypress_delay_ms: 20 # 在顶层配置,全局生效
modmap:
- name: Global
remap:
KEY_LEFTALT: KEY_LEFTMETA
KEY_LEFTMETA: KEY_LEFTALT
# ...
keymap:
# ...
keypress_delay_ms 的作用是在每个注入的 key 事件之间插入一个延迟。在我们的场景下,这意味着:
Control_L press → 等待 20ms
Super_L release → 等待 20ms ← 合成器有充分时间处理这个 release 并通知客户端
v press → Brave 收到时,Mod4 已经清掉了
20ms 是一个经验值。合成器的 event loop 通常以 60Hz(16.7ms/frame)或更高频率运行,20ms 足以保证 modifier release 被完整处理。实践中 10ms 通常也够,取决于系统负载。
这个方案的代价是轻微的输入延迟感——每次触发 keymap 规则时,按键响应会有约 20ms 的延迟。对于复制粘贴这类操作,这个延迟几乎不可感知。
七、更深层的思考
7.1 Wayland 协议的设计取舍
Wayland 协议本身并没有规定 modifier 事件和 key 事件的相对顺序。Wayland Book 里只说:
The modifiers event includes masks of the depressed, latched, and locked modifiers.
顺序是"implementation defined"。Mutter 选择了"key 先于 modifier 通知"的策略,weston(参考实现)也采用了同样的策略,但 KWin 的行为不同。这是 Wayland 生态里一个典型的合成器行为碎片化问题。
7.2 X11 为什么没有这个问题
在 X11 里,每个 XKeyEvent 结构体直接携带了 state 字段(modifier 位掩码),它反映的是该键事件发生时的 modifier 状态,是原子的,不存在异步通知的竞争问题。
typedef struct {
int type; /* KeyPress or KeyRelease */
unsigned long serial;
Bool send_event;
Display *display;
Window window;
...
unsigned int state; /* key or button mask */ ← modifier 内联在事件里
unsigned int keycode;
...
} XKeyEvent;
Wayland 将 modifier 状态拆分成独立的异步消息,在语义上更清晰,但引入了时序依赖。
7.3 uinput 注入的固有局限
xremap 通过 uinput 在内核层面注入事件,这是目前在 Wayland 下实现全局键盘重映射的唯一可行方案(Wayland 的安全模型不允许用户态程序拦截其他应用的键盘输入)。但 uinput 注入的事件和真实硬件事件在时序上有本质差异:
真实硬件:键盘固件保证 modifier 按下和字符键按下之间有物理时间间隔(人的手指按键速度)
uinput 注入:所有事件几乎同时写入,依赖合成器能正确处理"同一批次"内的事件顺序
keypress_delay_ms 本质上是在用人工延迟模拟这个物理时间间隔,让合成器的状态机有时间"追上"事件流。
总结
| 层次 | 组件 | 角色 |
|---|---|---|
| 内核 | evdev / uinput | 原始 keycode 事件流 |
| 重映射 | xremap | 拦截并变换事件,通过 uinput 注入 |
| 合成器 | KWin / Mutter | 维护 XKB modifier 状态,分发给客户端 |
| 工具包 | Qt / GTK / Chromium Ozone | 接收 Wayland 事件,构建本地 modifier 快照 |
| 应用 | Brave textarea | 用精确 modifier 匹配触发 paste action |
问题的本质是:uinput 批量注入事件导致 modifier release 和 key press 之间没有时间间隔,KWin 在分发事件时 modifier 状态尚未更新到客户端,Chromium 的严格 modifier 匹配导致快捷键失效。
Mutter 通过"key 事件先于 modifier 通知"的策略规避了这个问题,KWin 没有,所以同样的配置从 GNOME 迁移到 KDE 后才暴露了这个 Bug。
最终解法一行配置:
keypress_delay_ms: 20
用 20ms 的人工延迟换取 modifier 状态的正确传播,代价极小,效果稳定。
4 - 如何正确配置 font.conf 优化系统字体显示
在 Linux 系统中,字体的渲染和管理常常让新手头疼。尤其是在全新安装的 Debian 13 (Trixie) 上,默认的字体可能存在发虚、锯齿、或者中文字体回退(Fallback)不理想的情况。
其实,Linux 拥有非常强大的字体配置工具 —— Fontconfig。今天这篇博客就教大家如何通过编写 fonts.conf 配置文件,完美解决 Debian 13 的字体渲染与默认字体替换问题。
一、千万别动 /etc/fonts/fonts.conf!
很多网上的老教程会让你直接修改 /etc/fonts/fonts.conf。千万不要这样做!
在 Debian 13 中,该文件由软件包管理器直接维护。一旦系统更新 fontconfig 驱动,你的修改就会被彻底覆盖。
正确的配置文件存放位置:
- 当前用户生效(推荐):
~/.config/fontconfig/fonts.conf- 注:如果文件夹不存在,可以手动创建:
mkdir -p ~/.config/fontconfig
- 注:如果文件夹不存在,可以手动创建:
- 全系统所有用户生效:
/etc/fonts/local.conf
二、Fontconfig 配置文件的基本结构
Fontconfig 的配置文件使用的是 XML 格式。无论你要做什么配置,文件都必须包含以下的基础骨架:
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">
<fontconfig>
<!-- 你的自定义字体规则写在这里 -->
</fontconfig>
三、实战案例:常用配置代码段
你可以把下面的代码段组合放进你的 fonts.conf 中。
1. 开启抗锯齿与次像素渲染(告别模糊与锯齿)
现在的显示器基本都是 LCD/OLED,开启次像素渲染(Subpixel rendering)可以让字体边缘更清晰,减少眼睛疲劳。
<match target="font">
<!-- 开启抗锯齿 -->
<edit name="antialias" mode="assign"><bool>true</bool></edit>
<!-- 开启字体微调(Hinting) -->
<edit name="hinting" mode="assign"><bool>true</bool></edit>
<!-- 微调程度:略微(slight),这是目前最现代的渲染方式 -->
<edit name="hintstyle" mode="assign"><const>hintslight</const></edit>
<!-- 像素排列顺序,大部分显示器是 rgb -->
<edit name="rgba" mode="assign"><const>rgb</const></edit>
<!-- 开启默认的 LCD 过滤器 -->
<edit name="lcdfilter" mode="assign"><const>lcddefault</const></edit>
</match>
2. 自定义系统默认字体(无缝替换 Sans、Serif、Monospace)
当网页或者应用请求“无衬线字体 (sans-serif)”或“等宽字体 (monospace)”时,你可以指定系统优先调用你最喜欢的字体(例如 Noto Sans CJK SC 或 Hack)。
<!-- 优雅的无衬线字体设置(如网页、软件界面) -->
<match target="pattern">
<test qual="any" name="family"><string>sans-serif</string></test>
<edit name="family" mode="prepend" binding="strong">
<string>Noto Sans CJK SC</string> <!-- 优先中文字体 -->
<string>Noto Sans</string> <!-- 英文 fallback -->
</edit>
</match>
<!-- 程序员专属:等宽字体设置(写代码、终端) -->
<match target="pattern">
<test qual="any" name="family"><string>monospace</string></test>
<edit name="family" mode="prepend" binding="strong">
<string>Hack</string> <!-- 优先等宽英文 -->
<string>Noto Sans Mono CJK SC</string> <!-- 优先等宽中文 -->
</edit>
</match>
3. 强制字体替换(解决特定软件字体难看的问题)
如果某个老旧软件强制调用 Helvetica 字体,而在 Linux 下渲染很难看,你可以把它强制映射为 Inter 或 Arial:
<match target="pattern">
<test qual="any" name="family"><string>Helvetica</string></test>
<edit name="family" mode="assign" binding="same"><string>Inter</string></edit>
</match>
四、让配置生效与高级调试命令
保存好你的 fonts.conf 文件后,配置不会立即生效。我们可以通过 Debian 终端自带的 fc- 系列命令来管理、刷新和测试字体。
1. 刷新字体缓存
每当修改了配置文件,或在 ~/.local/share/fonts 中放入了新字体文件,都需要运行以下命令强制刷新缓存:
fc-cache -fv
(如果是系统级配置 /etc/fonts/local.conf,请加上 sudo fc-cache -fv)
2. 检查默认字体匹配(测试你的配置)
想知道现在系统在调用别名(如 monospace 或 sans-serif)时,实际绑定的是哪个字体?
# 查看系统默认的等宽字体
fc-match "monospace"
# 查看系统默认的无衬线(网页常用)字体
fc-match "sans-serif"
3. 查找系统中已安装的特定字体
如果你不确定某个中文字体在系统里的确切英文名称(例如:思源黑体的全称),可以用 grep 过滤查找:
# 列出系统内所有包含 "Noto" 字样的已安装字体
fc-list : family | grep -i "noto" | sort -u
4. 调试字体匹配轨迹(排查字体未生效问题)
如果你发现自己配了 fonts.conf 但系统死活不认,可以开启调试模式,查看某一特定请求在 Fontconfig 内部是如何流转和被替换的:
# 跟踪测试匹配 "sans-serif" 的全过程(终端会输出详细的 XML 匹配规则命中轨迹)
FC_DEBUG=1 fc-match "sans-serif"
5. 图形化向导配置法(Debian 专属)
如果你不想手写过多的 XML,也可以使用 Debian 系统自带的基础配置向导,来快速调整系统的位图字体、次像素平滑等全局开关:
sudo dpkg-reconfigure fontconfig-config
五、结语
通过几行简单的 XML 代码以及强大的 fc- 终端命令,我们就能彻底掌握 Debian 13 的字体主动权。相比于盲目安装各种桌面环境的第三方优化工具,直接通过 Fontconfig 配置文件管理字体更加底层、高效,而且可以在任何桌面环境(GNOME, KDE, XFCE, i3wm)下通用。
赶快去试试吧!如果你在配置过程中遇到了字体发虚或排版错乱的问题,欢迎在评论区留言交流!
如果你觉得这篇文章对你有帮助,不妨点个赞或者分享给更多正在使用 Debian 的小伙伴吧!
5 - 适合你的 Linux 桌面界面是什么?
https://thenewstack.io/whats-the-right-linux-desktop-ui-for-you/
如果你以前从未用过 Linux,而现在正打算尝试,那么你不可避免地会遇到一件事情,那就是选择。
在 Linux 中,你可以选择你的发行版、内核、初始化系统(init system)、文件系统类型、引导加载程序、默认应用程序以及你的桌面环境。
甚至在你深入探索这个“无底洞”之前,你就会发现桌面环境(Desktop Environment)和窗口管理器(Window Manager)之间是有区别的。
对某些人来说,这很快就会让人感到无所适从。
这就是我在这里帮助你理清这些桌面选择的原因。
你准备好了吗?
窗口管理器与桌面环境的区别
你可能会问自己的第一个问题是:“桌面环境和窗口管理器有什么区别?”
桌面环境是一套提供图形用户界面的工具和应用程序,包括面板、菜单和文件管理器等功能。而窗口管理器则专门负责管理应用程序窗口的外观和行为。
更让人困惑的是,每个桌面环境都有一个窗口管理器。例如,GNOME 使用 Mutter,而 KDE Plasma 使用 KWin。
而更让人头疼的是,有些窗口管理器设计之初就是为了直接充当你的桌面用户界面,而不需要安装桌面环境。
啊!这实在是太多太乱了。
不,其实这比你想象的要简单得多。
因为窗口管理器和桌面环境都可以作为你的桌面用户界面,所以我将对两者都进行介绍。在继续之前,我不会讨论市面上的每一个窗口管理器和桌面环境,因为实在太多了。我主要会介绍那些我认为对 Linux 新手和老手来说都是绝佳选择的桌面环境和窗口管理器。
准备好了吗? 我们开始吧。
GNOME
让我们先从重头戏开始,GNOME 是我所称的 Linux“三大桌面”之一。GNOME 是“极简主义者的梦想成真”。GNOME 背后的核心理念是“不碍事”,好让你能够专心于你需要做的任何事情。
这并不是说 GNOME 缺乏功能。这是一个功能齐全的桌面环境,包含了你保持高效工作所需的一切。最大的区别在于,你不会看到一个用于启动应用程序的常规桌面菜单,而是需要打开“应用程序概览”(Application Overview)。在应用程序概览中,你可以手动找到想要运行的应用程序、搜索想要运行的应用程序,或者将应用程序固定到 Dash 上。
Dash 实际上就是你的面板,只不过它平时是隐藏起来的。

如果你不喜欢收藏夹栏被隐藏起来,你可以安装 GNOME 扩展(Extensions),比如 Dash To Panel 或 Dash to Dock。有成百上千的 GNOME 扩展可供选择,它们可以扩展你桌面的功能集。
GNOME 适合谁? GNOME 非常适合那些不希望常规桌面琐碎元素碍眼的极简主义者。GNOME 基本上是一张白纸,让你在没有日常干扰的情况下做你想做的事。
KDE Plasma

这种默认布局非常易于使用。一个毫无 Linux 经验的 Windows 用户登录到 KDE Plasma 桌面后,也能立刻知道如何使用它。
当然,你越使用 KDE Plasma,就越想去定制它。你可以手动进行定制,也可以下载全局主题。在即将发布的 6.6 版本中,你将能够自定义你的桌面,并将其保存为全局主题。
KDE Plasma 是我通常向 Linux 新手推荐的桌面。原因有几个:首先,它简单易用。其次,它非常快速且稳定。KDE Plasma 还是那种独特的、能伴随你对 Linux 的深入了解而一同成长的桌面。起初,你会保持默认配置。随着你学到更多,你会发现自己想要对它进行微调,以更好地适应你的工作流。几个月或几年后,你最终可能会拥有一个完全独一无二的 KDE Plasma 桌面。
KDE Plasma 适合谁? 我通常会说 KDE Plasma 适合所有类型的用户,尤其是那些注重美学的用户。如果你想要一个最实用、最美观的桌面,KDE Plasma 是不二之选。
Cinnamon

Cinnamon 是 Linux Mint 的默认桌面,而 Linux Mint 恰好是目前最受欢迎的 Linux 发行版之一。如果 Linux Mint 足够好,能得到我尊敬的同事 Steven J. Vaughan-Nichols 的青睐,那么它对任何人来说都足够好了。相信我,他比大多数人都更懂开源。
说真的,Linux Mint 是我通常建议那些想在 Linux 世界“试水”的人使用的发行版。其中一个非常重要的原因就是 Cinnamon。
Cinnamon 诞生于 GNOME 2 向 GNOME 3 演变时期,当时的 GNOME 3 与用户习惯的界面大相径庭。很大一部分 GNOME 用户不想要这样的改变,于是他们基于 GNOME 2 分叉(fork)开发了 Cinnamon。
Cinnamon 几乎是一个通用型桌面,这意味着它拥有你已经习惯的所有元素。如果你是 Windows 用户,在 Cinnamon 上你会感到如鱼得水。
Cinnamon 适合谁? 任何人。说真的,任何人都可以使用 Cinnamon,无论你是从未用过 Linux,还是已经用了几十年。
Xfce

与 Cinnamon 一样,Xfce 也会让你立刻感到熟悉。它的默认配置包括一个面板、桌面菜单、系统托盘和可点击的图标。虽然 Cinnamon 的可定制性已经很强,但市面上很少有桌面能像 Xfce 那样被随意改造和折腾。
最重要的是,Xfce 的运行速度极快。和 Cinnamon 一样,Xfce 被归类为轻量级桌面环境,但就速度而言,我必须把这一票投给 Xfce。这也是为什么如此多的轻量级 Linux 发行版默认采用 Xfce 的原因之一。
如果你想知道 Cinnamon 和 Xfce 之间有什么区别,可以考虑这一点:Xfce 是老旧硬件的最佳桌面之一。如果你身边有一台闲置的老机器,你应该安装一个默认采用 Xfce 的 Linux 发行版(例如 Xubuntu),然后看着那台电脑运行得像新买的一样。Xfce 不提供 3D 加速(而 Cinnamon 提供),所以它的动画过渡可能不会那么快速和流畅。
本质上,Xfce 是一个高度可配置的桌面,但它没有现代 UI 的那些花哨功能。
Xfce 适合谁? Xfce 适合那些注重速度而非外观、且可能有一台想要复活的老旧电脑的用户。Xfce 也适合那些喜欢折腾桌面布局、但不太看重视觉特效的人。
i3

好了,我们要偏离常规,涉足一种不同类型的桌面:平铺式窗口管理器(Tiling Window Manager)。什么是平铺式窗口管理器?最简单的理解方式是,平铺式窗口管理器会自动帮你决定应用程序窗口的摆放位置。
更棒的是,平铺式窗口管理器在最大化利用桌面空间方面表现出色。
你打开第一个应用时,它会占满整个屏幕。打开第二个应用时,它会自动与第一个应用平分屏幕。打开第三个应用时,它会与第二个应用平分屏幕的右侧。打开第四个应用时,它会与第一个应用平分屏幕的左侧。
起初这可能看起来有点让人困惑,但好消息是,i3 是一款非常适合从未用过平铺式窗口管理器的用户的工具。
关于平铺式窗口管理器,需要记住的一点是,它们通常只使用键盘操作。你用键盘打开应用、切换你想要使用的应用的焦点、移动瓷砖布局等。你完全可以使用平铺式窗口管理器而一次都不碰鼠标。
正因如此,平铺式窗口管理器通常被认为效率极高,特别是对于开发人员和高度依赖多任务处理的人群。
i3 适合谁? 我认为在尝试平铺式窗口管理器之前,你需要有一些 Linux 的使用经验。但如果你觉得自己准备好了,i3 是最佳的入门之选。
不过,我不认为 i3 是一个适合 Linux 新手的入门之选,除非你极其确定自己想要一种最高效的应用交互方式。
是的,除了我在这里列出的这些,还有很多其他的桌面环境(DE)和窗口管理器(WM)。例如,我最喜欢的是 COSMIC,它还比较新。但如果要我向那些对现状感到厌倦、或是想要转向 Linux 的人推荐一款用户界面,可以肯定的是,上述几款会是我的首选推荐。
6 - 用于开发的最佳 Linux 发行版
在过去的几年里,Linux 在终端用户和开发者中的普及程度一直在缓慢提升。原因有很多,例如 Windows 10 的终结、易用性、灵活性、可靠性、游戏,以及……开发。
没错,Linux 是一个卓越的开发平台。它不仅拥有你所需的所有工具,而且这些工具通常是免费、开源且易于安装的。除此之外,你还拥有 Docker、Podman、Kubernetes、虚拟机 (VM) 等等。
通常来说,列出适合终端用户的最佳 Linux 发行版很容易,但涉及到开发时,可以肯定各方意见会层出不穷。大多数时候,这些意见更多地围绕着某个开发者具体在使用哪种发行版,而较少关注“无论我使用什么,这才是适合这项工作的正确工具”。
我将列表缩减到了五个不同的发行版。我承认我最常用的发行版就在这个名单上,但我向你保证,即使我没有使用它十年之久,我依然会推荐它。
请记住,几乎任何 Linux 发行版都可以转变为开发机。只要安装正确的工具链,添加你喜欢的语言和 IDE,融入容器运行时引擎,你就可以开始运行了。
但我还是想重点介绍我认为市场上最佳选择的那些发行版。我们开始吧。
1. Debian
Debian 被称为“发行版之母”是有原因的。Debian 是 Ubuntu 的基础,而大量的发行版又是基于 Ubuntu 的。没有 Debian,就没有 Ubuntu。此外,Debian 是现有的最坚如磐石的操作系统之一,这绝非夸张。原因在于 Debian 采用保守的发布周期、经过严耕筛选的应用程序以及快速且安全的更新。
除了稳定性之外,你还可以通过 Debian 仓库获得海量的安装软件、强大且用户友好的包管理器以及多架构支持。开发者还可以根据需要选择分支,从稳定版 (stable)、测试版 (testing) 到前沿版 (bleeding edge)。Debian 运行快速且高度可定制,有不同的桌面环境可供选择。Debian 还拥有庞大的用户群,这意味着无论你遇到什么问题,都可以轻松找到支持。
最后,Debian 非常安全。Debian 与基于 Ubuntu 的发行版的一个不同之处在于,它默认不为标准账户启用 sudo 权限。如果你需要执行需要管理员权限的操作,你需要使用传统方式通过 su 切换到 root 用户。当然,如果你愿意,你也可以将标准用户添加到 sudo 组中,以获得更便捷的体验。
2. Fedora
对许多人来说,Fedora 是开发的必然选择。其中一个主要原因是 Fedora 是一个专注于新技术的平台,通常比其他发行版更早采用新软件。Fedora 是首批改用 Wayland、使用 Btrfs 的发行版之一,并且总是在任何其他发行版发布之前就能享受到最新版本的 GNOME。由于它随附新版本的软件,你可以放心,你的工具链应用(如 GCC)和语言(如 Python)都将是可用的最新版本。因此,你开箱后需要安装或升级的软件会更少,也无需添加非官方仓库来获取最新版本。Fedora 还包含以开发者为中心的工具,如编译器和 IDE,还有用于创建可复现开发环境的 toolbox 命令。Fedora 还附带了 GNOME Boxes 应用程序。这款应用让你能够非常轻松地启动虚拟环境,而无需处理 VirtualBox 或其他虚拟机工具的繁琐操作。尽管 Fedora 倾向于“前沿”,但它依然非常可预测、可靠且稳定。最后,Fedora 拥有庞大的社区,因此寻找支持一点也不困难。
3. Pop!_OS
没错,Pop!_OS 是我的首选发行版。现在 System76 推出了 COSMIC 桌面,它变得更出色了。关于 COSMIC,你首先会注意到的是它的速度极快。原因在于该桌面环境是用 Rust 编写的,这是一种运行速度很快的语言。除了速度之外,Pop!_OS 实际上是专为创作者打造的。另一个让 Pop!_OS 脱颖而出的功能是能够随时启用或禁用平铺式窗口管理。如果你需要更高效的桌面环境,就开启平铺功能;如果不需要,就保持关闭。
Pop!_OS 出现在这个名单上的另一个非常重要的原因是,System76 为 NVIDIA 和 AMD GPU 都提供了专门的 ISO 镜像。通过选择正确的 ISO,你无需担心为你的显卡安装驱动程序。安装了适当的 NVIDIA 驱动后,你在进行机器学习 (ML) 和人工智能 (AI) 开发时会轻松得多。Pop!_OS 使用 APT 包管理器,因此你会受益于广泛的软件选择。此外,你还可以通过 Flatpak 获得更多选项。最后,Pop!_OS 开箱即提供全盘加密,这意味着如果你的系统或驱动器丢失或被盗,你无需担心数据被访问。
4. openSUSE
首先,openSUSE 有两个不同的版本:Tumbleweed 和 Leap。Tumbleweed 是滚动更新发行版,这意味着你将始终拥有最新、最好的软件。openSUSE 与其他滚动更新发行版的区别在于,开发团队使用 openQA 测试框架来确保高度的稳定性,这在其他滚动更新版中是不常见的。或者,如果你更喜欢长期支持,可以选择 Leap 版本。你还将获得一些特定于构建者的工具,例如 Open Build Service(一个强大的基于 Web 的工具,用于简化软件构建和分发过程)、YaST(一个强大且全面的管理工具,允许开发者一键安装所有必要的 -devel 软件包)以及 Btrfs 文件系统(包含快照功能,可以轻松回滚到之前的状态)。openSUSE 也非常适合通过 Docker、Podman 和 Kubernetes 进行开箱即用的容器化开发。
5. Linux Mint
如果你问任何开发者为什么要选择 Linux Mint,答案很简单:因为它太好用了。这种用户友好性意味着你可以毫无问题地运行操作系统,并获得一种既简单又可靠的用户体验。Linux Mint 也受益于 Ubuntu 基础,因此即使安装所有必要的构建组件,也只需一条命令:sudo apt-get install build-essential -y。该软件包安装了从源代码编译和构建软件所需的一整套核心工具和库。你将获得 C 和 C++ 编译器、GNU Make、标准 C 和 C++ 库头文件、dpkg-devel 等等。Linux Mint 默认使用 Cinnamon 桌面,它让人感觉非常亲切、快速且稳定。在开发环境中,你还需要什么呢?