Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

Commit d49642ca authored by Martin Flöser's avatar Martin Flöser

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
parent f7628023
/********************************************************************
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;
};
}
......@@ -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:
......
......@@ -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
......
......@@ -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;
}
......
......@@ -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;
};
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment