[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 XCapL KKeyServer::modXLock()
|
||||||
#define XNumL KKeyServer::modXNumLock()
|
#define XNumL KKeyServer::modXNumLock()
|
||||||
#define XScrL KKeyServer::modXScrollLock()
|
#define XScrL KKeyServer::modXScrollLock()
|
||||||
void X11Client::grabButton(int modifier)
|
void X11Client::grabButton(Qt::KeyboardModifier modifier, uint8_t button)
|
||||||
{
|
{
|
||||||
unsigned int mods[ 8 ] = {
|
uint16_t x11Modifier;
|
||||||
0, XCapL, XNumL, XNumL | XCapL,
|
|
||||||
XScrL, XScrL | XCapL,
|
switch (modifier) {
|
||||||
XScrL | XNumL, XScrL | XNumL | XCapL
|
case Qt::MetaModifier:
|
||||||
};
|
x11Modifier = KKeyServer::modXMeta();
|
||||||
for (int i = 0;
|
break;
|
||||||
i < 8;
|
case Qt::AltModifier:
|
||||||
++i)
|
x11Modifier = KKeyServer::modXAlt();
|
||||||
m_wrapper.grabButton(XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, modifier | mods[ i ]);
|
break;
|
||||||
}
|
default:
|
||||||
|
x11Modifier = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
void X11Client::ungrabButton(int modifier)
|
|
||||||
{
|
|
||||||
unsigned int mods[ 8 ] = {
|
unsigned int mods[ 8 ] = {
|
||||||
0, XCapL, XNumL, XNumL | XCapL,
|
0, XCapL, XNumL, XNumL | XCapL,
|
||||||
XScrL, XScrL | XCapL,
|
XScrL, XScrL | XCapL,
|
||||||
|
@ -846,47 +847,69 @@ void X11Client::ungrabButton(int modifier)
|
||||||
for (int i = 0;
|
for (int i = 0;
|
||||||
i < 8;
|
i < 8;
|
||||||
++i)
|
++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 XCapL
|
||||||
#undef XNumL
|
#undef XNumL
|
||||||
#undef XScrL
|
#undef XScrL
|
||||||
|
|
||||||
/**
|
bool X11Client::isMostRecentlyRaised() const
|
||||||
* Releases the passive grab for some modifier combinations when a
|
{
|
||||||
* window becomes active. This helps broken X programs that
|
// The last toplevel in the unconstrained stacking order is the most recently raised one.
|
||||||
* missinterpret LeaveNotify events in grab mode to work properly
|
return workspace()->topClientOnDesktop(VirtualDesktopManager::self()->current(), -1, true, false) == this;
|
||||||
* (Motif, AWT, Tk, ...)
|
}
|
||||||
*/
|
|
||||||
void X11Client::updateMouseGrab()
|
void X11Client::updateMouseGrab()
|
||||||
{
|
{
|
||||||
if (workspace()->globalShortcutsDisabled()) {
|
xcb_ungrab_button(connection(), XCB_BUTTON_INDEX_ANY, m_wrapper, XCB_MOD_MASK_ANY);
|
||||||
m_wrapper.ungrabButton();
|
|
||||||
// keep grab for the simple click without modifiers if needed (see below)
|
if (TabBox::TabBox::self()->forcedGlobalMouseGrab()) { // see TabBox::establishTabBoxGrab()
|
||||||
bool not_obscured = workspace()->topClientOnDesktop(VirtualDesktopManager::self()->current(), -1, true, false) == this;
|
m_wrapper.grabButton(XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
|
||||||
if (!(!options->isClickRaise() || not_obscured))
|
|
||||||
grabButton(XCB_NONE);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isActive() && !TabBox::TabBox::self()->forcedGlobalMouseGrab()) { // see TabBox::establishTabBoxGrab()
|
|
||||||
// first grab all modifier combinations
|
// When a passive grab is activated or deactivated, the X server will generate crossing
|
||||||
m_wrapper.grabButton(XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
|
// events as if the pointer were suddenly to warp from its current position to some position
|
||||||
// remove the grab for no modifiers only if the window
|
// in the grab window. Some /broken/ X11 clients do get confused by such EnterNotify and
|
||||||
// is unobscured or if the user doesn't want click raise
|
// LeaveNotify events so we release the passive grab for the active window.
|
||||||
// (it is unobscured if it the topmost in the unconstrained stacking order, i.e. it is
|
//
|
||||||
// the most recently raised window)
|
// The passive grab below is established so the window can be raised or activated when it
|
||||||
bool not_obscured = workspace()->topClientOnDesktop(VirtualDesktopManager::self()->current(), -1, true, false) == this;
|
// is clicked.
|
||||||
if (!options->isClickRaise() || not_obscured)
|
if ((options->focusPolicyIsReasonable() && !isActive()) ||
|
||||||
ungrabButton(XCB_NONE);
|
(options->isClickRaise() && !isMostRecentlyRaised())) {
|
||||||
else
|
if (options->commandWindow1() != Options::MouseNothing) {
|
||||||
grabButton(XCB_NONE);
|
grabButton(Qt::NoModifier, XCB_BUTTON_INDEX_1);
|
||||||
ungrabButton(XCB_MOD_MASK_SHIFT);
|
}
|
||||||
ungrabButton(XCB_MOD_MASK_CONTROL);
|
if (options->commandWindow2() != Options::MouseNothing) {
|
||||||
ungrabButton(XCB_MOD_MASK_CONTROL | XCB_MOD_MASK_SHIFT);
|
grabButton(Qt::NoModifier, XCB_BUTTON_INDEX_2);
|
||||||
} else {
|
}
|
||||||
m_wrapper.ungrabButton();
|
if (options->commandWindow3() != Options::MouseNothing) {
|
||||||
// simply grab all modifier combinations
|
grabButton(Qt::NoModifier, XCB_BUTTON_INDEX_3);
|
||||||
m_wrapper.grabButton(XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
|
}
|
||||||
|
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 {
|
MouseCommand commandAll3() const {
|
||||||
return CmdAll3;
|
return CmdAll3;
|
||||||
}
|
}
|
||||||
|
MouseWheelCommand commandAllWheel() const {
|
||||||
|
return CmdAllWheel;
|
||||||
|
}
|
||||||
uint keyCmdAllModKey() const {
|
uint keyCmdAllModKey() const {
|
||||||
return CmdAllModKey;
|
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)
|
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(clientMachine(), &ClientMachine::localhostChanged, this, &X11Client::updateCaption);
|
||||||
|
connect(options, &Options::configChanged, this, &X11Client::updateMouseGrab);
|
||||||
connect(options, &Options::condensedTitleChanged, this, &X11Client::updateCaption);
|
connect(options, &Options::condensedTitleChanged, this, &X11Client::updateCaption);
|
||||||
|
|
||||||
connect(this, &X11Client::moveResizeCursorChanged, this, [this] (CursorShape cursor) {
|
connect(this, &X11Client::moveResizeCursorChanged, this, [this] (CursorShape cursor) {
|
||||||
|
|
|
@ -397,6 +397,7 @@ Q_SIGNALS:
|
||||||
private:
|
private:
|
||||||
void exportMappingState(int s); // ICCCM 4.1.3.1, 4.1.4, NETWM 2.5.1
|
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 isManaged() const; ///< Returns false if this client is not yet managed
|
||||||
|
bool isMostRecentlyRaised() const;
|
||||||
void updateAllowedActions(bool force = false);
|
void updateAllowedActions(bool force = false);
|
||||||
QRect fullscreenMonitorsArea(NETFullscreenMonitors topology) const;
|
QRect fullscreenMonitorsArea(NETFullscreenMonitors topology) const;
|
||||||
void changeMaximize(bool horizontal, bool vertical, bool adjust) override;
|
void changeMaximize(bool horizontal, bool vertical, bool adjust) override;
|
||||||
|
@ -417,8 +418,7 @@ private:
|
||||||
void sendSyncRequest();
|
void sendSyncRequest();
|
||||||
void leaveMoveResize() override;
|
void leaveMoveResize() override;
|
||||||
void positionGeometryTip() override;
|
void positionGeometryTip() override;
|
||||||
void grabButton(int mod);
|
void grabButton(Qt::KeyboardModifier modifier, uint8_t button);
|
||||||
void ungrabButton(int mod);
|
|
||||||
void resizeDecoration();
|
void resizeDecoration();
|
||||||
void createDecoration(const QRect &oldgeom) override;
|
void createDecoration(const QRect &oldgeom) override;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue