qtbase/src/plugins/platforms/wayland/qwaylandinputdevice.cpp
Johan Klokkhammer Helsing 3b4dd0f24b Client: Set key repeat rate and delay from the repeat_info event
[ChangeLog][QPA plugin] Clients now follow the keyboard key repeat delay and
interval configured by the compositor.

Change-Id: I8a88d341a2bb648d70f95494fe19e941273ae31b
Reviewed-by: Pier Luigi Fiorini <pierluigi.fiorini@liri.io>
2018-10-17 14:24:46 +00:00

996 lines
30 KiB
C++

/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qwaylandinputdevice_p.h"
#include "qwaylandintegration_p.h"
#include "qwaylandwindow_p.h"
#include "qwaylandbuffer_p.h"
#if QT_CONFIG(wayland_datadevice)
#include "qwaylanddatadevice_p.h"
#include "qwaylanddatadevicemanager_p.h"
#endif
#include "qwaylandtouch_p.h"
#include "qwaylandscreen_p.h"
#include "qwaylandcursor_p.h"
#include "qwaylanddisplay_p.h"
#include "qwaylandshmbackingstore_p.h"
#include "../shared/qwaylandxkb_p.h"
#include "qwaylandinputcontext_p.h"
#include <QtGui/private/qpixmap_raster_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatformwindow.h>
#include <qpa/qplatforminputcontext.h>
#include <QDebug>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#if QT_CONFIG(cursor)
#include <wayland-cursor.h>
#endif
#include <QtGui/QGuiApplication>
#if QT_CONFIG(xkbcommon_evdev)
#include <xkbcommon/xkbcommon-compose.h>
#endif
QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
QWaylandInputDevice::Keyboard::Keyboard(QWaylandInputDevice *p)
: mParent(p)
{
connect(&mRepeatTimer, SIGNAL(timeout()), this, SLOT(repeatKey()));
}
#if QT_CONFIG(xkbcommon_evdev)
bool QWaylandInputDevice::Keyboard::createDefaultKeyMap()
{
if (mXkbContext && mXkbMap && mXkbState) {
return true;
}
xkb_rule_names names;
names.rules = strdup("evdev");
names.model = strdup("pc105");
names.layout = strdup("us");
names.variant = strdup("");
names.options = strdup("");
mXkbContext = xkb_context_new(xkb_context_flags(0));
if (mXkbContext) {
mXkbMap = xkb_map_new_from_names(mXkbContext, &names, xkb_map_compile_flags(0));
if (mXkbMap) {
mXkbState = xkb_state_new(mXkbMap);
}
}
if (!mXkbContext || !mXkbMap || !mXkbState) {
qWarning() << "xkb_map_new_from_names failed, no key input";
return false;
}
createComposeState();
return true;
}
void QWaylandInputDevice::Keyboard::releaseKeyMap()
{
if (mXkbState)
xkb_state_unref(mXkbState);
if (mXkbMap)
xkb_map_unref(mXkbMap);
if (mXkbContext)
xkb_context_unref(mXkbContext);
}
void QWaylandInputDevice::Keyboard::createComposeState()
{
static const char *locale = nullptr;
if (!locale) {
locale = getenv("LC_ALL");
if (!locale)
locale = getenv("LC_CTYPE");
if (!locale)
locale = getenv("LANG");
if (!locale)
locale = "C";
}
mXkbComposeTable = xkb_compose_table_new_from_locale(mXkbContext, locale, XKB_COMPOSE_COMPILE_NO_FLAGS);
if (mXkbComposeTable)
mXkbComposeState = xkb_compose_state_new(mXkbComposeTable, XKB_COMPOSE_STATE_NO_FLAGS);
}
void QWaylandInputDevice::Keyboard::releaseComposeState()
{
if (mXkbComposeState)
xkb_compose_state_unref(mXkbComposeState);
if (mXkbComposeTable)
xkb_compose_table_unref(mXkbComposeTable);
mXkbComposeState = nullptr;
mXkbComposeTable = nullptr;
}
#endif
QWaylandInputDevice::Keyboard::~Keyboard()
{
#if QT_CONFIG(xkbcommon_evdev)
releaseComposeState();
releaseKeyMap();
#endif
if (mFocus)
QWindowSystemInterface::handleWindowActivated(nullptr);
if (mParent->mVersion >= 3)
wl_keyboard_release(object());
else
wl_keyboard_destroy(object());
}
void QWaylandInputDevice::Keyboard::stopRepeat()
{
mRepeatTimer.stop();
}
QWaylandInputDevice::Pointer::Pointer(QWaylandInputDevice *p)
: mParent(p)
{
}
QWaylandInputDevice::Pointer::~Pointer()
{
if (mParent->mVersion >= 3)
wl_pointer_release(object());
else
wl_pointer_destroy(object());
}
QWaylandInputDevice::Touch::Touch(QWaylandInputDevice *p)
: mParent(p)
{
}
QWaylandInputDevice::Touch::~Touch()
{
if (mParent->mVersion >= 3)
wl_touch_release(object());
else
wl_touch_destroy(object());
}
QWaylandInputDevice::QWaylandInputDevice(QWaylandDisplay *display, int version, uint32_t id)
: QtWayland::wl_seat(display->wl_registry(), id, qMin(version, 4))
, mQDisplay(display)
, mDisplay(display->wl_display())
, mVersion(qMin(version, 4))
{
#if QT_CONFIG(wayland_datadevice)
if (mQDisplay->dndSelectionHandler()) {
mDataDevice = mQDisplay->dndSelectionHandler()->getDataDevice(this);
}
#endif
if (mQDisplay->textInputManager()) {
mTextInput = new QWaylandTextInput(mQDisplay, mQDisplay->textInputManager()->get_text_input(wl_seat()));
}
}
QWaylandInputDevice::~QWaylandInputDevice()
{
delete mPointer;
delete mKeyboard;
delete mTouch;
}
void QWaylandInputDevice::seat_capabilities(uint32_t caps)
{
mCaps = caps;
if (caps & WL_SEAT_CAPABILITY_KEYBOARD && !mKeyboard) {
mKeyboard = createKeyboard(this);
mKeyboard->init(get_keyboard());
} else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && mKeyboard) {
delete mKeyboard;
mKeyboard = nullptr;
}
if (caps & WL_SEAT_CAPABILITY_POINTER && !mPointer) {
mPointer = createPointer(this);
mPointer->init(get_pointer());
pointerSurface = mQDisplay->createSurface(this);
} else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && mPointer) {
delete mPointer;
mPointer = nullptr;
}
if (caps & WL_SEAT_CAPABILITY_TOUCH && !mTouch) {
mTouch = createTouch(this);
mTouch->init(get_touch());
if (!mTouchDevice) {
mTouchDevice = new QTouchDevice;
mTouchDevice->setType(QTouchDevice::TouchScreen);
mTouchDevice->setCapabilities(QTouchDevice::Position);
QWindowSystemInterface::registerTouchDevice(mTouchDevice);
}
} else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && mTouch) {
delete mTouch;
mTouch = nullptr;
}
}
QWaylandInputDevice::Keyboard *QWaylandInputDevice::createKeyboard(QWaylandInputDevice *device)
{
return new Keyboard(device);
}
QWaylandInputDevice::Pointer *QWaylandInputDevice::createPointer(QWaylandInputDevice *device)
{
return new Pointer(device);
}
QWaylandInputDevice::Touch *QWaylandInputDevice::createTouch(QWaylandInputDevice *device)
{
return new Touch(device);
}
void QWaylandInputDevice::handleWindowDestroyed(QWaylandWindow *window)
{
if (mKeyboard && window == mKeyboard->mFocus)
mKeyboard->stopRepeat();
}
void QWaylandInputDevice::handleEndDrag()
{
if (mTouch)
mTouch->releasePoints();
if (mPointer)
mPointer->releaseButtons();
}
#if QT_CONFIG(wayland_datadevice)
void QWaylandInputDevice::setDataDevice(QWaylandDataDevice *device)
{
mDataDevice = device;
}
QWaylandDataDevice *QWaylandInputDevice::dataDevice() const
{
return mDataDevice;
}
#endif
void QWaylandInputDevice::setTextInput(QWaylandTextInput *textInput)
{
mTextInput = textInput;
}
QWaylandTextInput *QWaylandInputDevice::textInput() const
{
return mTextInput;
}
void QWaylandInputDevice::removeMouseButtonFromState(Qt::MouseButton button)
{
if (mPointer)
mPointer->mButtons = mPointer->mButtons & !button;
}
QWaylandWindow *QWaylandInputDevice::pointerFocus() const
{
return mPointer ? mPointer->mFocus : nullptr;
}
QWaylandWindow *QWaylandInputDevice::keyboardFocus() const
{
return mKeyboard ? mKeyboard->mFocus : nullptr;
}
QWaylandWindow *QWaylandInputDevice::touchFocus() const
{
return mTouch ? mTouch->mFocus : nullptr;
}
Qt::KeyboardModifiers QWaylandInputDevice::modifiers() const
{
if (!mKeyboard)
return Qt::NoModifier;
return mKeyboard->modifiers();
}
Qt::KeyboardModifiers QWaylandInputDevice::Keyboard::modifiers() const
{
Qt::KeyboardModifiers ret = Qt::NoModifier;
#if QT_CONFIG(xkbcommon_evdev)
if (!mXkbState)
return ret;
ret = QWaylandXkb::modifiers(mXkbState);
#endif
return ret;
}
#if QT_CONFIG(cursor)
uint32_t QWaylandInputDevice::cursorSerial() const
{
if (mPointer)
return mPointer->mCursorSerial;
return 0;
}
void QWaylandInputDevice::setCursor(Qt::CursorShape newShape, QWaylandScreen *screen)
{
struct wl_cursor_image *image = screen->waylandCursor()->cursorImage(newShape);
if (!image) {
return;
}
struct wl_buffer *buffer = wl_cursor_image_get_buffer(image);
setCursor(buffer, image, screen->devicePixelRatio());
}
void QWaylandInputDevice::setCursor(const QCursor &cursor, QWaylandScreen *screen)
{
if (mPointer->mCursorSerial >= mPointer->mEnterSerial && (cursor.shape() != Qt::BitmapCursor && cursor.shape() == mPointer->mCursorShape))
return;
mPointer->mCursorShape = cursor.shape();
if (cursor.shape() == Qt::BitmapCursor) {
setCursor(screen->waylandCursor()->cursorBitmapImage(&cursor), cursor.hotSpot(), screen->devicePixelRatio());
return;
}
setCursor(cursor.shape(), screen);
}
void QWaylandInputDevice::setCursor(struct wl_buffer *buffer, struct wl_cursor_image *image, int bufferScale)
{
if (image) {
// Convert from pixel coordinates to surface coordinates
QPoint hotspot = QPoint(image->hotspot_x, image->hotspot_y) / bufferScale;
QSize size = QSize(image->width, image->height) / bufferScale;
setCursor(buffer, hotspot, size, bufferScale);
} else {
setCursor(buffer, QPoint(), QSize(), bufferScale);
}
}
// size and hotspot are in surface coordinates
void QWaylandInputDevice::setCursor(struct wl_buffer *buffer, const QPoint &hotSpot, const QSize &size, int bufferScale)
{
if (mCaps & WL_SEAT_CAPABILITY_POINTER) {
bool force = mPointer->mEnterSerial > mPointer->mCursorSerial;
if (!force && mPointer->mCursorBuffer == buffer)
return;
mPixmapCursor.clear();
mPointer->mCursorSerial = mPointer->mEnterSerial;
mPointer->mCursorBuffer = buffer;
/* Hide cursor */
if (!buffer)
{
mPointer->set_cursor(mPointer->mEnterSerial, nullptr, 0, 0);
return;
}
mPointer->set_cursor(mPointer->mEnterSerial, pointerSurface,
hotSpot.x(), hotSpot.y());
wl_surface_attach(pointerSurface, buffer, 0, 0);
if (mQDisplay->compositorVersion() >= 3)
wl_surface_set_buffer_scale(pointerSurface, bufferScale);
wl_surface_damage(pointerSurface, 0, 0, size.width(), size.height());
wl_surface_commit(pointerSurface);
}
}
void QWaylandInputDevice::setCursor(const QSharedPointer<QWaylandBuffer> &buffer, const QPoint &hotSpot, int bufferScale)
{
setCursor(buffer->buffer(), hotSpot, buffer->size(), bufferScale);
mPixmapCursor = buffer;
}
#endif
class EnterEvent : public QWaylandPointerEvent
{
public:
EnterEvent(const QPointF &l, const QPointF &g)
: QWaylandPointerEvent(QWaylandPointerEvent::Enter, 0, l, g, nullptr, Qt::NoModifier)
{}
};
void QWaylandInputDevice::Pointer::pointer_enter(uint32_t serial, struct wl_surface *surface,
wl_fixed_t sx, wl_fixed_t sy)
{
if (!surface)
return;
QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface);
#if QT_CONFIG(cursor)
window->window()->setCursor(window->window()->cursor());
#endif
mFocus = window;
mSurfacePos = QPointF(wl_fixed_to_double(sx), wl_fixed_to_double(sy));
mGlobalPos = window->window()->mapToGlobal(mSurfacePos.toPoint());
mParent->mSerial = serial;
mEnterSerial = serial;
QWaylandWindow *grab = QWaylandWindow::mouseGrab();
if (!grab) {
EnterEvent evt(mSurfacePos, mGlobalPos);
window->handleMouse(mParent, evt);
}
}
void QWaylandInputDevice::Pointer::pointer_leave(uint32_t time, struct wl_surface *surface)
{
// The event may arrive after destroying the window, indicated by
// a null surface.
if (!surface)
return;
if (!QWaylandWindow::mouseGrab()) {
QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface);
window->handleMouseLeave(mParent);
}
mFocus = nullptr;
mButtons = Qt::NoButton;
mParent->mTime = time;
}
class MotionEvent : public QWaylandPointerEvent
{
public:
MotionEvent(ulong t, const QPointF &l, const QPointF &g, Qt::MouseButtons b, Qt::KeyboardModifiers m)
: QWaylandPointerEvent(QWaylandPointerEvent::Motion, t, l, g, b, m)
{
}
};
void QWaylandInputDevice::Pointer::pointer_motion(uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y)
{
QWaylandWindow *window = mFocus;
if (!window) {
// We destroyed the pointer focus surface, but the server
// didn't get the message yet.
return;
}
QPointF pos(wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y));
QPointF delta = pos - pos.toPoint();
QPointF global = window->window()->mapToGlobal(pos.toPoint());
global += delta;
mSurfacePos = pos;
mGlobalPos = global;
mParent->mTime = time;
QWaylandWindow *grab = QWaylandWindow::mouseGrab();
if (grab && grab != window) {
// We can't know the true position since we're getting events for another surface,
// so we just set it outside of the window boundaries.
pos = QPointF(-1, -1);
global = grab->window()->mapToGlobal(pos.toPoint());
MotionEvent e(time, pos, global, mButtons, mParent->modifiers());
grab->handleMouse(mParent, e);
} else {
MotionEvent e(time, mSurfacePos, mGlobalPos, mButtons, mParent->modifiers());
window->handleMouse(mParent, e);
}
}
void QWaylandInputDevice::Pointer::pointer_button(uint32_t serial, uint32_t time,
uint32_t button, uint32_t state)
{
QWaylandWindow *window = mFocus;
Qt::MouseButton qt_button;
// translate from kernel (input.h) 'button' to corresponding Qt:MouseButton.
// The range of mouse values is 0x110 <= mouse_button < 0x120, the first Joystick button.
switch (button) {
case 0x110: qt_button = Qt::LeftButton; break; // kernel BTN_LEFT
case 0x111: qt_button = Qt::RightButton; break;
case 0x112: qt_button = Qt::MiddleButton; break;
case 0x113: qt_button = Qt::ExtraButton1; break; // AKA Qt::BackButton
case 0x114: qt_button = Qt::ExtraButton2; break; // AKA Qt::ForwardButton
case 0x115: qt_button = Qt::ExtraButton3; break; // AKA Qt::TaskButton
case 0x116: qt_button = Qt::ExtraButton4; break;
case 0x117: qt_button = Qt::ExtraButton5; break;
case 0x118: qt_button = Qt::ExtraButton6; break;
case 0x119: qt_button = Qt::ExtraButton7; break;
case 0x11a: qt_button = Qt::ExtraButton8; break;
case 0x11b: qt_button = Qt::ExtraButton9; break;
case 0x11c: qt_button = Qt::ExtraButton10; break;
case 0x11d: qt_button = Qt::ExtraButton11; break;
case 0x11e: qt_button = Qt::ExtraButton12; break;
case 0x11f: qt_button = Qt::ExtraButton13; break;
default: return; // invalid button number (as far as Qt is concerned)
}
if (state)
mButtons |= qt_button;
else
mButtons &= ~qt_button;
mParent->mTime = time;
mParent->mSerial = serial;
if (state)
mParent->mQDisplay->setLastInputDevice(mParent, serial, window);
QWaylandWindow *grab = QWaylandWindow::mouseGrab();
if (grab && grab != mFocus) {
QPointF pos = QPointF(-1, -1);
QPointF global = grab->window()->mapToGlobal(pos.toPoint());
MotionEvent e(time, pos, global, mButtons, mParent->modifiers());
grab->handleMouse(mParent, e);
} else if (window) {
MotionEvent e(time, mSurfacePos, mGlobalPos, mButtons, mParent->modifiers());
window->handleMouse(mParent, e);
}
}
void QWaylandInputDevice::Pointer::releaseButtons()
{
mButtons = Qt::NoButton;
MotionEvent e(mParent->mTime, mSurfacePos, mGlobalPos, mButtons, mParent->modifiers());
if (mFocus)
mFocus->handleMouse(mParent, e);
}
class WheelEvent : public QWaylandPointerEvent
{
public:
WheelEvent(ulong t, const QPointF &l, const QPointF &g, const QPoint &pd, const QPoint &ad, Qt::KeyboardModifiers m)
: QWaylandPointerEvent(QWaylandPointerEvent::Wheel, t, l, g, pd, ad, m)
{
}
};
void QWaylandInputDevice::Pointer::pointer_axis(uint32_t time, uint32_t axis, int32_t value)
{
QWaylandWindow *window = mFocus;
QPoint pixelDelta;
QPoint angleDelta;
if (!window) {
// We destroyed the pointer focus surface, but the server
// didn't get the message yet.
return;
}
//normalize value and inverse axis
int valueDelta = wl_fixed_to_int(value) * -12;
if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) {
pixelDelta = QPoint();
angleDelta.setX(valueDelta);
} else {
pixelDelta = QPoint();
angleDelta.setY(valueDelta);
}
WheelEvent e(time, mSurfacePos, mGlobalPos, pixelDelta, angleDelta, mParent->modifiers());
window->handleMouse(mParent, e);
}
void QWaylandInputDevice::Keyboard::keyboard_keymap(uint32_t format, int32_t fd, uint32_t size)
{
#if QT_CONFIG(xkbcommon_evdev)
if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
close(fd);
return;
}
char *map_str = static_cast<char *>(mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0));
if (map_str == MAP_FAILED) {
close(fd);
return;
}
// Release the old keymap resources in the case they were already created in
// the key event or when the compositor issues a new map
releaseComposeState();
releaseKeyMap();
mXkbContext = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
mXkbMap = xkb_map_new_from_string(mXkbContext, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
munmap(map_str, size);
close(fd);
mXkbState = xkb_state_new(mXkbMap);
createComposeState();
#else
Q_UNUSED(format);
Q_UNUSED(fd);
Q_UNUSED(size);
#endif
}
void QWaylandInputDevice::Keyboard::keyboard_enter(uint32_t time, struct wl_surface *surface, struct wl_array *keys)
{
Q_UNUSED(time);
Q_UNUSED(keys);
if (!surface)
return;
QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface);
mFocus = window;
mParent->mQDisplay->handleKeyboardFocusChanged(mParent);
}
void QWaylandInputDevice::Keyboard::keyboard_leave(uint32_t time, struct wl_surface *surface)
{
Q_UNUSED(time);
Q_UNUSED(surface);
if (surface) {
QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface);
window->unfocus();
}
mFocus = nullptr;
mParent->mQDisplay->handleKeyboardFocusChanged(mParent);
mRepeatTimer.stop();
}
static void sendKey(QWindow *tlw, ulong timestamp, QEvent::Type type, int key, Qt::KeyboardModifiers modifiers,
quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers,
const QString& text = QString(), bool autorep = false, ushort count = 1)
{
QPlatformInputContext *inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext();
bool filtered = false;
if (inputContext) {
QKeyEvent event(type, key, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers,
text, autorep, count);
event.setTimestamp(timestamp);
filtered = inputContext->filterEvent(&event);
}
if (!filtered) {
QWindowSystemInterface::handleExtendedKeyEvent(tlw, timestamp, type, key, modifiers,
nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count);
}
}
void QWaylandInputDevice::Keyboard::keyboard_key(uint32_t serial, uint32_t time, uint32_t key, uint32_t state)
{
QWaylandWindow *window = mFocus;
uint32_t code = key + 8;
bool isDown = state != WL_KEYBOARD_KEY_STATE_RELEASED;
QEvent::Type type = isDown ? QEvent::KeyPress : QEvent::KeyRelease;
QString text;
int qtkey = key + 8; // qt-compositor substracts 8 for some reason
mParent->mSerial = serial;
if (!window) {
// We destroyed the keyboard focus surface, but the server
// didn't get the message yet.
return;
}
if (isDown)
mParent->mQDisplay->setLastInputDevice(mParent, serial, window);
#if QT_CONFIG(xkbcommon_evdev)
if (!createDefaultKeyMap()) {
return;
}
QString composedText;
xkb_keysym_t sym = xkb_state_key_get_one_sym(mXkbState, code);
if (mXkbComposeState) {
if (isDown)
xkb_compose_state_feed(mXkbComposeState, sym);
xkb_compose_status status = xkb_compose_state_get_status(mXkbComposeState);
switch (status) {
case XKB_COMPOSE_COMPOSED: {
int size = xkb_compose_state_get_utf8(mXkbComposeState, nullptr, 0);
QVarLengthArray<char, 32> buffer(size + 1);
xkb_compose_state_get_utf8(mXkbComposeState, buffer.data(), buffer.size());
composedText = QString::fromUtf8(buffer.constData());
sym = xkb_compose_state_get_one_sym(mXkbComposeState);
xkb_compose_state_reset(mXkbComposeState);
} break;
case XKB_COMPOSE_COMPOSING:
case XKB_COMPOSE_CANCELLED:
return;
case XKB_COMPOSE_NOTHING:
break;
}
}
Qt::KeyboardModifiers modifiers = mParent->modifiers();
std::tie(qtkey, text) = QWaylandXkb::keysymToQtKey(sym, modifiers);
if (!composedText.isNull())
text = composedText;
sendKey(window->window(), time, type, qtkey, modifiers, code, sym, mNativeModifiers, text);
#else
// Generic fallback for single hard keys: Assume 'key' is a Qt key code.
sendKey(window->window(), time, type, qtkey, Qt::NoModifier, code, 0, 0);
#endif
if (state == WL_KEYBOARD_KEY_STATE_PRESSED
#if QT_CONFIG(xkbcommon_evdev)
&& xkb_keymap_key_repeats(mXkbMap, code)
#endif
) {
mRepeatKey = qtkey;
mRepeatCode = code;
mRepeatTime = time;
mRepeatText = text;
#if QT_CONFIG(xkbcommon_evdev)
mRepeatSym = sym;
#endif
mRepeatTimer.setInterval(mRepeatDelay);
mRepeatTimer.start();
} else if (mRepeatCode == code) {
mRepeatTimer.stop();
}
}
void QWaylandInputDevice::Keyboard::repeatKey()
{
mRepeatTimer.setInterval(mRepeatRate);
sendKey(mFocus->window(), mRepeatTime, QEvent::KeyRelease, mRepeatKey, modifiers(), mRepeatCode,
#if QT_CONFIG(xkbcommon_evdev)
mRepeatSym, mNativeModifiers,
#else
0, 0,
#endif
mRepeatText, true);
sendKey(mFocus->window(), mRepeatTime, QEvent::KeyPress, mRepeatKey, modifiers(), mRepeatCode,
#if QT_CONFIG(xkbcommon_evdev)
mRepeatSym, mNativeModifiers,
#else
0, 0,
#endif
mRepeatText, true);
}
void QWaylandInputDevice::Keyboard::keyboard_modifiers(uint32_t serial,
uint32_t mods_depressed,
uint32_t mods_latched,
uint32_t mods_locked,
uint32_t group)
{
Q_UNUSED(serial);
#if QT_CONFIG(xkbcommon_evdev)
if (mXkbState)
xkb_state_update_mask(mXkbState,
mods_depressed, mods_latched, mods_locked,
0, 0, group);
mNativeModifiers = mods_depressed | mods_latched | mods_locked;
#else
Q_UNUSED(serial);
Q_UNUSED(mods_depressed);
Q_UNUSED(mods_latched);
Q_UNUSED(mods_locked);
Q_UNUSED(group);
#endif
}
void QWaylandInputDevice::Keyboard::keyboard_repeat_info(int32_t rate, int32_t delay)
{
mRepeatRate = rate;
mRepeatDelay = delay;
}
void QWaylandInputDevice::Touch::touch_down(uint32_t serial,
uint32_t time,
struct wl_surface *surface,
int32_t id,
wl_fixed_t x,
wl_fixed_t y)
{
if (!surface)
return;
mParent->mTime = time;
mParent->mSerial = serial;
mFocus = QWaylandWindow::fromWlSurface(surface);
mParent->mQDisplay->setLastInputDevice(mParent, serial, mFocus);
mParent->handleTouchPoint(id, wl_fixed_to_double(x), wl_fixed_to_double(y), Qt::TouchPointPressed);
}
void QWaylandInputDevice::Touch::touch_up(uint32_t serial, uint32_t time, int32_t id)
{
Q_UNUSED(serial);
Q_UNUSED(time);
mFocus = nullptr;
mParent->handleTouchPoint(id, 0, 0, Qt::TouchPointReleased);
// As of Weston 1.5.90 there is no touch_frame after the last touch_up
// (i.e. when the last finger is released). To accommodate for this, issue a
// touch_frame. This cannot hurt since it is safe to call the touch_frame
// handler multiple times when there are no points left.
if (allTouchPointsReleased())
touch_frame();
}
void QWaylandInputDevice::Touch::touch_motion(uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y)
{
Q_UNUSED(time);
mParent->handleTouchPoint(id, wl_fixed_to_double(x), wl_fixed_to_double(y), Qt::TouchPointMoved);
}
void QWaylandInputDevice::Touch::touch_cancel()
{
mPrevTouchPoints.clear();
mTouchPoints.clear();
QWaylandTouchExtension *touchExt = mParent->mQDisplay->touchExtension();
if (touchExt)
touchExt->touchCanceled();
QWindowSystemInterface::handleTouchCancelEvent(nullptr, mParent->mTouchDevice);
}
void QWaylandInputDevice::handleTouchPoint(int id, double x, double y, Qt::TouchPointState state)
{
QWindowSystemInterface::TouchPoint tp;
// Find out the coordinates for Released events.
bool coordsOk = false;
if (state == Qt::TouchPointReleased)
for (int i = 0; i < mTouch->mPrevTouchPoints.count(); ++i)
if (mTouch->mPrevTouchPoints.at(i).id == id) {
tp.area = mTouch->mPrevTouchPoints.at(i).area;
coordsOk = true;
break;
}
if (!coordsOk) {
// x and y are surface relative.
// We need a global (screen) position.
QWaylandWindow *win = mTouch->mFocus;
//is it possible that mTouchFocus is null;
if (!win && mPointer)
win = mPointer->mFocus;
if (!win && mKeyboard)
win = mKeyboard->mFocus;
if (!win || !win->window())
return;
tp.area = QRectF(0, 0, 8, 8);
QMargins margins = win->frameMargins();
tp.area.moveCenter(win->window()->mapToGlobal(QPoint(x - margins.left(), y - margins.top())));
}
tp.state = state;
tp.id = id;
tp.pressure = tp.state == Qt::TouchPointReleased ? 0 : 1;
mTouch->mTouchPoints.append(tp);
}
bool QWaylandInputDevice::Touch::allTouchPointsReleased()
{
for (int i = 0; i < mTouchPoints.count(); ++i)
if (mTouchPoints.at(i).state != Qt::TouchPointReleased)
return false;
return true;
}
void QWaylandInputDevice::Touch::releasePoints()
{
Q_FOREACH (const QWindowSystemInterface::TouchPoint &previousPoint, mPrevTouchPoints) {
QWindowSystemInterface::TouchPoint tp = previousPoint;
tp.state = Qt::TouchPointReleased;
mTouchPoints.append(tp);
}
touch_frame();
}
void QWaylandInputDevice::Touch::touch_frame()
{
// Copy all points, that are in the previous but not in the current list, as stationary.
for (int i = 0; i < mPrevTouchPoints.count(); ++i) {
const QWindowSystemInterface::TouchPoint &prevPoint(mPrevTouchPoints.at(i));
if (prevPoint.state == Qt::TouchPointReleased)
continue;
bool found = false;
for (int j = 0; j < mTouchPoints.count(); ++j)
if (mTouchPoints.at(j).id == prevPoint.id) {
found = true;
break;
}
if (!found) {
QWindowSystemInterface::TouchPoint p = prevPoint;
p.state = Qt::TouchPointStationary;
mTouchPoints.append(p);
}
}
if (mTouchPoints.isEmpty()) {
mPrevTouchPoints.clear();
return;
}
QWindow *window = mFocus ? mFocus->window() : nullptr;
if (mFocus) {
const QWindowSystemInterface::TouchPoint &tp = mTouchPoints.last();
// When the touch event is received, the global pos is calculated with the margins
// in mind. Now we need to adjust again to get the correct local pos back.
QMargins margins = window->frameMargins();
QPoint p = tp.area.center().toPoint();
QPointF localPos(window->mapFromGlobal(QPoint(p.x() + margins.left(), p.y() + margins.top())));
if (mFocus->touchDragDecoration(mParent, localPos, tp.area.center(), tp.state, mParent->modifiers()))
return;
}
QWindowSystemInterface::handleTouchEvent(window, mParent->mTouchDevice, mTouchPoints);
if (allTouchPointsReleased())
mPrevTouchPoints.clear();
else
mPrevTouchPoints = mTouchPoints;
mTouchPoints.clear();
}
}
QT_END_NAMESPACE