From 5359d420afda4a4562f27d44251ae8cf764406b8 Mon Sep 17 00:00:00 2001 From: David Skoland Date: Tue, 7 Dec 2021 17:16:36 +0100 Subject: [PATCH] Move input event logic into QWasmCompositor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the window logic and state out of QWasmEventTranslator and into QWasmCompositor. The reasoning for this change is that the state of the windowing system was split between QWasmCompositor (eg. window stack) and QWasmEventTranslator (eg. dragged and pressed windows). This change moves the input handling and state to QWasmCompositor and demotes QWasmEventTranslator to a helper class. In this model, QWC acts as a proper window system / display server and can leverage multiple helper classes which are "owned" by the QWC. Here is a quick diagram illustrating the proposed general architecture around WASM window handling and its difference from typical OSes: https://i.imgur.com/vcBwUPf.png Change-Id: Idca617992b8e1b431e5c71d46b5db61597c99e75 Pick-to: 6.3 Reviewed-by: Morten Johan Sørvig --- .../platforms/wasm/qwasmcompositor.cpp | 504 +++++++++++++- src/plugins/platforms/wasm/qwasmcompositor.h | 52 ++ .../platforms/wasm/qwasmeventtranslator.cpp | 618 +++--------------- .../platforms/wasm/qwasmeventtranslator.h | 56 +- .../platforms/wasm/qwasmintegration.cpp | 5 + src/plugins/platforms/wasm/qwasmintegration.h | 1 + src/plugins/platforms/wasm/qwasmscreen.cpp | 2 +- src/plugins/platforms/wasm/qwasmwindow.cpp | 20 +- src/plugins/platforms/wasm/qwasmwindow.h | 16 +- 9 files changed, 667 insertions(+), 607 deletions(-) diff --git a/src/plugins/platforms/wasm/qwasmcompositor.cpp b/src/plugins/platforms/wasm/qwasmcompositor.cpp index aa498c8238..048dcfa2fc 100644 --- a/src/plugins/platforms/wasm/qwasmcompositor.cpp +++ b/src/plugins/platforms/wasm/qwasmcompositor.cpp @@ -28,9 +28,11 @@ ****************************************************************************/ #include "qwasmcompositor.h" -#include "qwasmeventdispatcher.h" -#include "qwasmwindow.h" #include "qwasmstylepixmaps_p.h" +#include "qwasmwindow.h" +#include "qwasmeventtranslator.h" +#include "qwasmeventdispatcher.h" +#include "qwasmclipboard.h" #include @@ -47,6 +49,10 @@ #include #include +#include + +using namespace emscripten; + Q_GUI_EXPORT int qt_defaultDpiX(); QWasmCompositedWindow::QWasmCompositedWindow() @@ -57,6 +63,25 @@ QWasmCompositedWindow::QWasmCompositedWindow() { } +// macOS CTRL <-> META switching. We most likely want to enable +// the existing switching code in QtGui, but for now do it here. + + +bool g_useNaturalScrolling = true; // natural scrolling is default on linux/windows + +static void mouseWheelEvent(emscripten::val event) { + + emscripten::val wheelInterted = event["webkitDirectionInvertedFromDevice"]; + + if (wheelInterted.as()) { + g_useNaturalScrolling = true; + } +} + +EMSCRIPTEN_BINDINGS(qtMouseModule) { + function("qtMouseWheelEvent", &mouseWheelEvent); +} + QWasmCompositor::QWasmCompositor(QWasmScreen *screen) :QObject(screen) , m_blitter(new QOpenGLTextureBlitter) @@ -65,16 +90,51 @@ QWasmCompositor::QWasmCompositor(QWasmScreen *screen) , m_inResize(false) , m_isEnabled(true) , m_targetDevicePixelRatio(1) + , draggedWindow(nullptr) + , lastWindow(nullptr) + , pressedButtons(Qt::NoButton) + , resizeMode(QWasmCompositor::ResizeNone) + , eventTranslator(new QWasmEventTranslator()) { + touchDevice = new QPointingDevice( + "touchscreen", 1, QInputDevice::DeviceType::TouchScreen, + QPointingDevice::PointerType::Finger, + QPointingDevice::Capability::Position | QPointingDevice::Capability::Area | QPointingDevice::Capability::NormalizedPosition, + 10, 0); + QWindowSystemInterface::registerInputDevice(touchDevice); + + initEventHandlers(); } QWasmCompositor::~QWasmCompositor() { if (m_requestAnimationFrameId != -1) emscripten_cancel_animation_frame(m_requestAnimationFrameId); + + deregisterEventHandlers(); destroy(); } +void QWasmCompositor::deregisterEventHandlers() +{ + QByteArray canvasSelector = "#" + screen()->canvasId().toUtf8(); + emscripten_set_keydown_callback(canvasSelector.constData(), 0, 0, NULL); + emscripten_set_keyup_callback(canvasSelector.constData(), 0, 0, NULL); + + emscripten_set_mousedown_callback(canvasSelector.constData(), 0, 0, NULL); + emscripten_set_mouseup_callback(canvasSelector.constData(), 0, 0, NULL); + emscripten_set_mousemove_callback(canvasSelector.constData(), 0, 0, NULL); + + emscripten_set_focus_callback(canvasSelector.constData(), 0, 0, NULL); + + emscripten_set_wheel_callback(canvasSelector.constData(), 0, 0, NULL); + + emscripten_set_touchstart_callback(canvasSelector.constData(), 0, 0, NULL); + emscripten_set_touchend_callback(canvasSelector.constData(), 0, 0, NULL); + emscripten_set_touchmove_callback(canvasSelector.constData(), 0, 0, NULL); + emscripten_set_touchcancel_callback(canvasSelector.constData(), 0, 0, NULL); +} + void QWasmCompositor::destroy() { // Destroy OpenGL resources. This is done here in a separate function @@ -96,6 +156,48 @@ void QWasmCompositor::destroy() m_isEnabled = false; // prevent frame() from creating a new m_context } +void QWasmCompositor::initEventHandlers() +{ + QByteArray canvasSelector = "#" + screen()->canvasId().toUtf8(); + + // The Platform Detect: expand coverage and move as needed + enum Platform { + GenericPlatform, + MacOSPlatform + }; + Platform platform = Platform(emscripten::val::global("navigator")["platform"] + .call("includes", emscripten::val("Mac"))); + + eventTranslator->setIsMac(platform == MacOSPlatform); + + if (platform == MacOSPlatform) { + g_useNaturalScrolling = false; // make this !default on macOS + + if (!emscripten::val::global("window")["safari"].isUndefined()) { + val canvas = screen()->canvas(); + canvas.call("addEventListener", + val("wheel"), + val::module_property("qtMouseWheelEvent")); + } + } + + emscripten_set_keydown_callback(canvasSelector.constData(), (void *)this, 1, &keyboard_cb); + emscripten_set_keyup_callback(canvasSelector.constData(), (void *)this, 1, &keyboard_cb); + + emscripten_set_mousedown_callback(canvasSelector.constData(), (void *)this, 1, &mouse_cb); + emscripten_set_mouseup_callback(canvasSelector.constData(), (void *)this, 1, &mouse_cb); + emscripten_set_mousemove_callback(canvasSelector.constData(), (void *)this, 1, &mouse_cb); + + emscripten_set_focus_callback(canvasSelector.constData(), (void *)this, 1, &focus_cb); + + emscripten_set_wheel_callback(canvasSelector.constData(), (void *)this, 1, &wheel_cb); + + emscripten_set_touchstart_callback(canvasSelector.constData(), (void *)this, 1, &touchCallback); + emscripten_set_touchend_callback(canvasSelector.constData(), (void *)this, 1, &touchCallback); + emscripten_set_touchmove_callback(canvasSelector.constData(), (void *)this, 1, &touchCallback); + emscripten_set_touchcancel_callback(canvasSelector.constData(), (void *)this, 1, &touchCallback); +} + void QWasmCompositor::setEnabled(bool enabled) { m_isEnabled = enabled; @@ -784,6 +886,62 @@ void QWasmCompositor::frame() m_context->swapBuffers(someWindow->window()); } +void QWasmCompositor::resizeWindow(QWindow *window, QWasmCompositor::ResizeMode mode, + QRect startRect, QPoint amount) +{ + if (mode == QWasmCompositor::ResizeNone) + return; + + bool top = mode == QWasmCompositor::ResizeTopLeft || + mode == QWasmCompositor::ResizeTop || + mode == QWasmCompositor::ResizeTopRight; + + bool bottom = mode == QWasmCompositor::ResizeBottomLeft || + mode == QWasmCompositor::ResizeBottom || + mode == QWasmCompositor::ResizeBottomRight; + + bool left = mode == QWasmCompositor::ResizeLeft || + mode == QWasmCompositor::ResizeTopLeft || + mode == QWasmCompositor::ResizeBottomLeft; + + bool right = mode == QWasmCompositor::ResizeRight || + mode == QWasmCompositor::ResizeTopRight || + mode == QWasmCompositor::ResizeBottomRight; + + int x1 = startRect.left(); + int y1 = startRect.top(); + int x2 = startRect.right(); + int y2 = startRect.bottom(); + + if (left) + x1 += amount.x(); + if (top) + y1 += amount.y(); + if (right) + x2 += amount.x(); + if (bottom) + y2 += amount.y(); + + int w = x2-x1; + int h = y2-y1; + + if (w < window->minimumWidth()) { + if (left) + x1 -= window->minimumWidth() - w; + + w = window->minimumWidth(); + } + + if (h < window->minimumHeight()) { + if (top) + y1 -= window->minimumHeight() - h; + + h = window->minimumHeight(); + } + + window->setGeometry(x1, y1, w, h); +} + void QWasmCompositor::notifyTopWindowChanged(QWasmWindow *window) { QWindow *modalWindow; @@ -807,3 +965,345 @@ QOpenGLContext *QWasmCompositor::context() { return m_context.data(); } + +int QWasmCompositor::keyboard_cb(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData) +{ + QWasmCompositor *wasmCompositor = reinterpret_cast(userData); + bool accepted = wasmCompositor->processKeyboard(eventType, keyEvent); + + return accepted ? 1 : 0; +} + +int QWasmCompositor::mouse_cb(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) +{ + QWasmCompositor *compositor = (QWasmCompositor*)userData; + bool accepted = compositor->processMouse(eventType, mouseEvent); + return accepted; +} + +int QWasmCompositor::focus_cb(int /*eventType*/, const EmscriptenFocusEvent */*focusEvent*/, void */*userData*/) +{ + return 0; +} + +int QWasmCompositor::wheel_cb(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData) +{ + QWasmCompositor *compositor = (QWasmCompositor *) userData; + bool accepted = compositor->processWheel(eventType, wheelEvent); + return accepted ? 1 : 0; +} + +int QWasmCompositor::touchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData) +{ + auto compositor = reinterpret_cast(userData); + return compositor->handleTouch(eventType, touchEvent); +} + +bool QWasmCompositor::processMouse(int eventType, const EmscriptenMouseEvent *mouseEvent) +{ + QPoint targetPoint(mouseEvent->targetX, mouseEvent->targetY); + QPoint globalPoint = screen()->geometry().topLeft() + targetPoint; + + QEvent::Type buttonEventType = QEvent::None; + Qt::MouseButton button = Qt::NoButton; + Qt::KeyboardModifiers modifiers = eventTranslator->translateMouseEventModifier(mouseEvent); + + QWindow *window2 = nullptr; + if (resizeMode == QWasmCompositor::ResizeNone) + window2 = screen()->compositor()->windowAt(globalPoint, 5); + + if (window2 == nullptr) { + window2 = lastWindow; + } else { + lastWindow = window2; + } + + QPoint localPoint = window2->mapFromGlobal(globalPoint); + bool interior = window2->geometry().contains(globalPoint); + + QWasmWindow *htmlWindow = static_cast(window2->handle()); + switch (eventType) { + case EMSCRIPTEN_EVENT_MOUSEDOWN: + { + button = QWasmEventTranslator::translateMouseButton(mouseEvent->button); + + if (window2) + window2->requestActivate(); + + pressedButtons.setFlag(button); + + pressedWindow = window2; + buttonEventType = QEvent::MouseButtonPress; + + // button overview: + // 0 = primary mouse button, usually left click + // 1 = middle mouse button, usually mouse wheel + // 2 = right mouse button, usually right click + // from: https://w3c.github.io/uievents/#dom-mouseevent-button + if (mouseEvent->button == 0) { + if (!(htmlWindow->m_windowState & Qt::WindowFullScreen) && !(htmlWindow->m_windowState & Qt::WindowMaximized)) { + if (htmlWindow && window2->flags().testFlag(Qt::WindowTitleHint) && htmlWindow->isPointOnTitle(globalPoint)) + draggedWindow = window2; + else if (htmlWindow && htmlWindow->isPointOnResizeRegion(globalPoint)) { + draggedWindow = window2; + resizeMode = htmlWindow->resizeModeAtPoint(globalPoint); + resizePoint = globalPoint; + resizeStartRect = window2->geometry(); + } + } + } + + htmlWindow->injectMousePressed(localPoint, globalPoint, button, modifiers); + break; + } + case EMSCRIPTEN_EVENT_MOUSEUP: + { + button = QWasmEventTranslator::translateMouseButton(mouseEvent->button); + pressedButtons.setFlag(button, false); + buttonEventType = QEvent::MouseButtonRelease; + QWasmWindow *oldWindow = nullptr; + + if (mouseEvent->button == 0 && pressedWindow) { + oldWindow = static_cast(pressedWindow->handle()); + pressedWindow = nullptr; + } + + if (draggedWindow && pressedButtons.testFlag(Qt::NoButton)) { + draggedWindow = nullptr; + resizeMode = QWasmCompositor::ResizeNone; + } + + if (oldWindow) + oldWindow->injectMouseReleased(localPoint, globalPoint, button, modifiers); + else + htmlWindow->injectMouseReleased(localPoint, globalPoint, button, modifiers); + break; + } + case EMSCRIPTEN_EVENT_MOUSEMOVE: // drag event + { + buttonEventType = QEvent::MouseMove; + + if (htmlWindow && pressedButtons.testFlag(Qt::NoButton)) { + + if (htmlWindow->isPointOnResizeRegion(globalPoint)) { + QCursor resizingCursor = eventTranslator->cursorForMode(htmlWindow->resizeModeAtPoint(globalPoint)); + + if (resizingCursor != window2->cursor()) { + isCursorOverridden = true; + QWasmCursor::setOverrideWasmCursor(&resizingCursor, window2->screen()); + } + } else { // off resizing area + if (isCursorOverridden) { + isCursorOverridden = false; + QWasmCursor::clearOverrideWasmCursor(window2->screen()); + } + } + } + + if (!(htmlWindow->m_windowState & Qt::WindowFullScreen) && !(htmlWindow->m_windowState & Qt::WindowMaximized)) { + if (resizeMode == QWasmCompositor::ResizeNone && draggedWindow) { + draggedWindow->setX(draggedWindow->x() + mouseEvent->movementX); + draggedWindow->setY(draggedWindow->y() + mouseEvent->movementY); + } + + if (resizeMode != QWasmCompositor::ResizeNone && !(htmlWindow->m_windowState & Qt::WindowFullScreen)) { + QPoint delta = QPoint(mouseEvent->targetX, mouseEvent->targetY) - resizePoint; + resizeWindow(draggedWindow, resizeMode, resizeStartRect, delta); + } + } + break; + } + default: // MOUSELEAVE MOUSEENTER + break; + }; + if (!window2 && buttonEventType == QEvent::MouseButtonRelease) { + window2 = lastWindow; + lastWindow = nullptr; + interior = true; + } + bool accepted = true; + if (window2 && interior) { + accepted = QWindowSystemInterface::handleMouseEvent( + window2, QWasmIntegration::getTimestamp(), localPoint, globalPoint, pressedButtons, button, buttonEventType, modifiers); + } + return accepted; +} + +bool QWasmCompositor::processKeyboard(int eventType, const EmscriptenKeyboardEvent *keyEvent) +{ + Qt::Key qtKey; + QString keyText; + QEvent::Type keyType = QEvent::None; + switch (eventType) { + case EMSCRIPTEN_EVENT_KEYPRESS: + case EMSCRIPTEN_EVENT_KEYDOWN: // down + keyType = QEvent::KeyPress; + keyText = this->eventTranslator->getKeyText(keyEvent); + qtKey = this->eventTranslator->getKey(keyEvent); + break; + case EMSCRIPTEN_EVENT_KEYUP: // up + keyType = QEvent::KeyRelease; + this->eventTranslator->setStickyDeadKey(keyEvent); + break; + default: + break; + }; + + if (keyType == QEvent::None) + return 0; + + QFlags modifiers = eventTranslator->translateKeyboardEventModifier(keyEvent); + + // Clipboard fallback path: cut/copy/paste are handled by clipboard event + // handlers if direct clipboard access is not available. + if (!QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi && modifiers & Qt::ControlModifier && + (qtKey == Qt::Key_X || qtKey == Qt::Key_C || qtKey == Qt::Key_V)) { + if (qtKey == Qt::Key_V) { + QWasmIntegration::get()->getWasmClipboard()->isPaste = true; + } + return false; + } + + bool accepted = false; + + if (keyType == QEvent::KeyPress && + modifiers.testFlag(Qt::ControlModifier) + && qtKey == Qt::Key_V) { + QWasmIntegration::get()->getWasmClipboard()->isPaste = true; + accepted = false; // continue on to event + } else { + if (keyText.isEmpty()) + keyText = QString(keyEvent->key); + if (keyText.size() > 1) + keyText.clear(); + accepted = QWindowSystemInterface::handleKeyEvent( + 0, keyType, qtKey, modifiers, keyText); + } + if (keyType == QEvent::KeyPress && + modifiers.testFlag(Qt::ControlModifier) + && qtKey == Qt::Key_C) { + QWasmIntegration::get()->getWasmClipboard()->isPaste = false; + accepted = false; // continue on to event + } + + return accepted; +} + +bool QWasmCompositor::processWheel(int eventType, const EmscriptenWheelEvent *wheelEvent) +{ + Q_UNUSED(eventType); + + EmscriptenMouseEvent mouseEvent = wheelEvent->mouse; + + int scrollFactor = 0; + switch (wheelEvent->deltaMode) { + case DOM_DELTA_PIXEL://chrome safari + scrollFactor = 1; + break; + case DOM_DELTA_LINE: //firefox + scrollFactor = 12; + break; + case DOM_DELTA_PAGE: + scrollFactor = 20; + break; + }; + + if (g_useNaturalScrolling) //macOS platform has document oriented scrolling + scrollFactor = -scrollFactor; + + Qt::KeyboardModifiers modifiers = eventTranslator->translateMouseEventModifier(&mouseEvent); + QPoint targetPoint(mouseEvent.targetX, mouseEvent.targetY); + QPoint globalPoint = screen()->geometry().topLeft() + targetPoint; + + QWindow *window2 = screen()->compositor()->windowAt(globalPoint, 5); + if (!window2) + return 0; + QPoint localPoint = window2->mapFromGlobal(globalPoint); + + QPoint pixelDelta; + + if (wheelEvent->deltaY != 0) pixelDelta.setY(wheelEvent->deltaY * scrollFactor); + if (wheelEvent->deltaX != 0) pixelDelta.setX(wheelEvent->deltaX * scrollFactor); + + bool accepted = QWindowSystemInterface::handleWheelEvent( + window2, QWasmIntegration::getTimestamp(), localPoint, + globalPoint, QPoint(), pixelDelta, modifiers); + return accepted; +} + +int QWasmCompositor::handleTouch(int eventType, const EmscriptenTouchEvent *touchEvent) +{ + QList touchPointList; + touchPointList.reserve(touchEvent->numTouches); + QWindow *window2; + + for (int i = 0; i < touchEvent->numTouches; i++) { + + const EmscriptenTouchPoint *touches = &touchEvent->touches[i]; + + QPoint targetPoint(touches->targetX, touches->targetY); + QPoint globalPoint = screen()->geometry().topLeft() + targetPoint; + + window2 = this->screen()->compositor()->windowAt(globalPoint, 5); + if (window2 == nullptr) + continue; + + QWindowSystemInterface::TouchPoint touchPoint; + + touchPoint.area = QRect(0, 0, 8, 8); + touchPoint.id = touches->identifier; + touchPoint.pressure = 1.0; + + touchPoint.area.moveCenter(globalPoint); + + const auto tp = pressedTouchIds.constFind(touchPoint.id); + if (tp != pressedTouchIds.constEnd()) + touchPoint.normalPosition = tp.value(); + + QPointF localPoint = QPointF(window2->mapFromGlobal(globalPoint)); + QPointF normalPosition(localPoint.x() / window2->width(), + localPoint.y() / window2->height()); + + const bool stationaryTouchPoint = (normalPosition == touchPoint.normalPosition); + touchPoint.normalPosition = normalPosition; + + switch (eventType) { + case EMSCRIPTEN_EVENT_TOUCHSTART: + if (tp != pressedTouchIds.constEnd()) { + touchPoint.state = (stationaryTouchPoint + ? QEventPoint::State::Stationary + : QEventPoint::State::Updated); + } else { + touchPoint.state = QEventPoint::State::Pressed; + } + pressedTouchIds.insert(touchPoint.id, touchPoint.normalPosition); + + break; + case EMSCRIPTEN_EVENT_TOUCHEND: + touchPoint.state = QEventPoint::State::Released; + pressedTouchIds.remove(touchPoint.id); + break; + case EMSCRIPTEN_EVENT_TOUCHMOVE: + touchPoint.state = (stationaryTouchPoint + ? QEventPoint::State::Stationary + : QEventPoint::State::Updated); + + pressedTouchIds.insert(touchPoint.id, touchPoint.normalPosition); + break; + default: + break; + } + + touchPointList.append(touchPoint); + } + + QFlags keyModifier = eventTranslator->translateTouchEventModifier(touchEvent); + + bool accepted = QWindowSystemInterface::handleTouchEvent( + window2, QWasmIntegration::getTimestamp(), touchDevice, touchPointList, keyModifier); + + if (eventType == EMSCRIPTEN_EVENT_TOUCHCANCEL) + accepted = QWindowSystemInterface::handleTouchCancelEvent(window2, QWasmIntegration::getTimestamp(), touchDevice, keyModifier); + + return static_cast(accepted); +} diff --git a/src/plugins/platforms/wasm/qwasmcompositor.h b/src/plugins/platforms/wasm/qwasmcompositor.h index 1a51016c03..1feda04870 100644 --- a/src/plugins/platforms/wasm/qwasmcompositor.h +++ b/src/plugins/platforms/wasm/qwasmcompositor.h @@ -36,6 +36,12 @@ #include #include #include +#include + +#include +#include + +#include QT_BEGIN_NAMESPACE @@ -43,6 +49,7 @@ class QWasmWindow; class QWasmScreen; class QOpenGLContext; class QOpenGLTexture; +class QWasmEventTranslator; class QWasmCompositedWindow { @@ -63,6 +70,7 @@ class QWasmCompositor : public QObject public: QWasmCompositor(QWasmScreen *screen); ~QWasmCompositor(); + void deregisterEventHandlers(); void destroy(); enum QWasmSubControl { @@ -84,6 +92,18 @@ public: }; Q_DECLARE_FLAGS(StateFlags, QWasmStateFlag) + enum ResizeMode { + ResizeNone, + ResizeTopLeft, + ResizeTop, + ResizeTopRight, + ResizeRight, + ResizeBottomRight, + ResizeBottom, + ResizeBottomLeft, + ResizeLeft + }; + struct QWasmTitleBarOptions { QRect rect; Qt::WindowFlags flags; @@ -128,9 +148,17 @@ public: void deliverUpdateRequests(); void deliverUpdateRequest(QWasmWindow *window, UpdateRequestDeliveryType updateType); void handleBackingStoreFlush(); + bool processMouse(int eventType, const EmscriptenMouseEvent *mouseEvent); + bool processKeyboard(int eventType, const EmscriptenKeyboardEvent *keyEvent); + bool processWheel(int eventType, const EmscriptenWheelEvent *wheelEvent); + int handleTouch(int eventType, const EmscriptenTouchEvent *touchEvent); + void resizeWindow(QWindow *window, QWasmCompositor::ResizeMode mode, QRect startRect, QPoint amount); + +private slots: void frame(); private: + void initEventHandlers(); void notifyTopWindowChanged(QWasmWindow *window); void drawWindow(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window); void drawWindowContent(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window); @@ -156,6 +184,21 @@ private: int m_requestAnimationFrameId = -1; bool m_inDeliverUpdateRequest = false; + QPointer draggedWindow; + QPointer pressedWindow; + QPointer lastWindow; + Qt::MouseButtons pressedButtons; + + QWasmCompositor::ResizeMode resizeMode; + QPoint resizePoint; + QRect resizeStartRect; + QPointingDevice *touchDevice; + + QMap pressedTouchIds; + + QCursor overriddenCursor; + bool isCursorOverridden = false; + static QPalette makeWindowPalette(); void drawFrameWindow(QWasmFrameOptions options, QPainter *painter); @@ -163,6 +206,15 @@ private: void drawShadePanel(QWasmTitleBarOptions options, QPainter *painter); void drawItemPixmap(QPainter *painter, const QRect &rect, int alignment, const QPixmap &pixmap) const; + + static int keyboard_cb(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData); + static int mouse_cb(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData); + static int focus_cb(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData); + static int wheel_cb(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData); + + static int touchCallback(int eventType, const EmscriptenTouchEvent *ev, void *userData); + + QWasmEventTranslator *eventTranslator; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QWasmCompositor::SubControls) diff --git a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp index 34b1a7358a..0285ace1e6 100644 --- a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp +++ b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp @@ -177,101 +177,12 @@ static constexpr const auto DeadKeyShiftTbl = qMakeArray( >::Data{} ); -// macOS CTRL <-> META switching. We most likely want to enable -// the existing switching code in QtGui, but for now do it here. -static bool g_usePlatformMacSpecifics = false; - -bool g_useNaturalScrolling = true; // natural scrolling is default on linux/windows - -static void mouseWheelEvent(emscripten::val event) { - - emscripten::val wheelInterted = event["webkitDirectionInvertedFromDevice"]; - - if (wheelInterted.as()) { - g_useNaturalScrolling = true; - } -} - -EMSCRIPTEN_BINDINGS(qtMouseModule) { - function("qtMouseWheelEvent", &mouseWheelEvent); -} - -QWasmEventTranslator::QWasmEventTranslator(QWasmScreen *screen) - : QObject(screen) - , draggedWindow(nullptr) - , lastWindow(nullptr) - , pressedButtons(Qt::NoButton) - , resizeMode(QWasmWindow::ResizeNone) +QWasmEventTranslator::QWasmEventTranslator() : QObject() { - touchDevice = new QPointingDevice("touchscreen", 1, QInputDevice::DeviceType::TouchScreen, - QPointingDevice::PointerType::Finger, - QPointingDevice::Capability::Position | QPointingDevice::Capability::Area | QPointingDevice::Capability::NormalizedPosition, - 10, 0); - QWindowSystemInterface::registerInputDevice(touchDevice); - - initEventHandlers(); } QWasmEventTranslator::~QWasmEventTranslator() { - // deregister event handlers - QByteArray canvasSelector = "#" + screen()->canvasId().toUtf8(); - emscripten_set_keydown_callback(canvasSelector.constData(), 0, 0, NULL); - emscripten_set_keyup_callback(canvasSelector.constData(), 0, 0, NULL); - - emscripten_set_mousedown_callback(canvasSelector.constData(), 0, 0, NULL); - emscripten_set_mouseup_callback(canvasSelector.constData(), 0, 0, NULL); - emscripten_set_mousemove_callback(canvasSelector.constData(), 0, 0, NULL); - - emscripten_set_focus_callback(canvasSelector.constData(), 0, 0, NULL); - - emscripten_set_wheel_callback(canvasSelector.constData(), 0, 0, NULL); - - emscripten_set_touchstart_callback(canvasSelector.constData(), 0, 0, NULL); - emscripten_set_touchend_callback(canvasSelector.constData(), 0, 0, NULL); - emscripten_set_touchmove_callback(canvasSelector.constData(), 0, 0, NULL); - emscripten_set_touchcancel_callback(canvasSelector.constData(), 0, 0, NULL); -} - -void QWasmEventTranslator::initEventHandlers() -{ - QByteArray canvasSelector = "#" + screen()->canvasId().toUtf8(); - - // The Platform Detect: expand coverage and move as needed - enum Platform { - GenericPlatform, - MacOSPlatform - }; - Platform platform = Platform(emscripten::val::global("navigator")["platform"] - .call("includes", emscripten::val("Mac"))); - g_usePlatformMacSpecifics = (platform == MacOSPlatform); - - if (platform == MacOSPlatform) { - g_useNaturalScrolling = false; // make this !default on macOS - - if (!emscripten::val::global("window")["safari"].isUndefined()) { - val canvas = screen()->canvas(); - canvas.call("addEventListener", - val("wheel"), - val::module_property("qtMouseWheelEvent")); - } - } - - emscripten_set_keydown_callback(canvasSelector.constData(), (void *)this, 1, &keyboard_cb); - emscripten_set_keyup_callback(canvasSelector.constData(), (void *)this, 1, &keyboard_cb); - - emscripten_set_mousedown_callback(canvasSelector.constData(), (void *)this, 1, &mouse_cb); - emscripten_set_mouseup_callback(canvasSelector.constData(), (void *)this, 1, &mouse_cb); - emscripten_set_mousemove_callback(canvasSelector.constData(), (void *)this, 1, &mouse_cb); - - emscripten_set_focus_callback(canvasSelector.constData(), (void *)this, 1, &focus_cb); - - emscripten_set_wheel_callback(canvasSelector.constData(), (void *)this, 1, &wheel_cb); - - emscripten_set_touchstart_callback(canvasSelector.constData(), (void *)this, 1, &touchCallback); - emscripten_set_touchend_callback(canvasSelector.constData(), (void *)this, 1, &touchCallback); - emscripten_set_touchmove_callback(canvasSelector.constData(), (void *)this, 1, &touchCallback); - emscripten_set_touchcancel_callback(canvasSelector.constData(), (void *)this, 1, &touchCallback); } template @@ -294,13 +205,15 @@ QFlags QWasmEventTranslator::translatKeyModifier(const Eve else keyModifier |= Qt::MetaModifier; } + return keyModifier; } -QFlags QWasmEventTranslator::translateKeyboardEventModifier(const EmscriptenKeyboardEvent *keyEvent) +QFlags QWasmEventTranslator::translateKeyboardEventModifier(const EmscriptenKeyboardEvent *event) { - QFlags keyModifier = translatKeyModifier(keyEvent); - if (keyEvent->location == DOM_KEY_LOCATION_NUMPAD) { + QFlags keyModifier = translatKeyModifier(event); + + if (event->location == DOM_KEY_LOCATION_NUMPAD) { keyModifier |= Qt::KeypadModifier; } @@ -312,17 +225,9 @@ QFlags QWasmEventTranslator::translateMouseEventModifier(c return translatKeyModifier(mouseEvent); } -int QWasmEventTranslator::keyboard_cb(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData) +QFlags QWasmEventTranslator::translateTouchEventModifier(const EmscriptenTouchEvent *touchEvent) { - QWasmEventTranslator *wasmTranslator = reinterpret_cast(userData); - bool accepted = wasmTranslator->processKeyboard(eventType, keyEvent); - - return accepted ? 1 : 0; -} - -QWasmScreen *QWasmEventTranslator::screen() -{ - return static_cast(parent()); + return translatKeyModifier(touchEvent); } Qt::Key QWasmEventTranslator::translateEmscriptKey(const EmscriptenKeyboardEvent *emscriptKey) @@ -367,335 +272,6 @@ Qt::MouseButton QWasmEventTranslator::translateMouseButton(unsigned short button return Qt::NoButton; } -int QWasmEventTranslator::mouse_cb(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) -{ - QWasmEventTranslator *translator = (QWasmEventTranslator*)userData; - bool accepted = translator->processMouse(eventType,mouseEvent); - return accepted; -} - -void resizeWindow(QWindow *window, QWasmWindow::ResizeMode mode, - QRect startRect, QPoint amount) -{ - if (mode == QWasmWindow::ResizeNone) - return; - - bool top = mode == QWasmWindow::ResizeTopLeft || - mode == QWasmWindow::ResizeTop || - mode == QWasmWindow::ResizeTopRight; - - bool bottom = mode == QWasmWindow::ResizeBottomLeft || - mode == QWasmWindow::ResizeBottom || - mode == QWasmWindow::ResizeBottomRight; - - bool left = mode == QWasmWindow::ResizeLeft || - mode == QWasmWindow::ResizeTopLeft || - mode == QWasmWindow::ResizeBottomLeft; - - bool right = mode == QWasmWindow::ResizeRight || - mode == QWasmWindow::ResizeTopRight || - mode == QWasmWindow::ResizeBottomRight; - - int x1 = startRect.left(); - int y1 = startRect.top(); - int x2 = startRect.right(); - int y2 = startRect.bottom(); - - if (left) - x1 += amount.x(); - if (top) - y1 += amount.y(); - if (right) - x2 += amount.x(); - if (bottom) - y2 += amount.y(); - - int w = x2-x1; - int h = y2-y1; - - if (w < window->minimumWidth()) { - if (left) - x1 -= window->minimumWidth() - w; - - w = window->minimumWidth(); - } - - if (h < window->minimumHeight()) { - if (top) - y1 -= window->minimumHeight() - h; - - h = window->minimumHeight(); - } - - window->setGeometry(x1, y1, w, h); -} - -bool QWasmEventTranslator::processMouse(int eventType, const EmscriptenMouseEvent *mouseEvent) -{ - QPoint targetPoint(mouseEvent->targetX, mouseEvent->targetY); - QPoint globalPoint = screen()->geometry().topLeft() + targetPoint; - - QEvent::Type buttonEventType = QEvent::None; - Qt::MouseButton button = Qt::NoButton; - Qt::KeyboardModifiers modifiers = translateMouseEventModifier(mouseEvent); - - QWindow *window2 = nullptr; - if (resizeMode == QWasmWindow::ResizeNone) - window2 = screen()->compositor()->windowAt(globalPoint, 5); - - if (window2 == nullptr) { - window2 = lastWindow; - } else { - lastWindow = window2; - } - - QPoint localPoint = window2->mapFromGlobal(globalPoint); - bool interior = window2->geometry().contains(globalPoint); - - QWasmWindow *htmlWindow = static_cast(window2->handle()); - switch (eventType) { - case EMSCRIPTEN_EVENT_MOUSEDOWN: - { - button = translateMouseButton(mouseEvent->button); - - if (window2) - window2->requestActivate(); - - pressedButtons.setFlag(button); - - pressedWindow = window2; - buttonEventType = QEvent::MouseButtonPress; - - // button overview: - // 0 = primary mouse button, usually left click - // 1 = middle mouse button, usually mouse wheel - // 2 = right mouse button, usually right click - // from: https://w3c.github.io/uievents/#dom-mouseevent-button - if (mouseEvent->button == 0) { - if (!(htmlWindow->m_windowState & Qt::WindowFullScreen) && !(htmlWindow->m_windowState & Qt::WindowMaximized)) { - if (htmlWindow && window2->flags().testFlag(Qt::WindowTitleHint) && htmlWindow->isPointOnTitle(globalPoint)) - draggedWindow = window2; - else if (htmlWindow && htmlWindow->isPointOnResizeRegion(globalPoint)) { - draggedWindow = window2; - resizeMode = htmlWindow->resizeModeAtPoint(globalPoint); - resizePoint = globalPoint; - resizeStartRect = window2->geometry(); - } - } - } - - htmlWindow->injectMousePressed(localPoint, globalPoint, button, modifiers); - break; - } - case EMSCRIPTEN_EVENT_MOUSEUP: - { - button = translateMouseButton(mouseEvent->button); - pressedButtons.setFlag(button, false); - buttonEventType = QEvent::MouseButtonRelease; - QWasmWindow *oldWindow = nullptr; - - if (mouseEvent->button == 0 && pressedWindow) { - oldWindow = static_cast(pressedWindow->handle()); - pressedWindow = nullptr; - } - - if (draggedWindow && pressedButtons.testFlag(Qt::NoButton)) { - draggedWindow = nullptr; - resizeMode = QWasmWindow::ResizeNone; - } - - if (oldWindow) - oldWindow->injectMouseReleased(localPoint, globalPoint, button, modifiers); - else - htmlWindow->injectMouseReleased(localPoint, globalPoint, button, modifiers); - break; - } - case EMSCRIPTEN_EVENT_MOUSEMOVE: // drag event - { - buttonEventType = QEvent::MouseMove; - - if (htmlWindow && pressedButtons.testFlag(Qt::NoButton)) { - - if (htmlWindow->isPointOnResizeRegion(globalPoint)) { - QCursor resizingCursor = cursorForMode(htmlWindow->resizeModeAtPoint(globalPoint)); - - if (resizingCursor != window2->cursor()) { - isCursorOverridden = true; - QWasmCursor::setOverrideWasmCursor(&resizingCursor, window2->screen()); - } - } else { // off resizing area - if (isCursorOverridden) { - isCursorOverridden = false; - QWasmCursor::clearOverrideWasmCursor(window2->screen()); - } - } - } - - if (!(htmlWindow->m_windowState & Qt::WindowFullScreen) && !(htmlWindow->m_windowState & Qt::WindowMaximized)) { - if (resizeMode == QWasmWindow::ResizeNone && draggedWindow) { - draggedWindow->setX(draggedWindow->x() + mouseEvent->movementX); - draggedWindow->setY(draggedWindow->y() + mouseEvent->movementY); - } - - if (resizeMode != QWasmWindow::ResizeNone && !(htmlWindow->m_windowState & Qt::WindowFullScreen)) { - QPoint delta = QPoint(mouseEvent->targetX, mouseEvent->targetY) - resizePoint; - resizeWindow(draggedWindow, resizeMode, resizeStartRect, delta); - } - } - break; - } - default: // MOUSELEAVE MOUSEENTER - break; - }; - if (!window2 && buttonEventType == QEvent::MouseButtonRelease) { - window2 = lastWindow; - lastWindow = nullptr; - interior = true; - } - bool accepted = true; - if (window2 && interior) { - accepted = QWindowSystemInterface::handleMouseEvent( - window2, getTimestamp(), localPoint, globalPoint, pressedButtons, button, buttonEventType, modifiers); - } - return accepted; -} - -int QWasmEventTranslator::focus_cb(int /*eventType*/, const EmscriptenFocusEvent */*focusEvent*/, void */*userData*/) -{ - return 0; -} - -int QWasmEventTranslator::wheel_cb(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData) -{ - Q_UNUSED(eventType); - - QWasmEventTranslator *eventTranslator = static_cast(userData); - EmscriptenMouseEvent mouseEvent = wheelEvent->mouse; - - int scrollFactor = 0; - switch (wheelEvent->deltaMode) { - case DOM_DELTA_PIXEL://chrome safari - scrollFactor = 1; - break; - case DOM_DELTA_LINE: //firefox - scrollFactor = 12; - break; - case DOM_DELTA_PAGE: - scrollFactor = 20; - break; - }; - - if (g_useNaturalScrolling) //macOS platform has document oriented scrolling - scrollFactor = -scrollFactor; - - QWasmEventTranslator *translator = (QWasmEventTranslator*)userData; - Qt::KeyboardModifiers modifiers = translator->translateMouseEventModifier(&mouseEvent); - QPoint targetPoint(mouseEvent.targetX, mouseEvent.targetY); - QPoint globalPoint = eventTranslator->screen()->geometry().topLeft() + targetPoint; - - QWindow *window2 = eventTranslator->screen()->compositor()->windowAt(globalPoint, 5); - if (!window2) - return 0; - QPoint localPoint = window2->mapFromGlobal(globalPoint); - - QPoint pixelDelta; - - if (wheelEvent->deltaY != 0) pixelDelta.setY(wheelEvent->deltaY * scrollFactor); - if (wheelEvent->deltaX != 0) pixelDelta.setX(wheelEvent->deltaX * scrollFactor); - - bool accepted = QWindowSystemInterface::handleWheelEvent(window2, getTimestamp(), localPoint, - globalPoint, QPoint(), pixelDelta, modifiers); - return static_cast(accepted); -} - -int QWasmEventTranslator::touchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData) -{ - auto translator = reinterpret_cast(userData); - return translator->handleTouch(eventType, touchEvent); -} - -int QWasmEventTranslator::handleTouch(int eventType, const EmscriptenTouchEvent *touchEvent) -{ - QList touchPointList; - touchPointList.reserve(touchEvent->numTouches); - QWindow *window2; - - for (int i = 0; i < touchEvent->numTouches; i++) { - - const EmscriptenTouchPoint *touches = &touchEvent->touches[i]; - - QPoint targetPoint(touches->targetX, touches->targetY); - QPoint globalPoint = screen()->geometry().topLeft() + targetPoint; - - window2 = this->screen()->compositor()->windowAt(globalPoint, 5); - if (window2 == nullptr) - continue; - - QWindowSystemInterface::TouchPoint touchPoint; - - touchPoint.area = QRect(0, 0, 8, 8); - touchPoint.id = touches->identifier; - touchPoint.pressure = 1.0; - - touchPoint.area.moveCenter(globalPoint); - - const auto tp = pressedTouchIds.constFind(touchPoint.id); - if (tp != pressedTouchIds.constEnd()) - touchPoint.normalPosition = tp.value(); - - QPointF localPoint = QPointF(window2->mapFromGlobal(globalPoint)); - QPointF normalPosition(localPoint.x() / window2->width(), - localPoint.y() / window2->height()); - - const bool stationaryTouchPoint = (normalPosition == touchPoint.normalPosition); - touchPoint.normalPosition = normalPosition; - - switch (eventType) { - case EMSCRIPTEN_EVENT_TOUCHSTART: - if (tp != pressedTouchIds.constEnd()) { - touchPoint.state = (stationaryTouchPoint - ? QEventPoint::State::Stationary - : QEventPoint::State::Updated); - } else { - touchPoint.state = QEventPoint::State::Pressed; - } - pressedTouchIds.insert(touchPoint.id, touchPoint.normalPosition); - - break; - case EMSCRIPTEN_EVENT_TOUCHEND: - touchPoint.state = QEventPoint::State::Released; - pressedTouchIds.remove(touchPoint.id); - break; - case EMSCRIPTEN_EVENT_TOUCHMOVE: - touchPoint.state = (stationaryTouchPoint - ? QEventPoint::State::Stationary - : QEventPoint::State::Updated); - - pressedTouchIds.insert(touchPoint.id, touchPoint.normalPosition); - break; - default: - break; - } - - touchPointList.append(touchPoint); - } - - QFlags keyModifier = translatKeyModifier(touchEvent); - - bool accepted = QWindowSystemInterface::handleTouchEvent( - window2, getTimestamp(), touchDevice, touchPointList, keyModifier); - - if (eventType == EMSCRIPTEN_EVENT_TOUCHCANCEL) - accepted = QWindowSystemInterface::handleTouchCancelEvent(window2, getTimestamp(), touchDevice, keyModifier); - - return static_cast(accepted); -} - -quint64 QWasmEventTranslator::getTimestamp() -{ - return emscripten_performance_now(); -} - struct KeyMapping { Qt::Key from, to; }; constexpr KeyMapping tildeKeyTable[] = { // ~ @@ -750,12 +326,12 @@ static Qt::Key find(const KeyMapping (&map)[N], Qt::Key key) noexcept return find_impl(map, map + N, key); } -Qt::Key QWasmEventTranslator::translateDeadKey(Qt::Key deadKey, Qt::Key accentBaseKey) +Qt::Key QWasmEventTranslator::translateDeadKey(Qt::Key deadKey, Qt::Key accentBaseKey, bool is_mac) { Qt::Key wasmKey = Qt::Key_unknown; if (deadKey == Qt::Key_QuoteLeft ) { - if (g_usePlatformMacSpecifics) { // ` macOS: Key_Dead_Grave + if (is_mac) { // ` macOS: Key_Dead_Grave wasmKey = find(graveKeyTable, accentBaseKey); } else { wasmKey = find(diaeresisKeyTable, accentBaseKey); @@ -790,121 +366,83 @@ Qt::Key QWasmEventTranslator::translateDeadKey(Qt::Key deadKey, Qt::Key accentBa return wasmKey; } -bool QWasmEventTranslator::processKeyboard(int eventType, const EmscriptenKeyboardEvent *keyEvent) -{ - Qt::Key qtKey = translateEmscriptKey(keyEvent); - - Qt::KeyboardModifiers modifiers = translateKeyboardEventModifier(keyEvent); - - QString keyText; - QEvent::Type keyType = QEvent::None; - switch (eventType) { - case EMSCRIPTEN_EVENT_KEYPRESS: - case EMSCRIPTEN_EVENT_KEYDOWN: // down - keyType = QEvent::KeyPress; - - if (m_emDeadKey != Qt::Key_unknown) { - - Qt::Key transformedKey = translateDeadKey(m_emDeadKey, qtKey); - - if (transformedKey != Qt::Key_unknown) - qtKey = transformedKey; - - if (keyEvent->shiftKey == 0) { - for (auto it = KeyTbl.cbegin(); it != KeyTbl.end(); ++it) { - if (it != KeyTbl.end() && (qtKey == static_cast(it->qt))) { - keyText = it->em; - m_emDeadKey = Qt::Key_unknown; - break; - } - } - } else { - for (auto it = DeadKeyShiftTbl.cbegin(); it != DeadKeyShiftTbl.end(); ++it) { - if (it != DeadKeyShiftTbl.end() && (qtKey == static_cast(it->qt))) { - keyText = it->em; - m_emDeadKey = Qt::Key_unknown; - break; - } - } - } - } - if (qstrncmp(keyEvent->key, "Dead", 4) == 0 || qtKey == Qt::Key_AltGr) { - qtKey = translateEmscriptKey(keyEvent); - m_emStickyDeadKey = true; - if (keyEvent->shiftKey == 1 && qtKey == Qt::Key_QuoteLeft) - qtKey = Qt::Key_AsciiTilde; - m_emDeadKey = qtKey; - } - break; - case EMSCRIPTEN_EVENT_KEYUP: // up - keyType = QEvent::KeyRelease; - if (m_emStickyDeadKey && qtKey != Qt::Key_Alt) { - m_emStickyDeadKey = false; - } - break; - default: - break; - }; - - if (keyType == QEvent::None) - return 0; - - QFlags mods = translateKeyboardEventModifier(keyEvent); - - // Clipboard fallback path: cut/copy/paste are handled by clipboard event - // handlers if direct clipboard access is not available. - if (!QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi && modifiers & Qt::ControlModifier && - (qtKey == Qt::Key_X || qtKey == Qt::Key_C || qtKey == Qt::Key_V)) { - if (qtKey == Qt::Key_V) { - QWasmIntegration::get()->getWasmClipboard()->isPaste = true; - } - return false; - } - - bool accepted = false; - - if (keyType == QEvent::KeyPress && - mods.testFlag(Qt::ControlModifier) - && qtKey == Qt::Key_V) { - QWasmIntegration::get()->getWasmClipboard()->isPaste = true; - accepted = false; // continue on to event - } else { - if (keyText.isEmpty()) - keyText = QString(keyEvent->key); - if (keyText.size() > 1) - keyText.clear(); - accepted = QWindowSystemInterface::handleKeyEvent( - 0, keyType, qtKey, modifiers, keyText); - } - if (keyType == QEvent::KeyPress && - mods.testFlag(Qt::ControlModifier) - && qtKey == Qt::Key_C) { - QWasmIntegration::get()->getWasmClipboard()->isPaste = false; - accepted = false; // continue on to event - } - - return accepted; -} - -QCursor QWasmEventTranslator::cursorForMode(QWasmWindow::ResizeMode m) +QCursor QWasmEventTranslator::cursorForMode(QWasmCompositor::ResizeMode m) { switch (m) { - case QWasmWindow::ResizeTopLeft: - case QWasmWindow::ResizeBottomRight: + case QWasmCompositor::ResizeTopLeft: + case QWasmCompositor::ResizeBottomRight: return Qt::SizeFDiagCursor; - case QWasmWindow::ResizeBottomLeft: - case QWasmWindow::ResizeTopRight: + case QWasmCompositor::ResizeBottomLeft: + case QWasmCompositor::ResizeTopRight: return Qt::SizeBDiagCursor; - case QWasmWindow::ResizeTop: - case QWasmWindow::ResizeBottom: + case QWasmCompositor::ResizeTop: + case QWasmCompositor::ResizeBottom: return Qt::SizeVerCursor; - case QWasmWindow::ResizeLeft: - case QWasmWindow::ResizeRight: + case QWasmCompositor::ResizeLeft: + case QWasmCompositor::ResizeRight: return Qt::SizeHorCursor; - case QWasmWindow::ResizeNone: + case QWasmCompositor::ResizeNone: return Qt::ArrowCursor; } return Qt::ArrowCursor; } +QString QWasmEventTranslator::getKeyText(const EmscriptenKeyboardEvent *keyEvent) +{ + QString keyText; + Qt::Key qtKey = translateEmscriptKey(keyEvent); + //Qt::KeyboardModifiers modifiers = translateKeyboardEventModifier(keyEvent); + + if (m_emDeadKey != Qt::Key_unknown) { + Qt::Key transformedKey = translateDeadKey(m_emDeadKey, qtKey); + + if (transformedKey != Qt::Key_unknown) + qtKey = transformedKey; + + if (keyEvent->shiftKey == 0) { + for (auto it = KeyTbl.cbegin(); it != KeyTbl.end(); ++it) { + if (it != KeyTbl.end() && (qtKey == static_cast(it->qt))) { + keyText = it->em; + m_emDeadKey = Qt::Key_unknown; + break; + } + } + } else { + for (auto it = DeadKeyShiftTbl.cbegin(); it != DeadKeyShiftTbl.end(); ++it) { + if (it != DeadKeyShiftTbl.end() && (qtKey == static_cast(it->qt))) { + keyText = it->em; + m_emDeadKey = Qt::Key_unknown; + break; + } + } + } + } + + return keyText; +} + +Qt::Key QWasmEventTranslator::getKey(const EmscriptenKeyboardEvent *keyEvent) +{ + Qt::Key qtKey = translateEmscriptKey(keyEvent); + + if (qstrncmp(keyEvent->key, "Dead", 4) == 0 || qtKey == Qt::Key_AltGr) { + qtKey = translateEmscriptKey(keyEvent); + m_emStickyDeadKey = true; + if (keyEvent->shiftKey == 1 && qtKey == Qt::Key_QuoteLeft) + qtKey = Qt::Key_AsciiTilde; + m_emDeadKey = qtKey; + } + + return qtKey; +} + +void QWasmEventTranslator::setStickyDeadKey(const EmscriptenKeyboardEvent *keyEvent) +{ + Qt::Key qtKey = translateEmscriptKey(keyEvent); + + if (m_emStickyDeadKey && qtKey != Qt::Key_Alt) { + m_emStickyDeadKey = false; + } +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmeventtranslator.h b/src/plugins/platforms/wasm/qwasmeventtranslator.h index 0b579691c5..341971d79f 100644 --- a/src/plugins/platforms/wasm/qwasmeventtranslator.h +++ b/src/plugins/platforms/wasm/qwasmeventtranslator.h @@ -49,59 +49,37 @@ class QWasmEventTranslator : public QObject public: - explicit QWasmEventTranslator(QWasmScreen *screen); + explicit QWasmEventTranslator(); ~QWasmEventTranslator(); - static int keyboard_cb(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData); - static int mouse_cb(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData); - static int focus_cb(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData); - static int wheel_cb(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData); + template + QFlags translatKeyModifier(const Event *event); - static int touchCallback(int eventType, const EmscriptenTouchEvent *ev, void *userData); + static Qt::Key translateEmscriptKey(const EmscriptenKeyboardEvent *emscriptKey); + QFlags translateKeyboardEventModifier(const EmscriptenKeyboardEvent *keyEvent); + QFlags translateMouseEventModifier(const EmscriptenMouseEvent *mouseEvent); + QFlags translateTouchEventModifier(const EmscriptenTouchEvent *touchEvent); + static Qt::MouseButton translateMouseButton(unsigned short button); + static QCursor cursorForMode(QWasmCompositor::ResizeMode mode); - void processEvents(); - void initEventHandlers(); - int handleTouch(int eventType, const EmscriptenTouchEvent *touchEvent); + QString getKeyText(const EmscriptenKeyboardEvent *keyEvent); + Qt::Key getKey(const EmscriptenKeyboardEvent *keyEvent); + void setStickyDeadKey(const EmscriptenKeyboardEvent *keyEvent); + + void setIsMac(bool is_mac) {g_usePlatformMacSpecifics = is_mac;}; Q_SIGNALS: void getWindowAt(const QPoint &point, QWindow **window); private: - QWasmScreen *screen(); - Qt::Key translateEmscriptKey(const EmscriptenKeyboardEvent *emscriptKey); - template - QFlags translatKeyModifier(const Event *event); - QFlags translateKeyboardEventModifier(const EmscriptenKeyboardEvent *keyEvent); - QFlags translateMouseEventModifier(const EmscriptenMouseEvent *mouseEvent); - Qt::MouseButton translateMouseButton(unsigned short button); - - bool processMouse(int eventType, const EmscriptenMouseEvent *mouseEvent); - bool processKeyboard(int eventType, const EmscriptenKeyboardEvent *keyEvent); - - Qt::Key translateDeadKey(Qt::Key deadKey, Qt::Key accentBaseKey); - - QMap pressedTouchIds; + bool g_usePlatformMacSpecifics = false; + static Qt::Key translateDeadKey(Qt::Key deadKey, Qt::Key accentBaseKey, bool is_mac = false); private: - QPointer draggedWindow; - QPointer pressedWindow; - QPointer lastWindow; - Qt::MouseButtons pressedButtons; - - QWasmWindow::ResizeMode resizeMode; - QPoint resizePoint; - QRect resizeStartRect; -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - QPointingDevice *touchDevice; -#else - QTouchDevice *touchDevice; -#endif static quint64 getTimestamp(); Qt::Key m_emDeadKey = Qt::Key_unknown; bool m_emStickyDeadKey = false; - QCursor cursorForMode(QWasmWindow::ResizeMode mode); - QCursor overriddenCursor; - bool isCursorOverridden = false; + }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmintegration.cpp b/src/plugins/platforms/wasm/qwasmintegration.cpp index 3dfd8cfe12..031128563e 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.cpp +++ b/src/plugins/platforms/wasm/qwasmintegration.cpp @@ -328,4 +328,9 @@ void QWasmIntegration::resizeAllScreens() canvasAndScreen.second->updateQScreenAndCanvasRenderSize(); } +quint64 QWasmIntegration::getTimestamp() +{ + return emscripten_performance_now(); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmintegration.h b/src/plugins/platforms/wasm/qwasmintegration.h index 16c48e1ea1..46fab8e818 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.h +++ b/src/plugins/platforms/wasm/qwasmintegration.h @@ -89,6 +89,7 @@ public: void resizeAllScreens(); void updateDpi(); void removeBackingStore(QWindow* window); + static quint64 getTimestamp(); private: mutable QWasmFontDatabase *m_fontDb; diff --git a/src/plugins/platforms/wasm/qwasmscreen.cpp b/src/plugins/platforms/wasm/qwasmscreen.cpp index 3cc067bbe7..eb77dc65b9 100644 --- a/src/plugins/platforms/wasm/qwasmscreen.cpp +++ b/src/plugins/platforms/wasm/qwasmscreen.cpp @@ -55,7 +55,7 @@ const char * QWasmScreen::m_canvasResizeObserverCallbackContextPropertyName = "d QWasmScreen::QWasmScreen(const emscripten::val &canvas) : m_canvas(canvas) , m_compositor(new QWasmCompositor(this)) - , m_eventTranslator(new QWasmEventTranslator(this)) + , m_eventTranslator(new QWasmEventTranslator()) { updateQScreenAndCanvasRenderSize(); m_canvas.call("focus"); diff --git a/src/plugins/platforms/wasm/qwasmwindow.cpp b/src/plugins/platforms/wasm/qwasmwindow.cpp index 3819e8daa6..a25b5a262c 100644 --- a/src/plugins/platforms/wasm/qwasmwindow.cpp +++ b/src/plugins/platforms/wasm/qwasmwindow.cpp @@ -274,7 +274,7 @@ bool QWasmWindow::isPointOnResizeRegion(QPoint point) const return resizeRegion().contains(point); } -QWasmWindow::ResizeMode QWasmWindow::resizeModeAtPoint(QPoint point) const +QWasmCompositor::ResizeMode QWasmWindow::resizeModeAtPoint(QPoint point) const { QPoint p1 = window()->frameGeometry().topLeft() - QPoint(5, 5); QPoint p2 = window()->frameGeometry().bottomRight() + QPoint(5, 5); @@ -291,28 +291,28 @@ QWasmWindow::ResizeMode QWasmWindow::resizeModeAtPoint(QPoint point) const if (top.contains(point)) { // Top if (left.contains(point)) - return ResizeTopLeft; + return QWasmCompositor::ResizeTopLeft; if (center.contains(point)) - return ResizeTop; + return QWasmCompositor::ResizeTop; if (right.contains(point)) - return ResizeTopRight; + return QWasmCompositor::ResizeTopRight; } else if (middle.contains(point)) { // Middle if (left.contains(point)) - return ResizeLeft; + return QWasmCompositor::ResizeLeft; if (right.contains(point)) - return ResizeRight; + return QWasmCompositor::ResizeRight; } else if (bottom.contains(point)) { // Bottom if (left.contains(point)) - return ResizeBottomLeft; + return QWasmCompositor::ResizeBottomLeft; if (center.contains(point)) - return ResizeBottom; + return QWasmCompositor::ResizeBottom; if (right.contains(point)) - return ResizeBottomRight; + return QWasmCompositor::ResizeBottomRight; } - return ResizeNone; + return QWasmCompositor::ResizeNone; } QRect getSubControlRect(const QWasmWindow *window, QWasmCompositor::SubControls subControl) diff --git a/src/plugins/platforms/wasm/qwasmwindow.h b/src/plugins/platforms/wasm/qwasmwindow.h index 5df6d9dc66..686a26430e 100644 --- a/src/plugins/platforms/wasm/qwasmwindow.h +++ b/src/plugins/platforms/wasm/qwasmwindow.h @@ -39,23 +39,9 @@ QT_BEGIN_NAMESPACE -class QWasmCompositor; - class QWasmWindow : public QPlatformWindow { public: - enum ResizeMode { - ResizeNone, - ResizeTopLeft, - ResizeTop, - ResizeTopRight, - ResizeRight, - ResizeBottomRight, - ResizeBottom, - ResizeBottomLeft, - ResizeLeft - }; - QWasmWindow(QWindow *w, QWasmCompositor *compositor, QWasmBackingStore *backingStore); ~QWasmWindow(); void destroy(); @@ -92,7 +78,7 @@ public: QRegion resizeRegion() const; bool isPointOnTitle(QPoint point) const; bool isPointOnResizeRegion(QPoint point) const; - ResizeMode resizeModeAtPoint(QPoint point) const; + QWasmCompositor::ResizeMode resizeModeAtPoint(QPoint point) const; QRect maxButtonRect() const; QRect minButtonRect() const; QRect closeButtonRect() const;