文章目录
- 一、背景
- 二、排查
- (1)检查创建,发现没问题。
- (2)查看 shortcutMap 是否注册
- (3)排查xcb有没有获取到该事件
- (4)排查是否是系统的问题
- (5)www.google.com
- (6)xcb可以正常获取到键值
- (7)继续调试 qt 代码
- (8)被拦截
- (9)谁拦截
- (10)fcitx
- (11)解决
- 1、通过fcitx配置工具修改
- 2、通过配置文件修改
一、背景
该快捷键在 Windows
中是被 Microsoft Office
中使用,我们的程序为了和它保持统一,也是使用的该快捷键方式,在 Window
中是正常的,但在 Linux
下,使用该快捷键无效,虽然使用的是一套代码,但在不同平台下某些插件的实现还是有差异的。
二、排查
(1)检查创建,发现没问题。
代码中使用了 QShortcut
类添加快捷键,绑定激活信号,槽函数执行动作。QShortcut
创建后,会将其添加到全局变量 shortcutMap
,在 qguiapplication_p.h 文件中定义。在代码中打印后,也确实发现了包含了该快捷键。
QKeySequence keySeq(Qt::ControlModifier + Qt::AltModifier + Qt::Key_P);
QShortcut *pShortcut = new QShortcut(keySeq, parent);
connect(pShortcut, &QShortcut::activated, this, [=](){});
(2)查看 shortcutMap 是否注册
每次按键后,qt从xcb事件分发器收集key事件后,都会尝试去捕获有没有注册的快捷键,这里可以打印到用户所有注册到shortcutMap中的快捷键。打印后,包含该快捷键。
bool QShortcutMap::tryShortcut(QKeyEvent *e)
{
Q_D(QShortcutMap);
if (e->key() == Qt::Key_unknown)
return false;
QVector<QShortcutEntry> vec = d->sequences;
for (int i = 0; i < vec.size(); i++)
{
qDebug() << vec[i].keyseq;
}
...
}
(3)排查xcb有没有获取到该事件
xcb是Linux下的底层系统库,所有的鼠标,键盘事件都从这个库收集上来,qt会对所有的事件进行转换为qt内部定义的事件,然后分发给应用程序。如果xcb都没有获取到,那肯定不是应用程序能够兼容的。还是从这个函数内加打印信息,查看到当次按键的键码描述符和键码。
结果:按下CTRL+ALT+P
后,键码值是16777219,理论上应该是80,因为上一个键是O,是正常的,其值是79。所以怀疑是xcb底层获取的有问题。
bool QShortcutMap::tryShortcut(QKeyEvent *e)
{
Q_D(QShortcutMap);
if (e->key() == Qt::Key_unknown)
return false;
qDebug() << __FUNCTION__ << e->key() << e->modifiers();
...
(4)排查是否是系统的问题
旁边同事有的机器是ok的,有的不行。而且测试机x64、arm、也有问题,排除架构的问题。ubuntu和麒麟v10也有问题排除系统。xfce和gnome也有问题排除桌面环境的问题。
(5)www.google.com
有人遇到了这个问题,也是该快捷键不生效,可能是clipit
这个软件造成的原因,其在低版本会占用并锁定五个快捷键,导致它和别的软件都不能使用。在clipit最新的版本修复了这个问题,或者卸载该程序,使用其他程序代替。而我的电脑恰好有这个软件,导致xcb获取错误。
CTRL+ALT+O
CTRL+ALT+P
CTRL+ALT+H
CTRL+ALT+F
CTRL+ALT+A
tips:可以使用xev程序检测,按CTRL+ALT+P,发现P键值为空,什么信息都没有,显示两行0。如果未被锁定,其显示正常的键值,比如下图Shift_L为50。
(6)xcb可以正常获取到键值
不想升级这个程序,我直接卸载了,然后通过xev检测,发现可以抓取到该快捷键,通过调试qt 程序,发现,tryShortcut
函数可以正常获取到该事件了,但是应用程序依然无法使用。
(7)继续调试 qt 代码
1、将xcb事件转换为qt内部的事件
2、qt再将内部事件转发下去。
3、如果keyEvent
也是个handleShortcutEvent
,则将其转发到快捷键事件中。
4、尝试发送快捷键
5、对shortcutMap
进行查询,如果匹配到注册的,就发送激活信号。
tips:如果是正常的链路,则是按如下调用方式。
tryShortcut() => nextState() => find() => dispatchEvent()
尝试调用快捷键 => 状态机查询匹配 => 查找快捷键 =>分发事件(通过CoreApplication::sendEvent)
(8)被拦截
最上层void QXcbKeyboard::handleKeyEvent
,获取到了正确的快捷键事件,但是传递到后面就没有了,怀疑是被拦截了,调试代码后最终发现,在调用filterEvent中被拦截了。
(9)谁拦截
在linux qt
平台下,集成了两种输入法,我当前环境是fcitx,所以走的是上面。而ok的同事使用的是ibus输入法,所以应该是fcitx过滤了。
(10)fcitx
qt中包含了两个fcitx版本的接口,分别是org.freedesktop.portal.Fcitx
和org.fcitx.Fcitx-0
,我的本机使用了第二种,它会通过QDUBS去请求,看是否会过滤,其结果通过processKeyEventResult
返回,返回true,说明fcitx内部进行了处理,qt层不用处理了,需要过滤,那么该事件就被reset,那么也就收不到了。如果fcitx没处理,那么通过forwardEvent,将其发送。
(11)解决
猜测fcitx内部占用了该快捷键,取消即可,自己的应用程序就能使用了。
1、通过fcitx配置工具修改
2、通过配置文件修改
如果fcitx config tool
版本过低,可以通过配置文件修改
sudo vim ~/.config/fcitx/config
// 将这一行
#SwitchPreedit=CTRL_ALT_P
// 改为这一行,保存后,重启电脑即可
SwitchPreedit=
tips:#必须去掉,虽然注释了,fcitx依然会使用默认该快捷键。