kwin/plugins/platforms/x11/standalone/xinputintegration.cpp
Martin Gräßlin 9c5f972c6a Forward x11 raw pointer buttons to PointerInputRedirection
Summary:
The raw pointer button events intercepted in the XInput2 input filter
get sent through the Platform to the PointerInputRedirection. This
makes the PointerInputRedirection track the pointer button state and
emit the signals for button changed and axis changed.

These signals are used by the modifier-only shortcut detection to
determine whether the shortcut should trigger.

On X11 the "normal" input handling doesn't use the InputRedirection
and the emitted signals are not consumed by anything else. As
PointerInputRedirection is not inited the events are not forwarded
to the input filter, thus won't be processed by other parts and
won't interfere with the normal event processing on X11.

Given that it also doesn't matter that the input filter does not
apply the left-handed setting. The internal tracking will have a
wrong mouse button, but nothing is going to do decisions based on
the value of the pressed mouse button. For the moment all we are
interested in is that a button is pressed.

Test Plan:
Pressed meta, clicked, scrolled, released meta: launcher
did not open. Pressed meta, released meta: launcher opened

Reviewers: #kwin, #plasma

Subscribers: plasma-devel, kwin

Tags: #kwin

Differential Revision: https://phabricator.kde.org/D2506

BUG: 367730
2016-08-25 11:47:47 +02:00

212 lines
7.2 KiB
C++

/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2016 Martin Gräßlin <mgraesslin@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "xinputintegration.h"
#include "main.h"
#include "logging.h"
#include "platform.h"
#include "x11cursor.h"
#include "keyboard_input.h"
#include "x11eventfilter.h"
#include <kwinglobals.h>
#include <X11/extensions/XInput2.h>
#include <X11/extensions/XI2proto.h>
#include <linux/input.h>
namespace KWin
{
class XInputEventFilter : public X11EventFilter
{
public:
XInputEventFilter(int xi_opcode)
: X11EventFilter(XCB_GE_GENERIC, xi_opcode, QVector<int>{XI_RawMotion, XI_RawButtonPress, XI_RawButtonRelease, XI_RawKeyPress, XI_RawKeyRelease})
{}
virtual ~XInputEventFilter() = default;
bool event(xcb_generic_event_t *event) override {
xcb_ge_generic_event_t *ge = reinterpret_cast<xcb_ge_generic_event_t *>(event);
switch (ge->event_type) {
case XI_RawKeyPress:
if (m_xkb) {
m_xkb->updateKey(reinterpret_cast<xXIRawEvent*>(event)->detail - 8, InputRedirection::KeyboardKeyPressed);
}
break;
case XI_RawKeyRelease:
if (m_xkb) {
m_xkb->updateKey(reinterpret_cast<xXIRawEvent*>(event)->detail - 8, InputRedirection::KeyboardKeyReleased);
}
break;
case XI_RawButtonPress:
if (m_xkb) {
auto e = reinterpret_cast<xXIRawEvent*>(event);
switch (e->detail) {
// TODO: this currently ignores left handed settings, for current usage not needed
// if we want to use also for global mouse shortcuts, this needs to reflect state correctly
case XCB_BUTTON_INDEX_1:
kwinApp()->platform()->pointerButtonPressed(BTN_LEFT, e->time);
break;
case XCB_BUTTON_INDEX_2:
kwinApp()->platform()->pointerButtonPressed(BTN_MIDDLE, e->time);
break;
case XCB_BUTTON_INDEX_3:
kwinApp()->platform()->pointerButtonPressed(BTN_RIGHT, e->time);
break;
case XCB_BUTTON_INDEX_4:
case XCB_BUTTON_INDEX_5:
// vertical axis, ignore on press
break;
// TODO: further buttons, horizontal scrolling?
}
}
if (m_x11Cursor) {
m_x11Cursor->schedulePoll();
}
break;
case XI_RawButtonRelease:
if (m_xkb) {
auto e = reinterpret_cast<xXIRawEvent*>(event);
switch (e->detail) {
// TODO: this currently ignores left handed settings, for current usage not needed
// if we want to use also for global mouse shortcuts, this needs to reflect state correctly
case XCB_BUTTON_INDEX_1:
kwinApp()->platform()->pointerButtonReleased(BTN_LEFT, e->time);
break;
case XCB_BUTTON_INDEX_2:
kwinApp()->platform()->pointerButtonReleased(BTN_MIDDLE, e->time);
break;
case XCB_BUTTON_INDEX_3:
kwinApp()->platform()->pointerButtonReleased(BTN_RIGHT, e->time);
break;
case XCB_BUTTON_INDEX_4:
kwinApp()->platform()->pointerAxisVertical(120, e->time);
break;
case XCB_BUTTON_INDEX_5:
kwinApp()->platform()->pointerAxisVertical(-120, e->time);
break;
// TODO: further buttons, horizontal scrolling?
}
}
if (m_x11Cursor) {
m_x11Cursor->schedulePoll();
}
break;
default:
if (m_x11Cursor) {
m_x11Cursor->schedulePoll();
}
break;
}
return false;
}
void setCursor(const QPointer<X11Cursor> &cursor) {
m_x11Cursor = cursor;
}
void setXkb(Xkb *xkb) {
m_xkb = xkb;
}
private:
QPointer<X11Cursor> m_x11Cursor;
// TODO: QPointer
Xkb *m_xkb = nullptr;
};
XInputIntegration::XInputIntegration(QObject *parent)
: QObject(parent)
{
}
XInputIntegration::~XInputIntegration() = default;
void XInputIntegration::init()
{
Display *dpy = display();
int xi_opcode, event, error;
// init XInput extension
if (!XQueryExtension(dpy, "XInputExtension", &xi_opcode, &event, &error)) {
qCDebug(KWIN_X11STANDALONE) << "XInputExtension not present";
return;
}
// verify that the XInput extension is at at least version 2.0
int major = 2, minor = 0;
int result = XIQueryVersion(dpy, &major, &minor);
if (result == BadImplementation) {
// Xinput 2.2 returns BadImplementation if checked against 2.0
major = 2;
minor = 2;
if (XIQueryVersion(dpy, &major, &minor) != Success) {
qCDebug(KWIN_X11STANDALONE) << "Failed to init XInput";
return;
}
} else if (result != Success) {
qCDebug(KWIN_X11STANDALONE) << "Failed to init XInput";
return;
}
m_hasXInput = true;
m_xiOpcode = xi_opcode;
m_majorVersion = major;
m_minorVersion = minor;
qCDebug(KWIN_X11STANDALONE) << "Has XInput support" << m_majorVersion << "." << m_minorVersion;
}
void XInputIntegration::setCursor(X11Cursor *cursor)
{
m_x11Cursor = QPointer<X11Cursor>(cursor);
}
void XInputIntegration::setXkb(Xkb *xkb)
{
m_xkb = xkb;
}
void XInputIntegration::startListening()
{
// this assumes KWin is the only one setting events on the root window
// given Qt's source code this seems to be true. If it breaks, we need to change
XIEventMask evmasks[1];
unsigned char mask1[XIMaskLen(XI_LASTEVENT)];
memset(mask1, 0, sizeof(mask1));
XISetMask(mask1, XI_RawMotion);
XISetMask(mask1, XI_RawButtonPress);
XISetMask(mask1, XI_RawButtonRelease);
if (m_majorVersion >= 2 && m_minorVersion >= 1) {
// we need to listen to all events, which is only available with XInput 2.1
XISetMask(mask1, XI_RawKeyPress);
XISetMask(mask1, XI_RawKeyRelease);
}
evmasks[0].deviceid = XIAllMasterDevices;
evmasks[0].mask_len = sizeof(mask1);
evmasks[0].mask = mask1;
XISelectEvents(display(), rootWindow(), evmasks, 1);
m_xiEventFilter.reset(new XInputEventFilter(m_xiOpcode));
m_xiEventFilter->setCursor(m_x11Cursor);
m_xiEventFilter->setXkb(m_xkb);
}
}