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:
parent
f76280231f
commit
d49642ca15
5 changed files with 178 additions and 26 deletions
55
plugins/platforms/x11/common/ge_event_mem_mover.h
Normal file
55
plugins/platforms/x11/common/ge_event_mem_mover.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue