fix: guard PanelPopup close behavior against transient X11 grab focus…#1542
Conversation
Reviewer's GuideIntroduces X11 focus-grab aware handling in PopupWindow/PanelPopup so that transient X11 grab/ungrab focus changes do not prematurely close the panel popup, using native XCB focus events, a grab-transition state, and a short QML timer to defer and potentially cancel close-on-inactive behavior per-popup instance. Sequence diagram for X11 grab/ungrab focus handling in PanelPopupsequenceDiagram
participant X11
participant PopupWindow
participant PanelPopup
participant grabInactiveTimer
X11->>PopupWindow: xcb_focus_out_event (mode GRAB)
PopupWindow->>PopupWindow: setX11GrabFocusTransition(true)
PopupWindow-->>PanelPopup: x11FocusOutByGrab()
PanelPopup->>PanelPopup: grabInactivePending = true
PanelPopup->>grabInactiveTimer: start(grabInactiveTimeout)
X11->>PopupWindow: xcb_focus_in_event (mode UNGRAB)
PopupWindow-->>PanelPopup: x11FocusInByUngrab()
alt grabInactivePending is true
PanelPopup->>PanelPopup: grabInactivePending = false
PanelPopup->>grabInactiveTimer: stop()
PanelPopup->>PanelPopup: Qt.callLater(check active)
alt popupWindow.active is false
PanelPopup->>PanelPopup: close()
else popupWindow.active is true
PanelPopup-->>PanelPopup: keep open
end
end
PopupWindow->>PopupWindow: setX11GrabFocusTransition(false)
%% Normal focus loss without grab
X11->>PopupWindow: xcb_focus_out_event (non GRAB)
PopupWindow-->>PanelPopup: active false (property change)
PanelPopup->>PanelPopup: onActiveChanged
alt not x11GrabFocusTransition and not grabInactivePending and not active
PanelPopup->>PanelPopup: close()
end
Updated class diagram for PopupWindow and PanelPopup focus-grab handlingclassDiagram
class QQuickApplicationWindow
class PopupWindow {
QWindow transientParent
bool x11GrabFocusTransition
bool m_dragging
bool m_pressing
bool m_x11GrabFocusTransition
PopupWindow(QWindow *parent)
void mouseReleaseEvent(QMouseEvent *event)
void mousePressEvent(QMouseEvent *event)
void mouseMoveEvent(QMouseEvent *event)
bool nativeEvent(const QByteArray &eventType, void *message, long *result)
bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result)
bool x11GrabFocusTransition()
void setX11GrabFocusTransition(bool transition)
x11GrabFocusTransitionChanged()
x11FocusOutByGrab()
x11FocusInByUngrab()
}
class PanelPopup {
int popupX
int popupY
bool readyBinding
bool grabInactivePending
int grabInactiveTimeout
string windowTitle
function close()
function onActiveChanged()
function onX11FocusOutByGrab()
function onX11FocusInByUngrab()
Timer grabInactiveTimer
}
PopupWindow <|-- QQuickApplicationWindow
PanelPopup ..> PopupWindow : uses
PanelPopup ..> Timer : owns
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- In
PopupWindow::nativeEvent, the parameters are unused on non-Linux builds due to the#ifdef Q_OS_LINUXblock; consider addingQ_UNUSED(eventType),Q_UNUSED(message), andQ_UNUSED(result)in the#elsepath to avoid compiler warnings. - When handling XCB focus events in
nativeEvent, you always cast toxcb_focus_in_event_teven forXCB_FOCUS_OUT; using the matchingxcb_focus_out_event_t(or a shared helper) for the OUT case would make the code clearer and avoid potential confusion. - The 200 ms
grabInactiveTimeoutis a magic number in QML; consider turning it into a named constant or providing a brief comment explaining why this specific duration is chosen.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `PopupWindow::nativeEvent`, the parameters are unused on non-Linux builds due to the `#ifdef Q_OS_LINUX` block; consider adding `Q_UNUSED(eventType)`, `Q_UNUSED(message)`, and `Q_UNUSED(result)` in the `#else` path to avoid compiler warnings.
- When handling XCB focus events in `nativeEvent`, you always cast to `xcb_focus_in_event_t` even for `XCB_FOCUS_OUT`; using the matching `xcb_focus_out_event_t` (or a shared helper) for the OUT case would make the code clearer and avoid potential confusion.
- The 200 ms `grabInactiveTimeout` is a magic number in QML; consider turning it into a named constant or providing a brief comment explaining why this specific duration is chosen.
## Individual Comments
### Comment 1
<location path="frame/qml/PanelPopup.qml" line_range="160-161" />
<code_context>
+ control.grabInactivePending = false
+ grabInactiveTimer.stop()
+
+ Qt.callLater(function() {
+ if (!popupWindow || !readyBinding || !popup.visible) {
+ return
+ }
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Re-check currentItem / grabInactivePending in the deferred close logic to avoid closing a different popup.
Because there’s a delay between scheduling `Qt.callLater` and its execution, `popupWindow.currentItem` and `grabInactivePending` may change (e.g. popup switched or another focus event fired). That can cause this handler to close a popup no longer associated with `control`. Inside the deferred function, also check that `popupWindow.currentItem === control` and, if relevant, `control.grabInactivePending` before calling `control.close()`.
Suggested implementation:
```
Qt.callLater(function() {
// Re-validate association to avoid closing a popup that no longer belongs to this control.
if (!popupWindow
|| !readyBinding
|| popupWindow.currentItem !== control
|| !popup.visible
|| control.grabInactivePending) {
return
}
```
If the inner `Qt.callLater` block is not located under `grabInactiveTimer.onTriggered` but instead under `onX11FocusInByUngrab`, apply the same replacement there: re-check `popupWindow.currentItem !== control` and `control.grabInactivePending` in the deferred handler before calling `control.close()`. Also ensure that the identifiers (`popupWindow`, `control`, `readyBinding`, `popup`) match the actual names in your file.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
22a7456 to
9970272
Compare
robertkill
left a comment
There was a problem hiding this comment.
LGTM ✅
这是一个正确的bug修复,解决了PanelPopup在X11 GRAB/UNGRAB期间意外关闭的问题。
核心评价:
- ✅ X11焦点事件处理逻辑正确
- ✅ 延迟关闭机制设计合理(200ms Timer)
- ✅ 避免跨instance干扰(currentItem检查)
- ✅ 所有CI测试通过
建议的小改进(不阻塞合并)(参考sourcery-ai的review):
- 非Linux构建时添加Q_UNUSED宏避免编译警告
- 考虑为200ms添加注释说明或提取为配置项
- 可考虑在Qt.callLater中重新检查currentItem,防止竞态
这些可以在后续PR中优化,当前实现已经足够稳定,建议合并。
9970272 to
73aa543
Compare
|
TAG Bot New tag: 2.0.35 |
|
TAG Bot New tag: 2.0.36 |
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: BLumia, Ivy233, robertkill The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
73aa543 to
5b0bfb3
Compare
… changes 修复:防止PanelPopup在X11键盘抓取期间因瞬时失焦被误关闭 1. Detect X11 FocusOut(GRAB)/FocusIn(UNGRAB) in PopupWindow and expose grab-transition signals. 2. Keep close-on-inactive behavior, but defer close during grab transition and cancel when ungrab focus returns. 3. Handle active-state transitions only for the current popup owner instance to avoid cross-instance interference. 1. 在PopupWindow中识别X11的FocusOut(GRAB)/FocusIn(UNGRAB),并向QML暴露抓取过渡信号。 2. 保留失焦关闭语义,但在抓取过渡期间延迟关闭,并在Ungrab后焦点恢复时取消关闭。 3. 仅由当前popup owner实例处理active状态变化,避免共享popupWindow下多个实例互相干扰。 PMS: BUG-301109
5b0bfb3 to
b5ab25a
Compare
|
/merge |
|
This pr cannot be merged! (status: unstable) |
|
/forcemerge |
|
This pr force merged! (status: unstable) |
… changes
修复:防止PanelPopup在X11键盘抓取期间因瞬时失焦被误关闭
Detect X11 FocusOut(GRAB)/FocusIn(UNGRAB) in PopupWindow and expose grab-transition signals.
Keep close-on-inactive behavior, but defer close during grab transition and cancel when ungrab focus returns.
Handle active-state transitions only for the current popup owner instance to avoid cross-instance interference.
在PopupWindow中识别X11的FocusOut(GRAB)/FocusIn(UNGRAB),并向QML暴露抓取过渡信号。
保留失焦关闭语义,但在抓取过渡期间延迟关闭,并在Ungrab后焦点恢复时取消关闭。
仅由当前popup owner实例处理active状态变化,避免共享popupWindow下多个实例互相干扰。
PMS: BUG-301109
Summary by Sourcery
Guard panel popup dismissal against transient X11 focus changes caused by keyboard/mouse grabs so popups stay open during grab transitions.
Bug Fixes:
Enhancements: