[x11] Hold a passive grab on buttons only when needed
Due to a bug in the XI2 protocol, clients have to reset scroll valuators on XI_Enter because the scroll valuators might have changed while the pointer was elsewhere. The XI_Enter event is usually sent when an input device enters the window, but it can also be generated by a passive grab. If an XI_Enter event has been generated by a passive grab, the client should not reset scroll valuators. Unfortunately, there is no any reliable way for the client to determine if an XI_Enter event has been sent in response to a deactivated passive grab. A correct fix for the scroll issues in GTK apps would involve changes in the XI2 protocol. As a work around, we can hold a passive grab only if the current mouse wheel action is either "Activate and scroll" or "Activate, raise, and scroll." BUG: 394772 FIXED-IN: 5.19.3
This commit is contained in:
parent
170741c499
commit
ec5a0249e2
4 changed files with 74 additions and 47 deletions
113
events.cpp
113
events.cpp
|
@ -823,21 +823,22 @@ void X11Client::leaveNotifyEvent(xcb_leave_notify_event_t *e)
|
|||
#define XCapL KKeyServer::modXLock()
|
||||
#define XNumL KKeyServer::modXNumLock()
|
||||
#define XScrL KKeyServer::modXScrollLock()
|
||||
void X11Client::grabButton(int modifier)
|
||||
void X11Client::grabButton(Qt::KeyboardModifier modifier, uint8_t button)
|
||||
{
|
||||
unsigned int mods[ 8 ] = {
|
||||
0, XCapL, XNumL, XNumL | XCapL,
|
||||
XScrL, XScrL | XCapL,
|
||||
XScrL | XNumL, XScrL | XNumL | XCapL
|
||||
};
|
||||
for (int i = 0;
|
||||
i < 8;
|
||||
++i)
|
||||
m_wrapper.grabButton(XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, modifier | mods[ i ]);
|
||||
}
|
||||
uint16_t x11Modifier;
|
||||
|
||||
switch (modifier) {
|
||||
case Qt::MetaModifier:
|
||||
x11Modifier = KKeyServer::modXMeta();
|
||||
break;
|
||||
case Qt::AltModifier:
|
||||
x11Modifier = KKeyServer::modXAlt();
|
||||
break;
|
||||
default:
|
||||
x11Modifier = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
void X11Client::ungrabButton(int modifier)
|
||||
{
|
||||
unsigned int mods[ 8 ] = {
|
||||
0, XCapL, XNumL, XNumL | XCapL,
|
||||
XScrL, XScrL | XCapL,
|
||||
|
@ -846,47 +847,69 @@ void X11Client::ungrabButton(int modifier)
|
|||
for (int i = 0;
|
||||
i < 8;
|
||||
++i)
|
||||
m_wrapper.ungrabButton(modifier | mods[ i ]);
|
||||
m_wrapper.grabButton(XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, x11Modifier | mods[ i ], button);
|
||||
}
|
||||
#undef XCapL
|
||||
#undef XNumL
|
||||
#undef XScrL
|
||||
|
||||
/**
|
||||
* Releases the passive grab for some modifier combinations when a
|
||||
* window becomes active. This helps broken X programs that
|
||||
* missinterpret LeaveNotify events in grab mode to work properly
|
||||
* (Motif, AWT, Tk, ...)
|
||||
*/
|
||||
bool X11Client::isMostRecentlyRaised() const
|
||||
{
|
||||
// The last toplevel in the unconstrained stacking order is the most recently raised one.
|
||||
return workspace()->topClientOnDesktop(VirtualDesktopManager::self()->current(), -1, true, false) == this;
|
||||
}
|
||||
|
||||
void X11Client::updateMouseGrab()
|
||||
{
|
||||
if (workspace()->globalShortcutsDisabled()) {
|
||||
m_wrapper.ungrabButton();
|
||||
// keep grab for the simple click without modifiers if needed (see below)
|
||||
bool not_obscured = workspace()->topClientOnDesktop(VirtualDesktopManager::self()->current(), -1, true, false) == this;
|
||||
if (!(!options->isClickRaise() || not_obscured))
|
||||
grabButton(XCB_NONE);
|
||||
xcb_ungrab_button(connection(), XCB_BUTTON_INDEX_ANY, m_wrapper, XCB_MOD_MASK_ANY);
|
||||
|
||||
if (TabBox::TabBox::self()->forcedGlobalMouseGrab()) { // see TabBox::establishTabBoxGrab()
|
||||
m_wrapper.grabButton(XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
|
||||
return;
|
||||
}
|
||||
if (isActive() && !TabBox::TabBox::self()->forcedGlobalMouseGrab()) { // see TabBox::establishTabBoxGrab()
|
||||
// first grab all modifier combinations
|
||||
m_wrapper.grabButton(XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
|
||||
// remove the grab for no modifiers only if the window
|
||||
// is unobscured or if the user doesn't want click raise
|
||||
// (it is unobscured if it the topmost in the unconstrained stacking order, i.e. it is
|
||||
// the most recently raised window)
|
||||
bool not_obscured = workspace()->topClientOnDesktop(VirtualDesktopManager::self()->current(), -1, true, false) == this;
|
||||
if (!options->isClickRaise() || not_obscured)
|
||||
ungrabButton(XCB_NONE);
|
||||
else
|
||||
grabButton(XCB_NONE);
|
||||
ungrabButton(XCB_MOD_MASK_SHIFT);
|
||||
ungrabButton(XCB_MOD_MASK_CONTROL);
|
||||
ungrabButton(XCB_MOD_MASK_CONTROL | XCB_MOD_MASK_SHIFT);
|
||||
} else {
|
||||
m_wrapper.ungrabButton();
|
||||
// simply grab all modifier combinations
|
||||
m_wrapper.grabButton(XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
|
||||
|
||||
// When a passive grab is activated or deactivated, the X server will generate crossing
|
||||
// events as if the pointer were suddenly to warp from its current position to some position
|
||||
// in the grab window. Some /broken/ X11 clients do get confused by such EnterNotify and
|
||||
// LeaveNotify events so we release the passive grab for the active window.
|
||||
//
|
||||
// The passive grab below is established so the window can be raised or activated when it
|
||||
// is clicked.
|
||||
if ((options->focusPolicyIsReasonable() && !isActive()) ||
|
||||
(options->isClickRaise() && !isMostRecentlyRaised())) {
|
||||
if (options->commandWindow1() != Options::MouseNothing) {
|
||||
grabButton(Qt::NoModifier, XCB_BUTTON_INDEX_1);
|
||||
}
|
||||
if (options->commandWindow2() != Options::MouseNothing) {
|
||||
grabButton(Qt::NoModifier, XCB_BUTTON_INDEX_2);
|
||||
}
|
||||
if (options->commandWindow3() != Options::MouseNothing) {
|
||||
grabButton(Qt::NoModifier, XCB_BUTTON_INDEX_3);
|
||||
}
|
||||
if (options->commandWindowWheel() != Options::MouseNothing) {
|
||||
grabButton(Qt::NoModifier, XCB_BUTTON_INDEX_4);
|
||||
grabButton(Qt::NoModifier, XCB_BUTTON_INDEX_5);
|
||||
}
|
||||
}
|
||||
|
||||
// We want to grab <command modifier> + buttons no matter what state the window is in. The
|
||||
// client will receive funky EnterNotify and LeaveNotify events, but there is nothing that
|
||||
// we can do about it, unfortunately.
|
||||
|
||||
if (!workspace()->globalShortcutsDisabled()) {
|
||||
if (options->commandAll1() != Options::MouseNothing) {
|
||||
grabButton(options->commandAllModifier(), XCB_BUTTON_INDEX_1);
|
||||
}
|
||||
if (options->commandAll2() != Options::MouseNothing) {
|
||||
grabButton(options->commandAllModifier(), XCB_BUTTON_INDEX_2);
|
||||
}
|
||||
if (options->commandAll3() != Options::MouseNothing) {
|
||||
grabButton(options->commandAllModifier(), XCB_BUTTON_INDEX_3);
|
||||
}
|
||||
if (options->commandAllWheel() != Options::MouseWheelNothing) {
|
||||
grabButton(options->commandAllModifier(), XCB_BUTTON_INDEX_4);
|
||||
grabButton(options->commandAllModifier(), XCB_BUTTON_INDEX_5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -447,6 +447,9 @@ public:
|
|||
MouseCommand commandAll3() const {
|
||||
return CmdAll3;
|
||||
}
|
||||
MouseWheelCommand commandAllWheel() const {
|
||||
return CmdAllWheel;
|
||||
}
|
||||
uint keyCmdAllModKey() const {
|
||||
return CmdAllModKey;
|
||||
}
|
||||
|
|
|
@ -162,6 +162,7 @@ X11Client::X11Client()
|
|||
m_frameGeometry = QRect(0, 0, 100, 100); // So that decorations don't start with size being (0,0)
|
||||
|
||||
connect(clientMachine(), &ClientMachine::localhostChanged, this, &X11Client::updateCaption);
|
||||
connect(options, &Options::configChanged, this, &X11Client::updateMouseGrab);
|
||||
connect(options, &Options::condensedTitleChanged, this, &X11Client::updateCaption);
|
||||
|
||||
connect(this, &X11Client::moveResizeCursorChanged, this, [this] (CursorShape cursor) {
|
||||
|
|
|
@ -397,6 +397,7 @@ Q_SIGNALS:
|
|||
private:
|
||||
void exportMappingState(int s); // ICCCM 4.1.3.1, 4.1.4, NETWM 2.5.1
|
||||
bool isManaged() const; ///< Returns false if this client is not yet managed
|
||||
bool isMostRecentlyRaised() const;
|
||||
void updateAllowedActions(bool force = false);
|
||||
QRect fullscreenMonitorsArea(NETFullscreenMonitors topology) const;
|
||||
void changeMaximize(bool horizontal, bool vertical, bool adjust) override;
|
||||
|
@ -417,8 +418,7 @@ private:
|
|||
void sendSyncRequest();
|
||||
void leaveMoveResize() override;
|
||||
void positionGeometryTip() override;
|
||||
void grabButton(int mod);
|
||||
void ungrabButton(int mod);
|
||||
void grabButton(Qt::KeyboardModifier modifier, uint8_t button);
|
||||
void resizeDecoration();
|
||||
void createDecoration(const QRect &oldgeom) override;
|
||||
|
||||
|
|
Loading…
Reference in a new issue