Bug 335187 - Clicking outside the TabBox window doesn't release pointer grab
Summary: Clicking outside the TabBox window doesn't release pointer grab
Status: RESOLVED FIXED
Alias: None
Product: kwin
Classification: Plasma
Component: tabbox (show other bugs)
Version: git master
Platform: unspecified Linux
: NOR major
Target Milestone: 5
Assignee: KWin default assignee
URL:
Keywords: regression
: 334876 (view as bug list)
Depends on:
Blocks:
 
Reported: 2014-05-22 12:11 UTC by Martin Flöser
Modified: 2014-06-02 11:33 UTC (History)
1 user (show)

See Also:
Latest Commit:
Version Fixed In:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Martin Flöser 2014-05-22 12:11:23 UTC
When using a (non-effect) TabBox clicking outside the TabBox closes it (as it should), but doesn't release the pointer grab. Pointer events are thus bound to the new selected window.

Reproducible: Always

Steps to Reproduce:
1. Press Alt+Tab
2. Wait till window is shown
3. Click outside the window
Actual Results:  
Pointer is grabbed

Expected Results:  
Pointer should no longer be grabbed.

This is a regression compared to 4.11. Client::updateMouseGrab() is called and the (un)grabButton calls look fine and do not fail.

Note that the code has been in general broken for quite some time. TabBox::establishTabBoxGrab() sets a m_forcedGlobalMouseGrab to true and in Client::updateMouseGrab() there is a check for this, but it's on Workspace. This must have been broken since TabBox got split out from Workspace. Fixing this does not solve the problem, though.
Comment 1 Martin Flöser 2014-05-22 12:21:02 UTC
porting Client::grabButton, ::ungrabButton and ::updateMouseGrab back to the code from 4.11 does not fix the issue.
Comment 2 Martin Flöser 2014-05-27 08:47:47 UTC
not passing the mouse events to TabBox at all (and thus not closing) doesn't improve the situation.
Comment 3 Thomas Lübking 2014-05-27 11:46:38 UTC
The 4.11 updateMouseGrab() code looks wrong.

"if (isActive() && !workspace()->forcedGlobalMouseGrab())"

for the tabbox calling (on the active windows and inactive have passive grabs anyway) this would by now read "true" since workspace()->forcedGlobalMouseGrab() is never set.
However the logics read twisted to me (this should only process if forcedGlobalMouseGrab is true, otherwise the ungrab process should occur)

One undesired consequence of this would be that the active window is *always* passively grabbed (recall the weird issue about that gtk+ input painting application?)

The ONLY purpose of grabbing the active window (here) is to close the tabbox if you click anywhere (clicking on an inactive window should always work) - if that doesn't happen there can be two reasons:

1. no (unbroken) tabbox closing calls
2. sth. else holds an active grab on the mouse (no input events for any other window, passive grab or not)

-> twist the !workspace()->forcedGlobalMouseGrab() (now Tabbox::self()) check - the branch should be entered when this is true.

-> can you "sleep 5; xprop" with the tabbox on screen?
-> do you enter THE CORRECT  TabBox::handleMouseEvent() function?
-> it's broken somewhere there ;-)
Comment 4 Martin Flöser 2014-05-30 15:38:24 UTC
just did another hour of playing around. twisting the condition did of course not help and after spending some thinking of what it should do I think it's correct: the isActive() branch should not be entered in case of a TabBox grab.

The interesting finding is: I commented out the close() from the mouse click event handler in TabBox and added a debug statement to see that this path is taken. If I click anywhere the mouse gets frozen in this case already, any further clicks have no effect at all. There is at max the debug message once, although the grab doesn't get updated (enough debug statements to be sure it wasn't entered).
Comment 5 Thomas Lübking 2014-05-30 16:26:44 UTC
Checked QWidget::mouseGrabber() value, resp. the ability to cause an active grab w/ eg. xprop?
Comment 6 Martin Flöser 2014-05-30 16:52:19 UTC
after the mouse is frozen, xprop fails with:

"xprop: error: Can't grab the mouse."

Now the big question is: where does the grab happen? It's not in KWin's source (only grab happens during move/resize) and I set break points on xcb_grab_pointer, xcb_grab_pointer_unchecked and XGrabPointer and it never hit.
Comment 7 Martin Flöser 2014-05-30 16:59:57 UTC
Just checked how to get info on who holds the grab:

[513706.466] (II) Printing all currently active device grabs:
[513706.466] Active grab 0x49600054 (core) on device 'Virtual core pointer' (2):
[513706.466]       client pid 18726 kwin --replace
[513706.466]       at 513703453 (from passive grab) (device frozen, state 6)
[513706.466]         core event mask 0x4
[513706.466]       passive grab type 4, detail 0x0, activating key 0
[513706.466]       owner-events false, kb 1 ptr 0, confine 0, cursor 0x0
[513706.466] (II) End list of active device grabs
Comment 8 Martin Flöser 2014-05-30 17:12:09 UTC
While TabBox is active prior to clicking anywhere:

[514374.896] Active grab 0x9800000 (core) on device 'Virtual core keyboard' (3):
[514374.896]       client pid 18776 kwin --replace
[514374.897]       at 514370923 (from active grab) (device thawed, state 1)
[514374.897]         core event mask 0x3
[514374.897]       owner-events false, kb 1 ptr 1, confine 0, cursor 0x0
[514374.897] (II) End list of active device grabs

And while TabBox is active after clicking anywhere:
[514431.004] (II) Printing all currently active device grabs:
[514431.005] Active grab 0x4980016a (core) on device 'Virtual core pointer' (2):
[514431.005]       client pid 18776 kwin --replace
[514431.005]       at 514428449 (from passive grab) (device frozen, state 6)
[514431.005]         core event mask 0x4
[514431.005]       passive grab type 4, detail 0x0, activating key 0
[514431.005]       owner-events false, kb 1 ptr 0, confine 0, cursor 0x0
[514431.005] Active grab 0x9800000 (core) on device 'Virtual core keyboard' (3):
[514431.005]       client pid 18776 kwin --replace
[514431.005]       at 514427482 (from active grab) (device thawed, state 1)
[514431.005]         core event mask 0x3
[514431.005]       owner-events false, kb 1 ptr 1, confine 0, cursor 0x0
[514431.005] (II) End list of active device grabs
Comment 9 Thomas Lübking 2014-05-30 17:32:05 UTC
pointer is passively grabbed, but device is frozen, ie. XAllowEvents doesn't happen.
try reverting the xcb call to XLib variant.
Comment 10 Thomas Lübking 2014-05-30 17:34:25 UTC
while actually the tabbox wouldn't require a sync passive grab at all, does it?
Comment 11 Martin Flöser 2014-05-30 17:47:34 UTC
(In reply to comment #10)
> while actually the tabbox wouldn't require a sync passive grab at all, does
> it?

nope, I just changed the branch to async and that fixes the problem.
Comment 12 Martin Flöser 2014-05-30 17:58:10 UTC
(In reply to comment #11)
> (In reply to comment #10)
> > while actually the tabbox wouldn't require a sync passive grab at all, does
> > it?
> 
> nope, I just changed the branch to async and that fixes the problem.

nope again, I can still reproduce it (but sometimes it works).
Comment 13 Martin Flöser 2014-05-30 18:00:53 UTC
also with XAllowEvents I'm still able to reproduce. The pattern is that I'm allowed to click once outside, afterwards it freezes (I had the close() call removed).
Comment 14 Martin Flöser 2014-05-30 18:10:51 UTC
And here we go (working solution):
diff --git a/tabbox/tabbox.cpp b/tabbox/tabbox.cpp
index 7b659ff..688ebd9 100644
--- a/tabbox/tabbox.cpp
+++ b/tabbox/tabbox.cpp
@@ -851,7 +851,7 @@ void TabBox::delayedShow()
 
 bool TabBox::handleMouseEvent(xcb_button_press_event_t *e)
 {
-    xcb_allow_events(connection(), XCB_ALLOW_ASYNC_POINTER, xTime());
+    xcb_allow_events(connection(), XCB_ALLOW_ASYNC_POINTER, XCB_CURRENT_TIME);
     if (!m_isShown && isDisplayed()) {
         // tabbox has been replaced, check effects
         if (effects && static_cast<EffectsHandlerImpl*>(effects)->checkInputWindowEvent(e))
@@ -881,7 +881,7 @@ bool TabBox::handleMouseEvent(xcb_button_press_event_t *e)
 
 bool TabBox::handleMouseEvent(xcb_motion_notify_event_t *e)
 {
-    xcb_allow_events(connection(), XCB_ALLOW_ASYNC_POINTER, xTime());
+    xcb_allow_events(connection(), XCB_ALLOW_ASYNC_POINTER, XCB_CURRENT_TIME);
     if (!m_isShown && isDisplayed()) {
         // tabbox has been replaced, check effects
         if (effects && static_cast<EffectsHandlerImpl*>(effects)->checkInputWindowEvent(e))
Comment 15 Thomas Lübking 2014-05-30 18:16:04 UTC
one more for the lazy timeupdate... ;-)
"ShipIt!"

we should maybe seek to reduce xTime() invocation as much as possible and sync on call?
Comment 16 Martin Flöser 2014-05-30 19:03:06 UTC
> we should maybe seek to reduce xTime() invocation as much as possible and
> sync on call?

agree
Comment 17 Martin Flöser 2014-06-01 15:25:17 UTC
Git commit b83f63edf211343e93175aa8ba0607c04a2b35e3 by Martin Gräßlin.
Committed on 01/06/2014 at 15:20.
Pushed by graesslin into branch 'master'.

[tabbox] Use XCB_CURRENT_TIME for xcb_allow_event

M  +2    -2    tabbox/tabbox.cpp

http://commits.kde.org/kwin/b83f63edf211343e93175aa8ba0607c04a2b35e3
Comment 18 Martin Flöser 2014-06-02 11:33:58 UTC
*** Bug 334876 has been marked as a duplicate of this bug. ***