环境: Ubuntu 26.04、GNOME(Wayland 会话)、Mutter 合成器、默认终端 Ptyxis、文件管理器 Nautilus,分数缩放 167%。fzf 由
source <(fzf --zsh)提供,终端里Alt+C已能模糊搜目录并cd。
终端里 fzf 的 Alt+C 很顺手:模糊搜当前目录下的子目录,回车就 cd 进去。自然就想要一个桌面级的等价物——在任何地方按一个全局快捷键,弹出模糊选择器从主目录 $HOME 往下找目录,选中后直接用 Nautilus 打开(而不是 cd)。
听上去十分钟的事。实际在 GNOME Wayland 上连撞两堵墙,记录一下,免得别人(和未来的我)重踩。
目标拆解
桌面级触发,跟终端无关,所以分两半:
- 一个模糊选择器:列出
$HOME下所有目录,让我打字过滤、选一个。 - 把选中的目录交给 Nautilus 打开。
- 用 GNOME 全局快捷键(比如
Super+E)把整件事串起来。
第 2、3 步都好办。难的全在第 1 步:在 GNOME Wayland 上找一个能用的 dmenu 式 GUI 模糊选择器。
第一条死路:fuzzel —— Mutter 不支持 layer-shell
fuzzel 是 Wayland 原生的轻量 dmenu 启动器,看着是首选。装上,把目录列表喂给它:
fdfind --type d --hidden --exclude .git --exclude .cache --exclude node_modules . "$HOME" \
| fuzzel --dmenu --prompt 'dir❯ '
窗口没弹出来,直接报错:
err: wayland.c:2321: compositor is missing support for the Wayland layer surface protocol
err: fdm.c:133: no such FD: 7
根因:fuzzel(以及 wofi、tofi、rofi-wayland 等一票 Wayland 原生菜单)靠 wlr-layer-shell 协议把自己叠在屏幕最上层。这个协议是 wlroots 系合成器(sway、Hyprland)的东西,GNOME 的 Mutter 从不实现它。在 GNOME 上,这些工具一个都起不来。
第二条死路:rofi —— 走 XWayland 弹出来却抓不到键盘
那退一步用老牌的 rofi?Ubuntu 26.04 的 rofi 包已经是 Wayland 原生构建,同样要 layer-shell:
$ printf 'a\nb\nc\n' | rofi -dmenu -matching fuzzy
(process:177616): Wayland-ERROR **: Rofi on wayland requires support for the layer shell protocol
但 rofi 还带 X11 后端。清掉 WAYLAND_DISPLAY,逼它走 XWayland:
printf 'a\nb\nc\n' | env -u WAYLAND_DISPLAY rofi -dmenu -matching fuzzy
这次窗口确实弹出来了(XWayland 在 GNOME 下是现成的),没有报错。看着像成了——直到你开始打字:
光标在 rofi 里闪,但敲的字一个都进不去,全跑到背后那个原来有焦点的窗口里了。
这是 XWayland 应用在 GNOME Wayland 下的经典毛病:rofi 想做键盘 grab(独占抓取),而 Mutter 不给 XWayland 客户端这种全局抓取。窗口画出来了,却拿不到键盘输入。对一个"靠打字过滤"的选择器来说,等于废了。
结论:GNOME Wayland 上 dmenu 式 GUI 选择器基本没戏
把两条路并一起看,结论很干脆:
| 方案 | 结果 | 原因 |
|---|---|---|
| fuzzel / wofi / tofi / rofi-wayland | 起不来 | 要 wlr-layer-shell,Mutter 不支持 |
| rofi(X11)/ dmenu 走 XWayland | 弹得出、打不进字 | Mutter 不给 XWayland 键盘 grab |
想在 GNOME Wayland 上要一个"悬浮的、能打字过滤的 GUI 菜单",原生这条路目前是堵死的。
那就别跟它较劲。一个普通的终端窗口,是能正常拿到键盘焦点的——那就在终端窗口里跑 fzf,跟我本来就喜欢的 Alt+C 同款手感,只是结尾换成开 Nautilus。
可行方案:开一个终端窗口跑 fzf
思路两段式:
- 启动器脚本:被全局快捷键调用,开一个新终端窗口,在里面运行选择器。
- 选择器脚本:在终端窗口内用 fzf 选目录,选完用 Nautilus 打开。
用当前默认终端 Ptyxis 就行,它支持 ptyxis --new-window -- 命令。
启动器 ~/.local/bin/nautilus-fuzzy-dir.sh:
#!/usr/bin/env bash
# 全局快捷键入口:开一个 Ptyxis 新窗口跑 fzf 选择器。
# (GNOME/Mutter 不支持 layer-shell,fuzzel/rofi 等 GUI 选择器无法抓键盘,
# 故走终端窗口内的 fzf —— 与 Alt+C 同款体验。)
exec ptyxis --new-window -T "模糊搜目录 → Nautilus" \
-- "$HOME/.local/bin/nautilus-fuzzy-pick.sh"
用别的终端也一样:ghostty 是
ghostty -e 命令,gnome-terminal/kgx 是-- 命令。挑你顺手、字体锐利的那个即可。
选择器脚本,以及 Nautilus 打不开的坑
第一版选择器很直白:fd 列目录 → fzf 选 → nautilus "$sel"。结果选完回车,Nautilus 没弹窗。
问题出在生命周期。fzf 一退出,选择器脚本也就跑完,Ptyxis 立刻关掉这个窗口,给整个进程组发 SIGHUP——刚被拉起来的 Nautilus 子进程还没把"开窗"的 D-Bus 请求发完,就被一起收走了。
两处一起改才稳:
- 用
setsid把 Nautilus 拉进新会话,脱离 Ptyxis 那棵进程树,关窗的 SIGHUP 打不到它。 - 用
gio open而不是nautilus 路径——前者走 D-Bus 让已有的 Nautilus 服务开窗,最稳;再sleep 1给请求一点发出去的时间。
选择器 ~/.local/bin/nautilus-fuzzy-pick.sh:
#!/usr/bin/env bash
# 在终端窗口内运行:fzf 模糊搜 $HOME 下的目录,选中后用 nautilus 打开。
set -uo pipefail
# 列目录:优先 fd/fdfind(快、自动忽略 .git/gitignore),否则 find + prune
if command -v fd >/dev/null 2>&1; then
FD=fd
elif command -v fdfind >/dev/null 2>&1; then
FD=fdfind
else
FD=""
fi
if [ -n "$FD" ]; then
list=$("$FD" --type d --hidden \
--exclude .git --exclude .cache --exclude node_modules \
. "$HOME")
else
list=$(find "$HOME" \
\( -type d -name .git -o -type d -name .cache -o -type d -name node_modules \) -prune -o \
-type d -print)
fi
# fzf 在终端窗口内交互;Esc/Ctrl-C 取消 → 退出码非 0 → 静默退出
sel=$(printf '%s\n' "$list" | fzf --prompt 'dir❯ ' --reverse --height 100% \
--header '回车=用 nautilus 打开 Esc=取消') || exit 0
[ -z "$sel" ] && exit 0
# setsid 脱离会话后台运行,避免 ptyxis 关窗时 SIGHUP 打断;
# gio open 走 D-Bus 让 Nautilus 开窗;sleep 1 给请求发出去的时间。
setsid gio open "$sel" >/dev/null 2>&1 < /dev/null &
sleep 1
exit 0
两个脚本都记得 chmod +x。
fdfind是 Ubuntu 上fd-find包的二进制名(不叫fd)。脚本两个名都探测,装不装都能跑,不装则回退到find并 prune 掉.cache/.git/node_modules。--hidden会把一堆隐藏配置目录也列进来,嫌杂就去掉它,只搜可见目录。
绑定 GNOME 全局快捷键
最后用 gsettings 在第一个自定义槽 custom0 上建一条快捷键,指向启动器。这里用 Super+E(“E for Explorer”,Win 键即 Super):
KEY=/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/
gsettings set org.gnome.settings-daemon.plugins.media-keys custom-keybindings "['$KEY']"
gsettings set "org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:$KEY" \
name 'Fuzzy dir → Nautilus'
gsettings set "org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:$KEY" \
command "$HOME/.local/bin/nautilus-fuzzy-dir.sh"
gsettings set "org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:$KEY" \
binding '<Super>e'
注意第二行是直接把
custom-keybindings覆盖成单元素列表。如果你已经有别的自定义快捷键,先gsettings get出来,把新路径追加进去,别覆盖。槽位也相应换成custom1、custom2……
按下 Super+E:Ptyxis 弹出一个小窗,里面是 fzf 列表;打字过滤、回车,窗口关闭、Nautilus 在选中目录开窗。和终端的 Alt+C 同一套手感,只是落点从"cd"变成了"文件管理器"。
若 Super+E 没反应,多半是被系统占用了——去「设置 → 键盘 → 查看及自定义快捷键」里换个键,或改上面 binding 那行(比如 '<Super><Shift>F')。
小结
- GNOME Wayland 上,Wayland 原生的 dmenu 类选择器(fuzzel/wofi/rofi-wayland)全军覆没——它们要
wlr-layer-shell,Mutter 不实现。 - 退到 XWayland 的 rofi 也不行——窗口弹得出,但 Mutter 不给它键盘 grab,打字进不去。
- 可靠出路是普通终端窗口里跑 fzf:终端能正常拿键盘焦点,体验还跟 fzf 的
Alt+C一致。 - 收尾那个"Nautilus 不弹窗"是关窗 SIGHUP 杀掉了还没发完 D-Bus 请求的子进程——
setsid脱离会话 +gio open+sleep 1解决。
绕了一圈,最朴素的方案反而最稳:别跟合成器抢悬浮窗,开个终端窗口就完事。