From e78ec1c48aa9e8912fe9ef96d5f0eb7ef66feadf Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 9 Dec 2014 11:45:29 +0100 Subject: Add libinput support Supports relative pointer, axis, keyboard and touch events. libinput support is only available in combination with libudev. libxkbcommon is required to perform key mapping. For now the default keymap is used always (selected when building xkbcommon). [ChangeLog][QtGui] Added a plugin to get mouse, keyboard and touch events via libinput. Change-Id: I469d8992c0cd3e79225cefaeb931697baf86a92b Reviewed-by: Shawn Rutledge --- src/platformsupport/input/input.pri | 4 + src/platformsupport/input/libinput/libinput.pri | 21 ++ .../input/libinput/qlibinputhandler.cpp | 188 ++++++++++++++ .../input/libinput/qlibinputhandler_p.h | 90 +++++++ .../input/libinput/qlibinputkeyboard.cpp | 279 +++++++++++++++++++++ .../input/libinput/qlibinputkeyboard_p.h | 98 ++++++++ .../input/libinput/qlibinputpointer.cpp | 101 ++++++++ .../input/libinput/qlibinputpointer_p.h | 70 ++++++ .../input/libinput/qlibinputtouch.cpp | 165 ++++++++++++ .../input/libinput/qlibinputtouch_p.h | 83 ++++++ 10 files changed, 1099 insertions(+) create mode 100644 src/platformsupport/input/libinput/libinput.pri create mode 100644 src/platformsupport/input/libinput/qlibinputhandler.cpp create mode 100644 src/platformsupport/input/libinput/qlibinputhandler_p.h create mode 100644 src/platformsupport/input/libinput/qlibinputkeyboard.cpp create mode 100644 src/platformsupport/input/libinput/qlibinputkeyboard_p.h create mode 100644 src/platformsupport/input/libinput/qlibinputpointer.cpp create mode 100644 src/platformsupport/input/libinput/qlibinputpointer_p.h create mode 100644 src/platformsupport/input/libinput/qlibinputtouch.cpp create mode 100644 src/platformsupport/input/libinput/qlibinputtouch_p.h (limited to 'src/platformsupport') diff --git a/src/platformsupport/input/input.pri b/src/platformsupport/input/input.pri index c1fd95703b..3b9593eb31 100644 --- a/src/platformsupport/input/input.pri +++ b/src/platformsupport/input/input.pri @@ -8,3 +8,7 @@ contains(QT_CONFIG, evdev) { contains(QT_CONFIG, tslib) { include($$PWD/tslib/tslib.pri) } + +contains(QT_CONFIG, libinput) { + include($$PWD/libinput/libinput.pri) +} diff --git a/src/platformsupport/input/libinput/libinput.pri b/src/platformsupport/input/libinput/libinput.pri new file mode 100644 index 0000000000..bed9e79738 --- /dev/null +++ b/src/platformsupport/input/libinput/libinput.pri @@ -0,0 +1,21 @@ +HEADERS += \ + $$PWD/qlibinputhandler_p.h \ + $$PWD/qlibinputpointer_p.h \ + $$PWD/qlibinputkeyboard_p.h \ + $$PWD/qlibinputtouch_p.h + +SOURCES += \ + $$PWD/qlibinputhandler.cpp \ + $$PWD/qlibinputpointer.cpp \ + $$PWD/qlibinputkeyboard.cpp \ + $$PWD/qlibinputtouch.cpp + +INCLUDEPATH += $$QMAKE_INCDIR_LIBUDEV $$QMAKE_INCDIR_LIBINPUT +LIBS_PRIVATE += $$QMAKE_LIBS_LIBUDEV $$QMAKE_LIBS_LIBINPUT + +contains(QT_CONFIG, xkbcommon-evdev) { + INCLUDEPATH += $$QMAKE_INCDIR_XKBCOMMON_EVDEV + LIBS_PRIVATE += $$QMAKE_LIBS_XKBCOMMON_EVDEV +} else { + DEFINES += QT_NO_XKBCOMMON_EVDEV +} diff --git a/src/platformsupport/input/libinput/qlibinputhandler.cpp b/src/platformsupport/input/libinput/qlibinputhandler.cpp new file mode 100644 index 0000000000..557c6eb435 --- /dev/null +++ b/src/platformsupport/input/libinput/qlibinputhandler.cpp @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qlibinputhandler_p.h" +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(qLcInput, "qt.qpa.input") + +static int liOpen(const char *path, int flags, void *user_data) +{ + Q_UNUSED(user_data); + return qt_safe_open(path, flags); +} + +static void liClose(int fd, void *user_data) +{ + Q_UNUSED(user_data); + qt_safe_close(fd); +} + +static const struct libinput_interface liInterface = { + liOpen, + liClose +}; + +static void liLogHandler(libinput *libinput, libinput_log_priority priority, const char *format, va_list args) +{ + Q_UNUSED(libinput); + Q_UNUSED(priority); + + char buf[512]; + int n = vsnprintf(buf, sizeof(buf), format, args); + if (n > 0) { + if (buf[n - 1] == '\n') + buf[n - 1] = '\0'; + qCDebug(qLcInput, "libinput: %s", buf); + } +} + +QLibInputHandler::QLibInputHandler(const QString &key, const QString &spec) +{ + Q_UNUSED(key); + Q_UNUSED(spec); + + m_udev = udev_new(); + if (!m_udev) + qFatal("Failed to get udev context for libinput"); + + m_li = libinput_udev_create_context(&liInterface, Q_NULLPTR, m_udev); + if (!m_li) + qFatal("Failed to get libinput context"); + + libinput_log_set_handler(m_li, liLogHandler); + if (qLcInput().isDebugEnabled()) + libinput_log_set_priority(m_li, LIBINPUT_LOG_PRIORITY_DEBUG); + + if (libinput_udev_assign_seat(m_li, "seat0")) + qFatal("Failed to assign seat"); + + m_liFd = libinput_get_fd(m_li); + m_notifier = new QSocketNotifier(m_liFd, QSocketNotifier::Read); + connect(m_notifier, SIGNAL(activated(int)), SLOT(onReadyRead())); + + // Process the initial burst of DEVICE_ADDED events. + onReadyRead(); +} + +QLibInputHandler::~QLibInputHandler() +{ + delete m_notifier; + + if (m_li) + libinput_unref(m_li); + + if (m_udev) + udev_unref(m_udev); +} + +void QLibInputHandler::onReadyRead() +{ + if (libinput_dispatch(m_li)) { + qWarning("libinput_dispatch failed"); + return; + } + + libinput_event *ev; + while ((ev = libinput_get_event(m_li)) != Q_NULLPTR) { + processEvent(ev); + libinput_event_destroy(ev); + } +} + +void QLibInputHandler::processEvent(libinput_event *ev) +{ + libinput_event_type type = libinput_event_get_type(ev); + libinput_device *dev = libinput_event_get_device(ev); + + switch (type) { + case LIBINPUT_EVENT_DEVICE_ADDED: + { + // This is not just for hotplugging, it is also called for each input + // device libinput reads from on startup. Hence it is suitable for doing + // touch device registration. + const char *sysname = libinput_device_get_sysname(dev); // node name without path + const char *name = libinput_device_get_name(dev); + emit deviceAdded(QString::fromUtf8(sysname), QString::fromUtf8(name)); + if (libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_TOUCH)) + m_touch.registerDevice(dev); + break; + } + case LIBINPUT_EVENT_DEVICE_REMOVED: + { + const char *sysname = libinput_device_get_sysname(dev); + const char *name = libinput_device_get_name(dev); + emit deviceRemoved(QString::fromUtf8(sysname), QString::fromUtf8(name)); + if (libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_TOUCH)) + m_touch.unregisterDevice(dev); + break; + } + case LIBINPUT_EVENT_POINTER_BUTTON: + m_pointer.processButton(libinput_event_get_pointer_event(ev)); + break; + case LIBINPUT_EVENT_POINTER_MOTION: + m_pointer.processMotion(libinput_event_get_pointer_event(ev)); + break; + case LIBINPUT_EVENT_POINTER_AXIS: + m_pointer.processAxis(libinput_event_get_pointer_event(ev)); + break; + case LIBINPUT_EVENT_KEYBOARD_KEY: + m_keyboard.processKey(libinput_event_get_keyboard_event(ev)); + break; + case LIBINPUT_EVENT_TOUCH_DOWN: + m_touch.processTouchDown(libinput_event_get_touch_event(ev)); + break; + case LIBINPUT_EVENT_TOUCH_MOTION: + m_touch.processTouchMotion(libinput_event_get_touch_event(ev)); + break; + case LIBINPUT_EVENT_TOUCH_UP: + m_touch.processTouchUp(libinput_event_get_touch_event(ev)); + break; + case LIBINPUT_EVENT_TOUCH_CANCEL: + m_touch.processTouchCancel(libinput_event_get_touch_event(ev)); + break; + case LIBINPUT_EVENT_TOUCH_FRAME: + m_touch.processTouchFrame(libinput_event_get_touch_event(ev)); + break; + default: + break; + } +} + +QT_END_NAMESPACE diff --git a/src/platformsupport/input/libinput/qlibinputhandler_p.h b/src/platformsupport/input/libinput/qlibinputhandler_p.h new file mode 100644 index 0000000000..b6c88111af --- /dev/null +++ b/src/platformsupport/input/libinput/qlibinputhandler_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QLIBINPUTHANDLER_P_H +#define QLIBINPUTHANDLER_P_H + +#include +#include "qlibinputpointer_p.h" +#include "qlibinputkeyboard_p.h" +#include "qlibinputtouch_p.h" + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +struct udev; +struct libinput; +struct libinput_event; + +QT_BEGIN_NAMESPACE + +class QSocketNotifier; + +class QLibInputHandler : public QObject +{ + Q_OBJECT + +public: + QLibInputHandler(const QString &key, const QString &spec); + ~QLibInputHandler(); + +signals: + void deviceAdded(const QString &sysname, const QString &name); + void deviceRemoved(const QString &sysname, const QString &name); + +private slots: + void onReadyRead(); + +private: + void processEvent(libinput_event *ev); + + udev *m_udev; + libinput *m_li; + int m_liFd; + QSocketNotifier *m_notifier; + QLibInputPointer m_pointer; + QLibInputKeyboard m_keyboard; + QLibInputTouch m_touch; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/platformsupport/input/libinput/qlibinputkeyboard.cpp b/src/platformsupport/input/libinput/qlibinputkeyboard.cpp new file mode 100644 index 0000000000..41c849e8a5 --- /dev/null +++ b/src/platformsupport/input/libinput/qlibinputkeyboard.cpp @@ -0,0 +1,279 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qlibinputkeyboard_p.h" +#include +#include +#include +#ifndef QT_NO_XKBCOMMON_EVDEV +#include +#include +#endif + +QT_BEGIN_NAMESPACE + +const int REPEAT_DELAY = 500; +const int REPEAT_RATE = 100; + +#ifndef QT_NO_XKBCOMMON_EVDEV +struct KeyTabEntry { + int xkbkey; + int qtkey; +}; + +static inline bool operator==(const KeyTabEntry &a, const KeyTabEntry &b) +{ + return a.xkbkey == b.xkbkey; +} + +static const KeyTabEntry keyTab[] = { + { XKB_KEY_Escape, Qt::Key_Escape }, + { XKB_KEY_Tab, Qt::Key_Tab }, + { XKB_KEY_ISO_Left_Tab, Qt::Key_Backtab }, + { XKB_KEY_BackSpace, Qt::Key_Backspace }, + { XKB_KEY_Return, Qt::Key_Return }, + { XKB_KEY_Insert, Qt::Key_Insert }, + { XKB_KEY_Delete, Qt::Key_Delete }, + { XKB_KEY_Clear, Qt::Key_Delete }, + { XKB_KEY_Pause, Qt::Key_Pause }, + { XKB_KEY_Print, Qt::Key_Print }, + + { XKB_KEY_Home, Qt::Key_Home }, + { XKB_KEY_End, Qt::Key_End }, + { XKB_KEY_Left, Qt::Key_Left }, + { XKB_KEY_Up, Qt::Key_Up }, + { XKB_KEY_Right, Qt::Key_Right }, + { XKB_KEY_Down, Qt::Key_Down }, + { XKB_KEY_Prior, Qt::Key_PageUp }, + { XKB_KEY_Next, Qt::Key_PageDown }, + + { XKB_KEY_Shift_L, Qt::Key_Shift }, + { XKB_KEY_Shift_R, Qt::Key_Shift }, + { XKB_KEY_Shift_Lock, Qt::Key_Shift }, + { XKB_KEY_Control_L, Qt::Key_Control }, + { XKB_KEY_Control_R, Qt::Key_Control }, + { XKB_KEY_Meta_L, Qt::Key_Meta }, + { XKB_KEY_Meta_R, Qt::Key_Meta }, + { XKB_KEY_Alt_L, Qt::Key_Alt }, + { XKB_KEY_Alt_R, Qt::Key_Alt }, + { XKB_KEY_Caps_Lock, Qt::Key_CapsLock }, + { XKB_KEY_Num_Lock, Qt::Key_NumLock }, + { XKB_KEY_Scroll_Lock, Qt::Key_ScrollLock }, + { XKB_KEY_Super_L, Qt::Key_Super_L }, + { XKB_KEY_Super_R, Qt::Key_Super_R }, + { XKB_KEY_Menu, Qt::Key_Menu }, + { XKB_KEY_Hyper_L, Qt::Key_Hyper_L }, + { XKB_KEY_Hyper_R, Qt::Key_Hyper_R }, + { XKB_KEY_Help, Qt::Key_Help }, + + { XKB_KEY_KP_Space, Qt::Key_Space }, + { XKB_KEY_KP_Tab, Qt::Key_Tab }, + { XKB_KEY_KP_Enter, Qt::Key_Enter }, + { XKB_KEY_KP_Home, Qt::Key_Home }, + { XKB_KEY_KP_Left, Qt::Key_Left }, + { XKB_KEY_KP_Up, Qt::Key_Up }, + { XKB_KEY_KP_Right, Qt::Key_Right }, + { XKB_KEY_KP_Down, Qt::Key_Down }, + { XKB_KEY_KP_Prior, Qt::Key_PageUp }, + { XKB_KEY_KP_Next, Qt::Key_PageDown }, + { XKB_KEY_KP_End, Qt::Key_End }, + { XKB_KEY_KP_Begin, Qt::Key_Clear }, + { XKB_KEY_KP_Insert, Qt::Key_Insert }, + { XKB_KEY_KP_Delete, Qt::Key_Delete }, + { XKB_KEY_KP_Equal, Qt::Key_Equal }, + { XKB_KEY_KP_Multiply, Qt::Key_Asterisk }, + { XKB_KEY_KP_Add, Qt::Key_Plus }, + { XKB_KEY_KP_Separator, Qt::Key_Comma }, + { XKB_KEY_KP_Subtract, Qt::Key_Minus }, + { XKB_KEY_KP_Decimal, Qt::Key_Period }, + { XKB_KEY_KP_Divide, Qt::Key_Slash }, +}; +#endif + +QLibInputKeyboard::QLibInputKeyboard() +#ifndef QT_NO_XKBCOMMON_EVDEV + : m_ctx(0), + m_keymap(0), + m_state(0) +#endif +{ +#ifndef QT_NO_XKBCOMMON_EVDEV + m_ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + if (!m_ctx) { + qWarning("Failed to create xkb context"); + return; + } + m_keymap = xkb_keymap_new_from_names(m_ctx, Q_NULLPTR, XKB_KEYMAP_COMPILE_NO_FLAGS); + if (!m_keymap) { + qWarning("Failed to compile keymap"); + return; + } + m_state = xkb_state_new(m_keymap); + if (!m_state) { + qWarning("Failed to create xkb state"); + return; + } + m_modindex[0] = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_CTRL); + m_modindex[1] = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_ALT); + m_modindex[2] = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_SHIFT); + m_modindex[3] = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_LOGO); + + m_repeatTimer.setSingleShot(true); + connect(&m_repeatTimer, &QTimer::timeout, this, &QLibInputKeyboard::handleRepeat); +#endif +} + +QLibInputKeyboard::~QLibInputKeyboard() +{ +#ifndef QT_NO_XKBCOMMON_EVDEV + if (m_state) + xkb_state_unref(m_state); + if (m_keymap) + xkb_keymap_unref(m_keymap); + if (m_ctx) + xkb_context_unref(m_ctx); +#endif +} + +void QLibInputKeyboard::processKey(libinput_event_keyboard *e) +{ +#ifndef QT_NO_XKBCOMMON_EVDEV + if (!m_ctx || !m_keymap || !m_state) + return; + + const uint32_t k = libinput_event_keyboard_get_key(e) + 8; + const bool pressed = libinput_event_keyboard_get_key_state(e) == LIBINPUT_KEY_STATE_PRESSED; + + QByteArray chars; + chars.resize(1 + xkb_state_key_get_utf8(m_state, k, Q_NULLPTR, 0)); + xkb_state_key_get_utf8(m_state, k, chars.data(), chars.size()); + const QString text = QString::fromUtf8(chars); + + const xkb_keysym_t sym = xkb_state_key_get_one_sym(m_state, k); + + Qt::KeyboardModifiers mods = Qt::NoModifier; + const int qtkey = keysymToQtKey(sym, &mods, text); + + xkb_state_component modtype = xkb_state_component(XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED); + if (xkb_state_mod_index_is_active(m_state, m_modindex[0], modtype) && (qtkey != Qt::Key_Control || !pressed)) + mods |= Qt::ControlModifier; + if (xkb_state_mod_index_is_active(m_state, m_modindex[1], modtype) && (qtkey != Qt::Key_Alt || !pressed)) + mods |= Qt::AltModifier; + if (xkb_state_mod_index_is_active(m_state, m_modindex[2], modtype) && (qtkey != Qt::Key_Shift || !pressed)) + mods |= Qt::ShiftModifier; + if (xkb_state_mod_index_is_active(m_state, m_modindex[3], modtype) && (qtkey != Qt::Key_Meta || !pressed)) + mods |= Qt::MetaModifier; + + xkb_state_update_key(m_state, k, pressed ? XKB_KEY_DOWN : XKB_KEY_UP); + + QWindowSystemInterface::handleExtendedKeyEvent(Q_NULLPTR, + pressed ? QEvent::KeyPress : QEvent::KeyRelease, + qtkey, mods, k, sym, mods, text); + + if (pressed && xkb_keymap_key_repeats(m_keymap, k)) { + m_repeatData.qtkey = qtkey; + m_repeatData.mods = mods; + m_repeatData.nativeScanCode = k; + m_repeatData.virtualKey = sym; + m_repeatData.nativeMods = mods; + m_repeatData.unicodeText = text; + m_repeatData.repeatCount = 1; + m_repeatTimer.setInterval(REPEAT_DELAY); + m_repeatTimer.start(); + } else if (m_repeatTimer.isActive()) { + m_repeatTimer.stop(); + } + +#else + Q_UNUSED(e); +#endif +} + +#ifndef QT_NO_XKBCOMMON_EVDEV +void QLibInputKeyboard::handleRepeat() +{ + QWindowSystemInterface::handleExtendedKeyEvent(Q_NULLPTR, QEvent::KeyPress, + m_repeatData.qtkey, m_repeatData.mods, + m_repeatData.nativeScanCode, m_repeatData.virtualKey, m_repeatData.nativeMods, + m_repeatData.unicodeText, true, m_repeatData.repeatCount); + m_repeatData.repeatCount += 1; + m_repeatTimer.setInterval(REPEAT_RATE); + m_repeatTimer.start(); +} + +int QLibInputKeyboard::keysymToQtKey(xkb_keysym_t key) const +{ + const size_t elemCount = sizeof(keyTab) / sizeof(KeyTabEntry); + KeyTabEntry e; + e.xkbkey = key; + const KeyTabEntry *result = std::find(keyTab, keyTab + elemCount, e); + return result != keyTab + elemCount ? result->qtkey : 0; +} + +int QLibInputKeyboard::keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers *modifiers, const QString &text) const +{ + int code = 0; +#ifndef QT_NO_TEXTCODEC + QTextCodec *systemCodec = QTextCodec::codecForLocale(); +#endif + if (keysym < 128 || (keysym < 256 +#ifndef QT_NO_TEXTCODEC + && systemCodec->mibEnum() == 4 +#endif + )) { + // upper-case key, if known + code = isprint((int)keysym) ? toupper((int)keysym) : 0; + } else if (keysym >= XKB_KEY_F1 && keysym <= XKB_KEY_F35) { + // function keys + code = Qt::Key_F1 + ((int)keysym - XKB_KEY_F1); + } else if (keysym >= XKB_KEY_KP_Space && keysym <= XKB_KEY_KP_9) { + if (keysym >= XKB_KEY_KP_0) { + // numeric keypad keys + code = Qt::Key_0 + ((int)keysym - XKB_KEY_KP_0); + } else { + code = keysymToQtKey(keysym); + } + *modifiers |= Qt::KeypadModifier; + } else if (text.length() == 1 && text.unicode()->unicode() > 0x1f + && text.unicode()->unicode() != 0x7f + && !(keysym >= XKB_KEY_dead_grave && keysym <= XKB_KEY_dead_currency)) { + code = text.unicode()->toUpper().unicode(); + } else { + // any other keys + code = keysymToQtKey(keysym); + } + return code; +} +#endif + +QT_END_NAMESPACE diff --git a/src/platformsupport/input/libinput/qlibinputkeyboard_p.h b/src/platformsupport/input/libinput/qlibinputkeyboard_p.h new file mode 100644 index 0000000000..bcd4d23d98 --- /dev/null +++ b/src/platformsupport/input/libinput/qlibinputkeyboard_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QLIBINPUTKEYBOARD_P_H +#define QLIBINPUTKEYBOARD_P_H + +#include +#include + +#ifndef QT_NO_XKBCOMMON_EVDEV +#include +#endif + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +struct libinput_event_keyboard; + +QT_BEGIN_NAMESPACE + +class QLibInputKeyboard : public QObject +{ + Q_OBJECT + +public: + QLibInputKeyboard(); + ~QLibInputKeyboard(); + + void processKey(libinput_event_keyboard *e); + +#ifndef QT_NO_XKBCOMMON_EVDEV +private slots: + void handleRepeat(); + +private: + int keysymToQtKey(xkb_keysym_t key) const; + int keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers *modifiers, const QString &text) const; + + xkb_context *m_ctx; + xkb_keymap *m_keymap; + xkb_state *m_state; + xkb_mod_index_t m_modindex[4]; + + QTimer m_repeatTimer; + + struct { + int qtkey; + Qt::KeyboardModifiers mods; + int nativeScanCode; + int virtualKey; + int nativeMods; + QString unicodeText; + int repeatCount; + } m_repeatData; +#endif +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/platformsupport/input/libinput/qlibinputpointer.cpp b/src/platformsupport/input/libinput/qlibinputpointer.cpp new file mode 100644 index 0000000000..28e5529b3c --- /dev/null +++ b/src/platformsupport/input/libinput/qlibinputpointer.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qlibinputpointer_p.h" +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QLibInputPointer::QLibInputPointer() + : m_buttons(Qt::NoButton) +{ +} + +void QLibInputPointer::processButton(libinput_event_pointer *e) +{ + const uint32_t b = libinput_event_pointer_get_button(e); + const bool pressed = libinput_event_pointer_get_button_state(e) == LIBINPUT_BUTTON_STATE_PRESSED; + + Qt::MouseButton button = Qt::NoButton; + switch (b) { + case 0x110: button = Qt::LeftButton; break; // BTN_LEFT + case 0x111: button = Qt::RightButton; break; + case 0x112: button = Qt::MiddleButton; break; + case 0x113: button = Qt::ExtraButton1; break; // AKA Qt::BackButton + case 0x114: button = Qt::ExtraButton2; break; // AKA Qt::ForwardButton + case 0x115: button = Qt::ExtraButton3; break; // AKA Qt::TaskButton + case 0x116: button = Qt::ExtraButton4; break; + case 0x117: button = Qt::ExtraButton5; break; + case 0x118: button = Qt::ExtraButton6; break; + case 0x119: button = Qt::ExtraButton7; break; + case 0x11a: button = Qt::ExtraButton8; break; + case 0x11b: button = Qt::ExtraButton9; break; + case 0x11c: button = Qt::ExtraButton10; break; + case 0x11d: button = Qt::ExtraButton11; break; + case 0x11e: button = Qt::ExtraButton12; break; + case 0x11f: button = Qt::ExtraButton13; break; + } + + if (pressed) + m_buttons |= button; + else + m_buttons &= ~button; + + QWindowSystemInterface::handleMouseEvent(Q_NULLPTR, m_pos, m_pos, m_buttons, QGuiApplication::keyboardModifiers()); +} + +void QLibInputPointer::processMotion(libinput_event_pointer *e) +{ + const double dx = libinput_event_pointer_get_dx(e); + const double dy = libinput_event_pointer_get_dy(e); + const QRect g = QGuiApplication::primaryScreen()->virtualGeometry(); + + m_pos.setX(qBound(g.left(), qRound(m_pos.x() + dx), g.right())); + m_pos.setY(qBound(g.top(), qRound(m_pos.y() + dy), g.bottom())); + + QWindowSystemInterface::handleMouseEvent(Q_NULLPTR, m_pos, m_pos, m_buttons, QGuiApplication::keyboardModifiers()); +} + +void QLibInputPointer::processAxis(libinput_event_pointer *e) +{ + const double v = libinput_event_pointer_get_axis_value(e) * 120; + const Qt::Orientation ori = libinput_event_pointer_get_axis(e) == LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL + ? Qt::Vertical : Qt::Horizontal; + + QWindowSystemInterface::handleWheelEvent(Q_NULLPTR, m_pos, m_pos, qRound(-v), ori, QGuiApplication::keyboardModifiers()); +} + +QT_END_NAMESPACE diff --git a/src/platformsupport/input/libinput/qlibinputpointer_p.h b/src/platformsupport/input/libinput/qlibinputpointer_p.h new file mode 100644 index 0000000000..efc19ab4fd --- /dev/null +++ b/src/platformsupport/input/libinput/qlibinputpointer_p.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QLIBINPUTPOINTER_P_H +#define QLIBINPUTPOINTER_P_H + +#include + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +struct libinput_event_pointer; + +QT_BEGIN_NAMESPACE + +class QLibInputPointer +{ +public: + QLibInputPointer(); + + void processButton(libinput_event_pointer *e); + void processMotion(libinput_event_pointer *e); + void processAxis(libinput_event_pointer *e); + +private: + QPoint m_pos; + Qt::MouseButtons m_buttons; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/platformsupport/input/libinput/qlibinputtouch.cpp b/src/platformsupport/input/libinput/qlibinputtouch.cpp new file mode 100644 index 0000000000..ed453a6a9a --- /dev/null +++ b/src/platformsupport/input/libinput/qlibinputtouch.cpp @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qlibinputtouch_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QWindowSystemInterface::TouchPoint *QLibInputTouch::DeviceState::point(int32_t slot) +{ + const int id = qMax(0, slot); + + for (int i = 0; i < m_points.count(); ++i) + if (m_points.at(i).id == id) + return &m_points[i]; + + return Q_NULLPTR; +} + +QLibInputTouch::DeviceState *QLibInputTouch::deviceState(libinput_event_touch *e) +{ + libinput_device *dev = libinput_event_get_device(libinput_event_touch_get_base_event(e)); + return &m_devState[dev]; +} + +static inline QPointF getPos(libinput_event_touch *e) +{ + const QSize screenSize = QGuiApplication::primaryScreen()->geometry().size(); + const double x = libinput_event_touch_get_x_transformed(e, screenSize.width()); + const double y = libinput_event_touch_get_y_transformed(e, screenSize.height()); + return QPointF(x, y); +} + +void QLibInputTouch::registerDevice(libinput_device *dev) +{ + QTouchDevice *&td = m_devState[dev].m_touchDevice; + td = new QTouchDevice; + td->setName(QString::fromUtf8(libinput_device_get_name(dev))); + td->setType(QTouchDevice::TouchScreen); + td->setCapabilities(QTouchDevice::Position | QTouchDevice::Area); + QWindowSystemInterface::registerTouchDevice(td); +} + +void QLibInputTouch::unregisterDevice(libinput_device *dev) +{ + Q_UNUSED(dev); + // There is no way to remove a QTouchDevice. +} + +void QLibInputTouch::processTouchDown(libinput_event_touch *e) +{ + int slot = libinput_event_touch_get_slot(e); + DeviceState *state = deviceState(e); + QWindowSystemInterface::TouchPoint *tp = state->point(slot); + if (tp) { + qWarning("Incorrect touch state"); + } else { + QWindowSystemInterface::TouchPoint newTp; + newTp.id = qMax(0, slot); + newTp.state = Qt::TouchPointPressed; + newTp.area = QRect(0, 0, 8, 8); + newTp.area.moveCenter(getPos(e)); + state->m_points.append(newTp); + } +} + +void QLibInputTouch::processTouchMotion(libinput_event_touch *e) +{ + int slot = libinput_event_touch_get_slot(e); + DeviceState *state = deviceState(e); + QWindowSystemInterface::TouchPoint *tp = state->point(slot); + if (tp) { + const QPointF p = getPos(e); + if (tp->area.center() != p) { + tp->area.moveCenter(p); + // 'down' may be followed by 'motion' within the same "frame". + // Handle this by compressing and keeping the Pressed state until the 'frame'. + if (tp->state != Qt::TouchPointPressed) + tp->state = Qt::TouchPointMoved; + } else { + tp->state = Qt::TouchPointStationary; + } + } else { + qWarning("Inconsistent touch state (got 'motion' without 'down')"); + } +} + +void QLibInputTouch::processTouchUp(libinput_event_touch *e) +{ + int slot = libinput_event_touch_get_slot(e); + DeviceState *state = deviceState(e); + QWindowSystemInterface::TouchPoint *tp = state->point(slot); + if (tp) { + tp->state = Qt::TouchPointReleased; + // There may not be a Frame event after the last Up. Work this around. + Qt::TouchPointStates s = 0; + for (int i = 0; i < state->m_points.count(); ++i) + s |= state->m_points.at(i).state; + if (s == Qt::TouchPointReleased) + processTouchFrame(e); + } else { + qWarning("Inconsistent touch state (got 'up' without 'down')"); + } +} + +void QLibInputTouch::processTouchCancel(libinput_event_touch *e) +{ + DeviceState *state = deviceState(e); + if (state->m_touchDevice) + QWindowSystemInterface::handleTouchCancelEvent(Q_NULLPTR, state->m_touchDevice, QGuiApplication::keyboardModifiers()); + else + qWarning("TouchCancel without registered device"); +} + +void QLibInputTouch::processTouchFrame(libinput_event_touch *e) +{ + DeviceState *state = deviceState(e); + if (state->m_touchDevice && !state->m_points.isEmpty()) { + QWindowSystemInterface::handleTouchEvent(Q_NULLPTR, state->m_touchDevice, state->m_points, + QGuiApplication::keyboardModifiers()); + for (int i = 0; i < state->m_points.count(); ++i) { + QWindowSystemInterface::TouchPoint &tp(state->m_points[i]); + if (tp.state == Qt::TouchPointReleased) + state->m_points.removeAt(i--); + else if (tp.state == Qt::TouchPointPressed) + tp.state = Qt::TouchPointStationary; + } + } else { + qWarning("TouchFrame without registered device"); + } +} + +QT_END_NAMESPACE diff --git a/src/platformsupport/input/libinput/qlibinputtouch_p.h b/src/platformsupport/input/libinput/qlibinputtouch_p.h new file mode 100644 index 0000000000..dceea5600c --- /dev/null +++ b/src/platformsupport/input/libinput/qlibinputtouch_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QLIBINPUTTOUCH_P_H +#define QLIBINPUTTOUCH_P_H + +#include +#include +#include + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +struct libinput_event_touch; +struct libinput_device; + +QT_BEGIN_NAMESPACE + +class QLibInputTouch +{ +public: + void registerDevice(libinput_device *dev); + void unregisterDevice(libinput_device *dev); + void processTouchDown(libinput_event_touch *e); + void processTouchMotion(libinput_event_touch *e); + void processTouchUp(libinput_event_touch *e); + void processTouchCancel(libinput_event_touch *e); + void processTouchFrame(libinput_event_touch *e); + +private: + struct DeviceState { + DeviceState() : m_touchDevice(0) { } + QWindowSystemInterface::TouchPoint *point(int32_t slot); + QList m_points; + QTouchDevice *m_touchDevice; + }; + + DeviceState *deviceState(libinput_event_touch *e); + + QHash m_devState; +}; + +QT_END_NAMESPACE + +#endif -- cgit v1.2.3