Ubuntu 下 Flameshot 截图一直闪屏,已找到问题并解决
昨天还在正常用 Flameshot,今天重启电脑后,一进入截图界面,屏幕就开始不停地闪,按 Esc 退出才恢复。
最后解决了,桌面也保留在 125% 缩放。
把过程记一下,遇到相同问题的朋友可以参考。
一开始查错了方向
我的系统是 Ubuntu 22.04.5,GNOME 桌面,用的是 X11。Flameshot 版本是 11.0.0,笔记本有核显和 NVIDIA 独显。
最初怀疑是显卡的问题,去 NVIDIA Settings 里找 Allow Flipping,结果连网上说的 X Screen 0 都没有。后来确认机器处于 on-demand 模式,屏幕由核显负责显示,独显按需使用,这条路就先放下了。
接着重启了 GNOME Shell,又用一份临时配置启动 Flameshot,还是闪。调整 Qt 的自动缩放变量也没用。
真正有用的是一个很简单的测试:把桌面缩放从 125% 改成 100%。
不闪了。
但我的屏幕是 2560 × 1600,100% 下的字和窗口太小,看着费劲。总不能每次截图前都去改一遍缩放。
日志里能看到它在反复切换尺寸
检查显示状态用的是这几条命令:
bash
echo "$XDG_CURRENT_DESKTOP / $XDG_SESSION_TYPE"
xrandr --current
xrandr --verbose
gsettings get org.gnome.mutter experimental-features
系统开启了 x11-randr-fractional-scaling。125% 缩放时,桌面的虚拟尺寸是 4096 × 2560;闪屏期间,Xorg 日志一直重复这两行:
text
modeset(0): Allocate new frame buffer 2560x1600 stride
modeset(0): Allocate new frame buffer 4096x2560 stride
这里变的是画面缓冲区尺寸。屏幕面板还是原来的分辨率,只是桌面内部的画面尺寸在来回切换。
GitHub 上也有类似的报告:125% 下截图持续闪烁,100% 下正常。Flameshot 的 FAQ 则提到了 Qt 在分数缩放和全屏应用上的兼容问题。
到这里,排查方向就转到了截图窗口和桌面合成的处理上。至于为什么昨天没事、重启后才出现,目前还没查清。
保留 125%,只处理 Flameshot 窗口
这次有效的办法,是给截图窗口加上这个属性:
text
_NET_WM_BYPASS_COMPOSITOR = 2
按 EWMH 规范,2 表示请求这个窗口继续经过合成器处理。在我的环境里,加上后就不再反复切换画面尺寸了。
中间还有个小插曲:第一次改完确实不闪了,但我自己重新输入 flameshot gui,又开始闪。
原因是刚才只改了那一个窗口。下一次截图会创建新窗口,之前的属性自然就没了。
所以最后用了一个 Python 脚本,监听新显示的窗口,遇到 Flameshot 的截图覆盖窗口,就自动设置这个属性。这样托盘、快捷键和原来的命令都能照常用。
下面是实际用的脚本。保存到 ~/.local/bin/flameshot-compositor-workaround,目录不存在就先创建:
bash
mkdir -p "$HOME/.local/bin"
python
#!/usr/bin/python3
"""Keep Flameshot capture overlays composited on GNOME X11 fractional scaling.
GNOME can otherwise repeatedly switch the framebuffer size while the overlay
is visible. This changes only the overlay's _NET_WM_BYPASS_COMPOSITOR hint.
"""
import fcntl
import os
import sys
from pathlib import Path
from Xlib import X, Xatom, display, error
if os.environ.get('XDG_SESSION_TYPE') != 'x11':
sys.exit(0)
runtime = os.environ.get('XDG_RUNTIME_DIR')
if not runtime:
sys.exit('XDG_RUNTIME_DIR is unavailable')
lock = open(Path(runtime) / 'flameshot-compositor-workaround.lock', 'w')
try:
fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
except BlockingIOError:
sys.exit(0)
connection = display.Display()
connection.set_error_handler(lambda *_: None)
root = connection.screen().root
hint = connection.intern_atom('_NET_WM_BYPASS_COMPOSITOR')
root.change_attributes(event_mask=X.SubstructureNotifyMask)
connection.sync()
def keep_composited(window):
try:
cls = window.get_wm_class() or ()
if not any(s.lower() == 'flameshot' for s in cls):
return
if not window.get_attributes().override_redirect:
return
prop = window.get_full_property(hint, Xatom.CARDINAL)
if prop is None or list(prop.value) != [2]:
window.change_property(hint, Xatom.CARDINAL, 32, [2])
connection.flush()
except (error.XError, error.DisplayError):
pass
for window in root.query_tree().children:
keep_composited(window)
connection.flush()
while True:
event = connection.next_event()
if event.type == X.MapNotify:
keep_composited(event.window)
脚本依赖 python-xlib。可以先检查一下:
bash
/usr/bin/python3 -c 'from Xlib import display'
如果提示找不到 Xlib,Ubuntu 下安装 python3-xlib:
bash
sudo apt install python3-xlib
在桌面的终端里启动它:
bash
nohup /usr/bin/python3 "$HOME/.local/bin/flameshot-compositor-workaround" \
> /tmp/flameshot-compositor-workaround.log 2>&1 < /dev/null &
然后再试 flameshot gui。记得退出截图后多开几次,也试试屏幕边缘的框选,确认新窗口都正常。
加上自启动,以后不用手动管
确认有效后,再加一个登录自启动项:
bash
mkdir -p "$HOME/.config/autostart"
cat > "$HOME/.config/autostart/flameshot-compositor-workaround.desktop" <<EOF
[Desktop Entry]
Type=Application
Name=Flameshot fractional scaling workaround
Exec=/usr/bin/python3 "$HOME/.local/bin/flameshot-compositor-workaround"
TryExec=/usr/bin/python3
Terminal=false
NoDisplay=true
X-GNOME-Autostart-enabled=true
EOF
我这边连续重新打开了三次截图,日志里没有再出现尺寸切换,实际框选也正常。现在桌面还是 125%,不用为了截图把整个界面调小。
这个办法只在我这套 GNOME + X11 环境里验证过,是绕过兼容问题的处理。Wayland 不适用,脚本检测到 Wayland 会直接退出。
如果以后升级后不再需要它,删除脚本和自启动项,再注销登录即可:
bash
rm "$HOME/.local/bin/flameshot-compositor-workaround"
rm "$HOME/.config/autostart/flameshot-compositor-workaround.desktop"