summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/mirclient/qmirclientinput.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/mirclient/qmirclientinput.cpp')
-rw-r--r--src/plugins/platforms/mirclient/qmirclientinput.cpp520
1 files changed, 520 insertions, 0 deletions
diff --git a/src/plugins/platforms/mirclient/qmirclientinput.cpp b/src/plugins/platforms/mirclient/qmirclientinput.cpp
new file mode 100644
index 0000000000..56bc21f420
--- /dev/null
+++ b/src/plugins/platforms/mirclient/qmirclientinput.cpp
@@ -0,0 +1,520 @@
+/****************************************************************************
+**
+** Copyright (C) 2014-2015 Canonical, Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+// Local
+#include "qmirclientinput.h"
+#include "qmirclientintegration.h"
+#include "qmirclientnativeinterface.h"
+#include "qmirclientscreen.h"
+#include "qmirclientwindow.h"
+#include "qmirclientlogging.h"
+#include "qmirclientorientationchangeevent_p.h"
+
+// Qt
+#if !defined(QT_NO_DEBUG)
+#include <QtCore/QThread>
+#endif
+#include <QtCore/qglobal.h>
+#include <QtCore/QCoreApplication>
+#include <private/qguiapplication_p.h>
+#include <qpa/qplatforminputcontext.h>
+#include <qpa/qwindowsysteminterface.h>
+
+#include <xkbcommon/xkbcommon.h>
+#include <xkbcommon/xkbcommon-keysyms.h>
+
+#include <mir_toolkit/mir_client_library.h>
+
+#define LOG_EVENTS 0
+
+// XKB Keysyms which do not map directly to Qt types (i.e. Unicode points)
+static const uint32_t KeyTable[] = {
+ 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,
+
+ XKB_KEY_ISO_Level3_Shift, Qt::Key_AltGr,
+ XKB_KEY_Multi_key, Qt::Key_Multi_key,
+ XKB_KEY_Codeinput, Qt::Key_Codeinput,
+ XKB_KEY_SingleCandidate, Qt::Key_SingleCandidate,
+ XKB_KEY_MultipleCandidate, Qt::Key_MultipleCandidate,
+ XKB_KEY_PreviousCandidate, Qt::Key_PreviousCandidate,
+
+ XKB_KEY_Mode_switch, Qt::Key_Mode_switch,
+ XKB_KEY_script_switch, Qt::Key_Mode_switch,
+ XKB_KEY_XF86AudioRaiseVolume, Qt::Key_VolumeUp,
+ XKB_KEY_XF86AudioLowerVolume, Qt::Key_VolumeDown,
+ XKB_KEY_XF86PowerOff, Qt::Key_PowerOff,
+ XKB_KEY_XF86PowerDown, Qt::Key_PowerDown,
+
+ 0, 0
+};
+
+class QMirClientEvent : public QEvent
+{
+public:
+ QMirClientEvent(QMirClientWindow* window, const MirEvent *event, QEvent::Type type)
+ : QEvent(type), window(window) {
+ nativeEvent = mir_event_ref(event);
+ }
+ ~QMirClientEvent()
+ {
+ mir_event_unref(nativeEvent);
+ }
+
+ QPointer<QMirClientWindow> window;
+ const MirEvent *nativeEvent;
+};
+
+QMirClientInput::QMirClientInput(QMirClientClientIntegration* integration)
+ : QObject(nullptr)
+ , mIntegration(integration)
+ , mEventFilterType(static_cast<QMirClientNativeInterface*>(
+ integration->nativeInterface())->genericEventFilterType())
+ , mEventType(static_cast<QEvent::Type>(QEvent::registerEventType()))
+{
+ // Initialize touch device.
+ mTouchDevice = new QTouchDevice;
+ mTouchDevice->setType(QTouchDevice::TouchScreen);
+ mTouchDevice->setCapabilities(
+ QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::Pressure |
+ QTouchDevice::NormalizedPosition);
+ QWindowSystemInterface::registerTouchDevice(mTouchDevice);
+}
+
+QMirClientInput::~QMirClientInput()
+{
+ // Qt will take care of deleting mTouchDevice.
+}
+
+#if (LOG_EVENTS != 0)
+static const char* nativeEventTypeToStr(MirEventType t)
+{
+ switch (t)
+ {
+ case mir_event_type_key:
+ return "mir_event_type_key";
+ case mir_event_type_motion:
+ return "mir_event_type_motion";
+ case mir_event_type_surface:
+ return "mir_event_type_surface";
+ case mir_event_type_resize:
+ return "mir_event_type_resize";
+ case mir_event_type_prompt_session_state_change:
+ return "mir_event_type_prompt_session_state_change";
+ case mir_event_type_orientation:
+ return "mir_event_type_orientation";
+ case mir_event_type_close_surface:
+ return "mir_event_type_close_surface";
+ case mir_event_type_input:
+ return "mir_event_type_input";
+ default:
+ DLOG("Invalid event type %d", t);
+ return "invalid";
+ }
+}
+#endif // LOG_EVENTS != 0
+
+void QMirClientInput::customEvent(QEvent* event)
+{
+ DASSERT(QThread::currentThread() == thread());
+ QMirClientEvent* ubuntuEvent = static_cast<QMirClientEvent*>(event);
+ const MirEvent *nativeEvent = ubuntuEvent->nativeEvent;
+
+ if ((ubuntuEvent->window == nullptr) || (ubuntuEvent->window->window() == nullptr)) {
+ qWarning() << "Attempted to deliver an event to a non-existent window, ignoring.";
+ return;
+ }
+
+ // Event filtering.
+ long result;
+ if (QWindowSystemInterface::handleNativeEvent(
+ ubuntuEvent->window->window(), mEventFilterType,
+ const_cast<void *>(static_cast<const void *>(nativeEvent)), &result) == true) {
+ DLOG("event filtered out by native interface");
+ return;
+ }
+
+ #if (LOG_EVENTS != 0)
+ LOG("QMirClientInput::customEvent(type=%s)", nativeEventTypeToStr(mir_event_get_type(nativeEvent)));
+ #endif
+
+ // Event dispatching.
+ switch (mir_event_get_type(nativeEvent))
+ {
+ case mir_event_type_input:
+ dispatchInputEvent(ubuntuEvent->window->window(), mir_event_get_input_event(nativeEvent));
+ break;
+ case mir_event_type_resize:
+ {
+ Q_ASSERT(ubuntuEvent->window->screen() == mIntegration->screen());
+
+ auto resizeEvent = mir_event_get_resize_event(nativeEvent);
+
+ mIntegration->screen()->handleWindowSurfaceResize(
+ mir_resize_event_get_width(resizeEvent),
+ mir_resize_event_get_height(resizeEvent));
+
+ ubuntuEvent->window->handleSurfaceResize(mir_resize_event_get_width(resizeEvent),
+ mir_resize_event_get_height(resizeEvent));
+ break;
+ }
+ case mir_event_type_surface:
+ {
+ auto surfaceEvent = mir_event_get_surface_event(nativeEvent);
+ if (mir_surface_event_get_attribute(surfaceEvent) == mir_surface_attrib_focus) {
+ ubuntuEvent->window->handleSurfaceFocusChange(mir_surface_event_get_attribute_value(surfaceEvent) ==
+ mir_surface_focused);
+ }
+ break;
+ }
+ case mir_event_type_orientation:
+ dispatchOrientationEvent(ubuntuEvent->window->window(), mir_event_get_orientation_event(nativeEvent));
+ break;
+ case mir_event_type_close_surface:
+ QWindowSystemInterface::handleCloseEvent(ubuntuEvent->window->window());
+ break;
+ default:
+ DLOG("unhandled event type: %d", static_cast<int>(mir_event_get_type(nativeEvent)));
+ }
+}
+
+void QMirClientInput::postEvent(QMirClientWindow *platformWindow, const MirEvent *event)
+{
+ QWindow *window = platformWindow->window();
+
+ QCoreApplication::postEvent(this, new QMirClientEvent(
+ platformWindow, event, mEventType));
+
+ if ((window->flags().testFlag(Qt::WindowTransparentForInput)) && window->parent()) {
+ QCoreApplication::postEvent(this, new QMirClientEvent(
+ static_cast<QMirClientWindow*>(platformWindow->QPlatformWindow::parent()),
+ event, mEventType));
+ }
+}
+
+void QMirClientInput::dispatchInputEvent(QWindow *window, const MirInputEvent *ev)
+{
+ switch (mir_input_event_get_type(ev))
+ {
+ case mir_input_event_type_key:
+ dispatchKeyEvent(window, ev);
+ break;
+ case mir_input_event_type_touch:
+ dispatchTouchEvent(window, ev);
+ break;
+ case mir_input_event_type_pointer:
+ dispatchPointerEvent(window, ev);
+ break;
+ default:
+ break;
+ }
+}
+
+void QMirClientInput::dispatchTouchEvent(QWindow *window, const MirInputEvent *ev)
+{
+ const MirTouchEvent *tev = mir_input_event_get_touch_event(ev);
+
+ // FIXME(loicm) Max pressure is device specific. That one is for the Samsung Galaxy Nexus. That
+ // needs to be fixed as soon as the compat input lib adds query support.
+ const float kMaxPressure = 1.28;
+ const QRect kWindowGeometry = window->geometry();
+ QList<QWindowSystemInterface::TouchPoint> touchPoints;
+
+
+ // TODO: Is it worth setting the Qt::TouchPointStationary ones? Currently they are left
+ // as Qt::TouchPointMoved
+ const unsigned int kPointerCount = mir_touch_event_point_count(tev);
+ for (unsigned int i = 0; i < kPointerCount; ++i) {
+ QWindowSystemInterface::TouchPoint touchPoint;
+
+ const float kX = mir_touch_event_axis_value(tev, i, mir_touch_axis_x) + kWindowGeometry.x();
+ const float kY = mir_touch_event_axis_value(tev, i, mir_touch_axis_y) + kWindowGeometry.y(); // see bug lp:1346633 workaround comments elsewhere
+ const float kW = mir_touch_event_axis_value(tev, i, mir_touch_axis_touch_major);
+ const float kH = mir_touch_event_axis_value(tev, i, mir_touch_axis_touch_minor);
+ const float kP = mir_touch_event_axis_value(tev, i, mir_touch_axis_pressure);
+ touchPoint.id = mir_touch_event_id(tev, i);
+ touchPoint.normalPosition = QPointF(kX / kWindowGeometry.width(), kY / kWindowGeometry.height());
+ touchPoint.area = QRectF(kX - (kW / 2.0), kY - (kH / 2.0), kW, kH);
+ touchPoint.pressure = kP / kMaxPressure;
+
+ MirTouchAction touch_action = mir_touch_event_action(tev, i);
+ switch (touch_action)
+ {
+ case mir_touch_action_down:
+ touchPoint.state = Qt::TouchPointPressed;
+ break;
+ case mir_touch_action_up:
+ touchPoint.state = Qt::TouchPointReleased;
+ break;
+ case mir_touch_action_change:
+ default:
+ touchPoint.state = Qt::TouchPointMoved;
+ }
+
+ touchPoints.append(touchPoint);
+ }
+
+ ulong timestamp = mir_input_event_get_event_time(ev) / 1000000;
+ QWindowSystemInterface::handleTouchEvent(window, timestamp,
+ mTouchDevice, touchPoints);
+}
+
+static uint32_t translateKeysym(uint32_t sym, char *string, size_t size)
+{
+ Q_UNUSED(size);
+ string[0] = '\0';
+
+ if (sym >= XKB_KEY_F1 && sym <= XKB_KEY_F35)
+ return Qt::Key_F1 + (int(sym) - XKB_KEY_F1);
+
+ for (int i = 0; KeyTable[i]; i += 2) {
+ if (sym == KeyTable[i])
+ return KeyTable[i + 1];
+ }
+
+ string[0] = sym;
+ string[1] = '\0';
+ return toupper(sym);
+}
+
+namespace
+{
+Qt::KeyboardModifiers qt_modifiers_from_mir(MirInputEventModifiers modifiers)
+{
+ Qt::KeyboardModifiers q_modifiers = Qt::NoModifier;
+ if (modifiers & mir_input_event_modifier_shift) {
+ q_modifiers |= Qt::ShiftModifier;
+ }
+ if (modifiers & mir_input_event_modifier_ctrl) {
+ q_modifiers |= Qt::ControlModifier;
+ }
+ if (modifiers & mir_input_event_modifier_alt) {
+ q_modifiers |= Qt::AltModifier;
+ }
+ if (modifiers & mir_input_event_modifier_meta) {
+ q_modifiers |= Qt::MetaModifier;
+ }
+ return q_modifiers;
+}
+}
+
+void QMirClientInput::dispatchKeyEvent(QWindow *window, const MirInputEvent *event)
+{
+ const MirKeyboardEvent *key_event = mir_input_event_get_keyboard_event(event);
+
+ ulong timestamp = mir_input_event_get_event_time(event) / 1000000;
+ xkb_keysym_t xk_sym = mir_keyboard_event_key_code(key_event);
+
+ // Key modifier and unicode index mapping.
+ auto modifiers = qt_modifiers_from_mir(mir_keyboard_event_modifiers(key_event));
+
+ MirKeyboardAction action = mir_keyboard_event_action(key_event);
+ QEvent::Type keyType = action == mir_keyboard_action_up
+ ? QEvent::KeyRelease : QEvent::KeyPress;
+
+ char s[2];
+ int sym = translateKeysym(xk_sym, s, sizeof(s));
+ QString text = QString::fromLatin1(s);
+
+ bool is_auto_rep = action == mir_keyboard_action_repeat;
+
+ QPlatformInputContext *context = QGuiApplicationPrivate::platformIntegration()->inputContext();
+ if (context) {
+ QKeyEvent qKeyEvent(keyType, sym, modifiers, text, is_auto_rep);
+ qKeyEvent.setTimestamp(timestamp);
+ if (context->filterEvent(&qKeyEvent)) {
+ DLOG("key event filtered out by input context");
+ return;
+ }
+ }
+
+ QWindowSystemInterface::handleKeyEvent(window, timestamp, keyType, sym, modifiers, text, is_auto_rep);
+}
+
+namespace
+{
+Qt::MouseButtons extract_buttons(const MirPointerEvent *pev)
+{
+ Qt::MouseButtons buttons = Qt::NoButton;
+ if (mir_pointer_event_button_state(pev, mir_pointer_button_primary))
+ buttons |= Qt::LeftButton;
+ if (mir_pointer_event_button_state(pev, mir_pointer_button_secondary))
+ buttons |= Qt::RightButton;
+ if (mir_pointer_event_button_state(pev, mir_pointer_button_tertiary))
+ buttons |= Qt::MidButton;
+
+ // TODO: Should mir back and forward buttons exist?
+ // should they be Qt::X button 1 and 2?
+ return buttons;
+}
+}
+
+void QMirClientInput::dispatchPointerEvent(QWindow *window, const MirInputEvent *ev)
+{
+ auto timestamp = mir_input_event_get_event_time(ev) / 1000000;
+
+ auto pev = mir_input_event_get_pointer_event(ev);
+ auto modifiers = qt_modifiers_from_mir(mir_pointer_event_modifiers(pev));
+ auto buttons = extract_buttons(pev);
+
+ auto local_point = QPointF(mir_pointer_event_axis_value(pev, mir_pointer_axis_x),
+ mir_pointer_event_axis_value(pev, mir_pointer_axis_y));
+
+ QWindowSystemInterface::handleMouseEvent(window, timestamp, local_point, local_point /* Should we omit global point instead? */,
+ buttons, modifiers);
+}
+
+#if (LOG_EVENTS != 0)
+static const char* nativeOrientationDirectionToStr(MirOrientation orientation)
+{
+ switch (orientation) {
+ case mir_orientation_normal:
+ return "Normal";
+ break;
+ case mir_orientation_left:
+ return "Left";
+ break;
+ case mir_orientation_inverted:
+ return "Inverted";
+ break;
+ case mir_orientation_right:
+ return "Right";
+ break;
+ default:
+ return "INVALID!";
+ }
+}
+#endif
+
+void QMirClientInput::dispatchOrientationEvent(QWindow *window, const MirOrientationEvent *event)
+{
+ MirOrientation mir_orientation = mir_orientation_event_get_direction(event);
+ #if (LOG_EVENTS != 0)
+ // Orientation event logging.
+ LOG("ORIENTATION direction: %s", nativeOrientationDirectionToStr(mir_orientation));
+ #endif
+
+ if (!window->screen()) {
+ DLOG("Window has no associated screen, dropping orientation event");
+ return;
+ }
+
+ OrientationChangeEvent::Orientation orientation;
+ switch (mir_orientation) {
+ case mir_orientation_normal:
+ orientation = OrientationChangeEvent::TopUp;
+ break;
+ case mir_orientation_left:
+ orientation = OrientationChangeEvent::LeftUp;
+ break;
+ case mir_orientation_inverted:
+ orientation = OrientationChangeEvent::TopDown;
+ break;
+ case mir_orientation_right:
+ orientation = OrientationChangeEvent::RightUp;
+ break;
+ default:
+ DLOG("No such orientation %d", mir_orientation);
+ return;
+ }
+
+ // Dispatch orientation event to [Platform]Screen, as that is where Qt reads it. Screen will handle
+ // notifying Qt of the actual orientation change - done to prevent multiple Windows each creating
+ // an identical orientation change event and passing it directly to Qt.
+ // [Platform]Screen can also factor in the native orientation.
+ QCoreApplication::postEvent(static_cast<QMirClientScreen*>(window->screen()->handle()),
+ new OrientationChangeEvent(OrientationChangeEvent::mType, orientation));
+}
+