Add touch support to x11 windowed platform

Summary:
This change inits XInput extension, listens for touch events and
forwards them to our platform API. Thus touch events are forwarded on a
nested wayland session on X11.

Please note that I only tested this change on Xwayland.

Test Plan: Run nested kwin_wayland with two outputs and looked into debug console

Reviewers: #kwin

Subscribers: kwin

Tags: #kwin

Differential Revision: https://phabricator.kde.org/D17369
This commit is contained in:
Martin Flöser 2018-12-05 18:46:35 +01:00
parent f76280231f
commit d49642ca15
5 changed files with 178 additions and 26 deletions

View file

@ -0,0 +1,55 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2018 Martin Flöser <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/>.
*********************************************************************/
#pragma once
#include <xcb/xcb.h>
#include <string.h>
namespace KWin
{
class GeEventMemMover
{
public:
GeEventMemMover(xcb_generic_event_t *event)
: m_event(reinterpret_cast<xcb_ge_generic_event_t *>(event))
{
// xcb event structs contain stuff that wasn't on the wire, the full_sequence field
// adds an extra 4 bytes and generic events cookie data is on the wire right after the standard 32 bytes.
// Move this data back to have the same layout in memory as it was on the wire
// and allow casting, overwriting the full_sequence field.
memmove((char*) m_event + 32, (char*) m_event + 36, m_event->length * 4);
}
~GeEventMemMover()
{
// move memory layout back, so that Qt can do the same without breaking
memmove((char*) m_event + 36, (char *) m_event + 32, m_event->length * 4);
}
xcb_ge_generic_event_t *operator->() const {
return m_event;
}
private:
xcb_ge_generic_event_t *m_event;
};
}

View file

@ -24,6 +24,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "platform.h"
#include "screenedge.h"
#include "x11cursor.h"
#include "ge_event_mem_mover.h"
#include "input.h"
#include "x11eventfilter.h"
@ -43,32 +44,6 @@ static inline qreal fixed1616ToReal(FP1616 val)
return (val) * 1.0 / (1 << 16);
}
class GeEventMemMover
{
public:
GeEventMemMover(xcb_generic_event_t *event)
: m_event(reinterpret_cast<xcb_ge_generic_event_t *>(event))
{
// xcb event structs contain stuff that wasn't on the wire, the full_sequence field
// adds an extra 4 bytes and generic events cookie data is on the wire right after the standard 32 bytes.
// Move this data back to have the same layout in memory as it was on the wire
// and allow casting, overwriting the full_sequence field.
memmove((char*) m_event + 32, (char*) m_event + 36, m_event->length * 4);
}
~GeEventMemMover()
{
// move memory layout back, so that Qt can do the same without breaking
memmove((char*) m_event + 36, (char *) m_event + 32, m_event->length * 4);
}
xcb_ge_generic_event_t *operator->() const {
return m_event;
}
private:
xcb_ge_generic_event_t *m_event;
};
class XInputEventFilter : public X11EventFilter
{
public:

View file

@ -9,6 +9,9 @@ include_directories(${CMAKE_SOURCE_DIR}/platformsupport/scenes/opengl)
add_library(KWinWaylandX11Backend MODULE ${X11BACKEND_SOURCES})
set_target_properties(KWinWaylandX11Backend PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/org.kde.kwin.waylandbackends/")
target_link_libraries(KWinWaylandX11Backend eglx11common kwin kwinxrenderutils X11::XCB SceneQPainterBackend SceneOpenGLBackend)
if(X11_Xinput_FOUND)
target_link_libraries(KWinWaylandX11Backend ${X11_Xinput_LIB})
endif()
install(
TARGETS

View file

@ -39,6 +39,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <KWayland/Server/surface_interface.h>
// xcb
#include <xcb/xcb_keysyms.h>
// X11
#if HAVE_X11_XINPUT
#include "ge_event_mem_mover.h"
#include <X11/extensions/XInput2.h>
#include <X11/extensions/XI2proto.h>
#endif
// system
#include <linux/input.h>
#include <X11/Xlib-xcb.h>
@ -92,6 +98,7 @@ void X11WindowedBackend::init()
m_screen = it.data;
}
}
initXInput();
XRenderUtils::init(m_connection, m_screen->root);
createWindow();
connect(kwinApp(), &Application::workspaceCreated, this, &X11WindowedBackend::startEventReading);
@ -103,12 +110,43 @@ void X11WindowedBackend::init()
setReady(true);
waylandServer()->seat()->setHasPointer(true);
waylandServer()->seat()->setHasKeyboard(true);
if (m_hasXInput) {
waylandServer()->seat()->setHasTouch(true);
}
emit screensQueried();
} else {
emit initFailed();
}
}
void X11WindowedBackend::initXInput()
{
#if HAVE_X11_XINPUT
int xi_opcode, event, error;
// init XInput extension
if (!XQueryExtension(m_display, "XInputExtension", &xi_opcode, &event, &error)) {
qCDebug(KWIN_X11WINDOWED) << "XInputExtension not present";
return;
}
// verify that the XInput extension is at at least version 2.0
int major = 2, minor = 2;
int result = XIQueryVersion(m_display, &major, &minor);
if (result != Success) {
qCDebug(KWIN_X11WINDOWED) << "Failed to init XInput 2.2, trying 2.0";
minor = 0;
if (XIQueryVersion(m_display, &major, &minor) != Success) {
qCDebug(KWIN_X11WINDOWED) << "Failed to init XInput";
return;
}
}
m_xiOpcode = xi_opcode;
m_majorVersion = major;
m_minorVersion = minor;
m_hasXInput = m_majorVersion >=2 && m_minorVersion >= 2;
#endif
}
void X11WindowedBackend::createWindow()
{
Xcb::Atom protocolsAtom(QByteArrayLiteral("WM_PROTOCOLS"), false, m_connection);
@ -139,6 +177,9 @@ void X11WindowedBackend::createWindow()
0, 0, o.size.width(), o.size.height(),
0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, mask, values);
// select xinput 2 events
initXInputForWindow(o.window);
o.winInfo = new NETWinInfo(m_connection, o.window, m_screen->root, NET::WMWindowType, NET::Properties2());
o.winInfo->setWindowType(NET::Normal);
o.winInfo->setPid(QCoreApplication::applicationPid());
@ -171,6 +212,29 @@ void X11WindowedBackend::createWindow()
xcb_flush(m_connection);
}
void X11WindowedBackend::initXInputForWindow(xcb_window_t window)
{
if (!m_hasXInput) {
return;
}
#if HAVE_X11_XINPUT
XIEventMask evmasks[1];
unsigned char mask1[XIMaskLen(XI_LASTEVENT)];
memset(mask1, 0, sizeof(mask1));
XISetMask(mask1, XI_TouchBegin);
XISetMask(mask1, XI_TouchUpdate);
XISetMask(mask1, XI_TouchOwnership);
XISetMask(mask1, XI_TouchEnd);
evmasks[0].deviceid = XIAllMasterDevices;
evmasks[0].mask_len = sizeof(mask1);
evmasks[0].mask = mask1;
XISelectEvents(m_display, window, evmasks, 1);
#else
Q_UNUSED(window)
#endif
}
void X11WindowedBackend::startEventReading()
{
QSocketNotifier *notifier = new QSocketNotifier(xcb_get_file_descriptor(m_connection), QSocketNotifier::Read, this);
@ -186,6 +250,14 @@ void X11WindowedBackend::startEventReading()
connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::awake, this, processXcbEvents);
}
#if HAVE_X11_XINPUT
static inline qreal fixed1616ToReal(FP1616 val)
{
return (val) * 1.0 / (1 << 16);
}
#endif
void X11WindowedBackend::handleEvent(xcb_generic_event_t *e)
{
const uint8_t eventType = e->response_type & ~0x80;
@ -249,6 +321,46 @@ void X11WindowedBackend::handleEvent(xcb_generic_event_t *e)
xcb_refresh_keyboard_mapping(m_keySymbols, reinterpret_cast<xcb_mapping_notify_event_t*>(e));
}
break;
#if HAVE_X11_XINPUT
case XCB_GE_GENERIC: {
GeEventMemMover ge(e);
auto te = reinterpret_cast<xXIDeviceEvent*>(e);
auto it = std::find_if(m_windows.constBegin(), m_windows.constEnd(), [te] (const Output &o) { return o.window == te->event; });
if (it == m_windows.constEnd()) {
break;
}
QPointF position{
fixed1616ToReal(te->root_x) - (*it).xPosition.x() + (*it).internalPosition.x(),
fixed1616ToReal(te->root_y) - (*it).xPosition.y() + (*it).internalPosition.y()
};
position /= it->scale;
switch (ge->event_type) {
case XI_TouchBegin: {
touchDown(te->detail, position, te->time);
touchFrame();
break;
}
case XI_TouchUpdate: {
touchMotion(te->detail, position, te->time);
touchFrame();
break;
}
case XI_TouchEnd: {
touchUp(te->detail, te->time);
touchFrame();
break;
}
case XI_TouchOwnership: {
auto te = reinterpret_cast<xXITouchOwnershipEvent*>(e);
XIAllowTouchEvents(m_display, te->deviceid, te->sourceid, te->touchid, XIAcceptTouch);
break;
}
}
break;
}
#endif
default:
break;
}

View file

@ -87,6 +87,8 @@ private:
void handleExpose(xcb_expose_event_t *event);
void updateSize(xcb_configure_notify_event_t *event);
void createCursor(const QImage &img, const QPoint &hotspot);
void initXInputForWindow(xcb_window_t window);
void initXInput();
xcb_connection_t *m_connection = nullptr;
xcb_screen_t *m_screen = nullptr;
@ -106,6 +108,11 @@ private:
xcb_cursor_t m_cursor = XCB_CURSOR_NONE;
Display *m_display = nullptr;
bool m_keyboardGrabbed = false;
bool m_hasXInput = false;
int m_xiOpcode = 0;
int m_majorVersion = 0;
int m_minorVersion = 0;
};
}