diff options
Diffstat (limited to 'src/platformsupport/input/libinput')
9 files changed, 1095 insertions, 0 deletions
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 <libudev.h> +#include <libinput.h> +#include <QtCore/QLoggingCategory> +#include <QtCore/QSocketNotifier> +#include <QtCore/private/qcore_unix_p.h> + +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 <QtCore/QObject> +#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 <QtCore/QTextCodec> +#include <qpa/qwindowsysteminterface.h> +#include <libinput.h> +#ifndef QT_NO_XKBCOMMON_EVDEV +#include <xkbcommon/xkbcommon-keysyms.h> +#include <xkbcommon/xkbcommon-names.h> +#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 <QtCore/QPoint> +#include <QtCore/QTimer> + +#ifndef QT_NO_XKBCOMMON_EVDEV +#include <xkbcommon/xkbcommon.h> +#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 <libinput.h> +#include <QtGui/QGuiApplication> +#include <QtGui/QScreen> +#include <qpa/qwindowsysteminterface.h> + +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 <QtCore/QPoint> + +// +// 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 <libinput.h> +#include <QtGui/QGuiApplication> +#include <QtGui/QScreen> + +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 <QtCore/QHash> +#include <QtCore/QList> +#include <qpa/qwindowsysteminterface.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 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<QWindowSystemInterface::TouchPoint> m_points; + QTouchDevice *m_touchDevice; + }; + + DeviceState *deviceState(libinput_event_touch *e); + + QHash<libinput_device *, DeviceState> m_devState; +}; + +QT_END_NAMESPACE + +#endif |