diff --git a/src/plugins/platforms/wasm/CMakeLists.txt b/src/plugins/platforms/wasm/CMakeLists.txt index 113dad18c6..b29c59c4e7 100644 --- a/src/plugins/platforms/wasm/CMakeLists.txt +++ b/src/plugins/platforms/wasm/CMakeLists.txt @@ -15,12 +15,14 @@ qt_internal_add_plugin(QWasmIntegrationPlugin qwasmclipboard.cpp qwasmclipboard.h qwasmcompositor.cpp qwasmcompositor.h qwasmcursor.cpp qwasmcursor.h + qwasmevent.cpp qwasmevent.h qwasmeventdispatcher.cpp qwasmeventdispatcher.h qwasmeventtranslator.cpp qwasmeventtranslator.h qwasmfontdatabase.cpp qwasmfontdatabase.h qwasmintegration.cpp qwasmintegration.h qwasmoffscreensurface.cpp qwasmoffscreensurface.h qwasmopenglcontext.cpp qwasmopenglcontext.h + qwasmplatform.cpp qwasmplatform.h qwasmscreen.cpp qwasmscreen.h qwasmservices.cpp qwasmservices.h qwasmstring.cpp qwasmstring.h diff --git a/src/plugins/platforms/wasm/qwasmcompositor.cpp b/src/plugins/platforms/wasm/qwasmcompositor.cpp index 45e9eeeedd..85c2783439 100644 --- a/src/plugins/platforms/wasm/qwasmcompositor.cpp +++ b/src/plugins/platforms/wasm/qwasmcompositor.cpp @@ -7,6 +7,7 @@ #include "qwasmeventtranslator.h" #include "qwasmeventdispatcher.h" #include "qwasmclipboard.h" +#include "qwasmevent.h" #include @@ -58,6 +59,7 @@ EMSCRIPTEN_BINDINGS(qtMouseModule) { QWasmCompositor::QWasmCompositor(QWasmScreen *screen) : QObject(screen) + , m_windowManipulation(screen) , m_blitter(new QOpenGLTextureBlitter) , m_eventTranslator(std::make_unique()) { @@ -87,12 +89,6 @@ void QWasmCompositor::deregisterEventHandlers() 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_mouseenter_callback(canvasSelector.constData(), 0, 0, NULL); - emscripten_set_mouseleave_callback(canvasSelector.constData(), 0, 0, NULL); - emscripten_set_focus_callback(canvasSelector.constData(), 0, 0, NULL); emscripten_set_wheel_callback(canvasSelector.constData(), 0, 0, NULL); @@ -133,9 +129,7 @@ void QWasmCompositor::initEventHandlers() { QByteArray canvasSelector = screen()->canvasTargetId().toUtf8(); - m_eventTranslator->g_usePlatformMacSpecifics - = (QWasmIntegration::get()->platform == QWasmIntegration::MacOSPlatform); - if (QWasmIntegration::get()->platform == QWasmIntegration::MacOSPlatform) { + if (platform() == Platform::MacOS) { if (!emscripten::val::global("window")["safari"].isUndefined()) { val canvas = screen()->canvas(); canvas.call("addEventListener", @@ -149,11 +143,17 @@ void QWasmCompositor::initEventHandlers() emscripten_set_keydown_callback(canvasSelector.constData(), (void *)this, UseCapture, &keyboard_cb); emscripten_set_keyup_callback(canvasSelector.constData(), (void *)this, UseCapture, &keyboard_cb); - emscripten_set_mousedown_callback(canvasSelector.constData(), (void *)this, UseCapture, &mouse_cb); - emscripten_set_mouseup_callback(canvasSelector.constData(), (void *)this, UseCapture, &mouse_cb); - emscripten_set_mousemove_callback(canvasSelector.constData(), (void *)this, UseCapture, &mouse_cb); - emscripten_set_mouseenter_callback(canvasSelector.constData(), (void *)this, UseCapture, &mouse_cb); - emscripten_set_mouseleave_callback(canvasSelector.constData(), (void *)this, UseCapture, &mouse_cb); + val canvas = screen()->canvas(); + const auto callback = std::function([this](emscripten::val event) { + if (processPointer(*PointerEvent::fromWeb(event))) + event.call("preventDefault"); + }); + + m_pointerDownCallback = std::make_unique(canvas, "pointerdown", callback); + m_pointerMoveCallback = std::make_unique(canvas, "pointermove", callback); + m_pointerUpCallback = std::make_unique(canvas, "pointerup", callback); + m_pointerEnterCallback = std::make_unique(canvas, "pointerenter", callback); + m_pointerLeaveCallback = std::make_unique(canvas, "pointerleave", callback); emscripten_set_focus_callback(canvasSelector.constData(), (void *)this, UseCapture, &focus_cb); @@ -164,7 +164,6 @@ void QWasmCompositor::initEventHandlers() emscripten_set_touchmove_callback(canvasSelector.constData(), (void *)this, UseCapture, &touchCallback); emscripten_set_touchcancel_callback(canvasSelector.constData(), (void *)this, UseCapture, &touchCallback); - val canvas = screen()->canvas(); canvas.call("addEventListener", std::string("drop"), val::module_property("qtDrop"), val(true)); @@ -859,60 +858,27 @@ void QWasmCompositor::frame() m_context->swapBuffers(someWindow->window()); } -void QWasmCompositor::resizeWindow(QWindow *window, QWasmCompositor::ResizeMode mode, - QRect startRect, QPoint amount) +void QWasmCompositor::WindowManipulation::resizeWindow(const QPoint& amount) { - if (mode == QWasmCompositor::ResizeNone) - return; + const auto& minShrink = std::get(m_state->operationSpecific).m_minShrink; + const auto& maxGrow = std::get(m_state->operationSpecific).m_maxGrow; + const auto& resizeMode = std::get(m_state->operationSpecific).m_resizeMode; - bool top = mode == QWasmCompositor::ResizeTopLeft || - mode == QWasmCompositor::ResizeTop || - mode == QWasmCompositor::ResizeTopRight; + const QPoint cappedGrowVector( + std::min(maxGrow.x(), std::max(minShrink.x(), + (resizeMode & Left) ? -amount.x() : (resizeMode & Right) ? amount.x() : 0)), + std::min(maxGrow.y(), std::max(minShrink.y(), + (resizeMode & Top) ? -amount.y() : (resizeMode & Bottom) ? amount.y() : 0))); - 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); + const auto& initialBounds = + std::get(m_state->operationSpecific).m_initialWindowBounds; + m_state->window->setGeometry( + initialBounds.adjusted( + (resizeMode & Left) ? -cappedGrowVector.x() : 0, + (resizeMode & Top) ? -cappedGrowVector.y() : 0, + (resizeMode & Right) ? cappedGrowVector.x() : 0, + (resizeMode & Bottom) ? cappedGrowVector.y() : 0 + )); } void QWasmCompositor::notifyTopWindowChanged(QWasmWindow *window) @@ -945,12 +911,6 @@ int QWasmCompositor::keyboard_cb(int eventType, const EmscriptenKeyboardEvent *k return static_cast(wasmCompositor->processKeyboard(eventType, keyEvent)); } -int QWasmCompositor::mouse_cb(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) -{ - QWasmCompositor *compositor = (QWasmCompositor*)userData; - return static_cast(compositor->processMouse(eventType, mouseEvent)); -} - int QWasmCompositor::focus_cb(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData) { Q_UNUSED(eventType) @@ -972,19 +932,18 @@ int QWasmCompositor::touchCallback(int eventType, const EmscriptenTouchEvent *to return static_cast(compositor->handleTouch(eventType, touchEvent)); } -bool QWasmCompositor::processMouse(int eventType, const EmscriptenMouseEvent *mouseEvent) +bool QWasmCompositor::processPointer(const PointerEvent& event) { - const Qt::MouseButton button = QWasmEventTranslator::translateMouseButton(mouseEvent->button); + if (event.pointerType != PointerType::Mouse) + return false; - const QPoint targetPointInCanvasCoords(mouseEvent->targetX, mouseEvent->targetY); - const QPoint targetPointInScreenCoords = screen()->geometry().topLeft() + targetPointInCanvasCoords; + const QPoint targetPointInScreenCoords = screen()->geometry().topLeft() + event.point; QEvent::Type buttonEventType = QEvent::None; - Qt::KeyboardModifiers modifiers = m_eventTranslator->translateMouseEventModifier(mouseEvent); QWindow *const targetWindow = ([this, &targetPointInScreenCoords]() -> QWindow * { auto *targetWindow = - m_resizeMode == QWasmCompositor::ResizeNone ? + m_windowManipulation.operation() == WindowManipulation::Operation::None ? screen()->compositor()->windowAt(targetPointInScreenCoords, 5) : nullptr; return targetWindow ? targetWindow : m_lastMouseTargetWindow.get(); @@ -1006,57 +965,41 @@ bool QWasmCompositor::processMouse(int eventType, const EmscriptenMouseEvent *mo Qt::WindowStates windowState = targetWindow->windowState(); const bool isTargetWindowResizable = !windowState.testFlag(Qt::WindowMaximized) && !windowState.testFlag(Qt::WindowFullScreen); - switch (eventType) { - case EMSCRIPTEN_EVENT_MOUSEDOWN: + switch (event.type) { + case EventType::PointerDown: { buttonEventType = QEvent::MouseButtonPress; - m_pressedButtons.setFlag(button); - if (targetWindow) targetWindow->requestActivate(); m_pressedWindow = targetWindow; - if (isTargetWindowResizable && button == Qt::MouseButton::LeftButton && !isTargetWindowBlocked) { - if (wasmTargetWindow->isPointOnTitle(targetPointInScreenCoords)) { - m_windowBeingManipulated = targetWindow; - } else if (wasmTargetWindow->isPointOnResizeRegion(targetPointInScreenCoords)) { - m_windowBeingManipulated = targetWindow; - m_resizeMode = wasmTargetWindow->resizeModeAtPoint(targetPointInScreenCoords); - m_resizePoint = targetPointInScreenCoords; - m_resizeStartRect = targetWindow->geometry(); - } - } + m_windowManipulation.onPointerDown(event, targetWindow); - wasmTargetWindow->injectMousePressed(pointInTargetWindowCoords, targetPointInScreenCoords, button, modifiers); + wasmTargetWindow->injectMousePressed(pointInTargetWindowCoords, targetPointInScreenCoords, event.mouseButton, event.modifiers); break; } - case EMSCRIPTEN_EVENT_MOUSEUP: + case EventType::PointerUp: { buttonEventType = QEvent::MouseButtonRelease; - m_pressedButtons.setFlag(button, false); - - if (m_windowBeingManipulated && m_pressedButtons.testFlag(Qt::NoButton)) { - m_windowBeingManipulated = nullptr; - m_resizeMode = QWasmCompositor::ResizeNone; - } + m_windowManipulation.onPointerUp(event); if (m_pressedWindow) { // Always deliver the released event to the same window that was pressed - AsWasmWindow(m_pressedWindow)->injectMouseReleased(pointInTargetWindowCoords, targetPointInScreenCoords, button, modifiers); - if (button == Qt::MouseButton::LeftButton) + AsWasmWindow(m_pressedWindow)->injectMouseReleased(pointInTargetWindowCoords, targetPointInScreenCoords, event.mouseButton, event.modifiers); + if (event.mouseButton == Qt::MouseButton::LeftButton) m_pressedWindow = nullptr; } else { - wasmTargetWindow->injectMouseReleased(pointInTargetWindowCoords, targetPointInScreenCoords, button, modifiers); + wasmTargetWindow->injectMouseReleased(pointInTargetWindowCoords, targetPointInScreenCoords, event.mouseButton, event.modifiers); } break; } - case EMSCRIPTEN_EVENT_MOUSEMOVE: + case EventType::PointerMove: { buttonEventType = QEvent::MouseMove; - if (wasmTargetWindow && m_pressedButtons.testFlag(Qt::NoButton)) { + if (wasmTargetWindow && event.mouseButtons.testFlag(Qt::NoButton)) { const bool isOnResizeRegion = wasmTargetWindow->isPointOnResizeRegion(targetPointInScreenCoords); if (isTargetWindowResizable && isOnResizeRegion && !isTargetWindowBlocked) { @@ -1073,34 +1016,26 @@ bool QWasmCompositor::processMouse(int eventType, const EmscriptenMouseEvent *mo } } - if (m_windowBeingManipulated) { - if (m_resizeMode == QWasmCompositor::ResizeNone) { - m_windowBeingManipulated->setPosition( - m_windowBeingManipulated->position() + QPoint(mouseEvent->movementX, mouseEvent->movementY)); - } else { - const QPoint delta = targetPointInCanvasCoords - m_resizePoint; - resizeWindow(m_windowBeingManipulated, m_resizeMode, m_resizeStartRect, delta); - } - } + m_windowManipulation.onPointerMove(event); break; } - case EMSCRIPTEN_EVENT_MOUSEENTER: - processMouseEnter(mouseEvent); + case EventType::PointerEnter: + processMouseEnter(nullptr); break; - case EMSCRIPTEN_EVENT_MOUSELEAVE: + case EventType::PointerLeave: processMouseLeave(); break; default: break; }; - if (!pointerIsWithinTargetWindowBounds && m_pressedButtons.testFlag(Qt::NoButton)) { + if (!pointerIsWithinTargetWindowBounds && event.mouseButtons.testFlag(Qt::NoButton)) { leaveWindow(m_lastMouseTargetWindow); } bool shouldDeliverEvent = pointerIsWithinTargetWindowBounds; QWindow *eventTarget = targetWindow; - if (!eventTarget && buttonEventType == QEvent::MouseButtonRelease) { + if (!eventTarget && event.type == EventType::PointerUp) { eventTarget = m_lastMouseTargetWindow; m_lastMouseTargetWindow = nullptr; shouldDeliverEvent = true; @@ -1109,13 +1044,122 @@ bool QWasmCompositor::processMouse(int eventType, const EmscriptenMouseEvent *mo eventTarget != nullptr && shouldDeliverEvent && QWindowSystemInterface::handleMouseEvent( eventTarget, QWasmIntegration::getTimestamp(), pointInTargetWindowCoords, targetPointInScreenCoords, - m_pressedButtons, button, buttonEventType, modifiers); + event.mouseButtons, event.mouseButton, buttonEventType, event.modifiers); - if (!eventAccepted && buttonEventType == QEvent::MouseButtonPress) + if (!eventAccepted && event.type == EventType::PointerDown) QGuiApplicationPrivate::instance()->closeAllPopups(); return eventAccepted; } +QWasmCompositor::WindowManipulation::WindowManipulation(QWasmScreen *screen) + : m_screen(screen) +{ + Q_ASSERT(!!screen); +} + +QWasmCompositor::WindowManipulation::Operation QWasmCompositor::WindowManipulation::operation() const +{ + if (!m_state) + return Operation::None; + + return std::holds_alternative(m_state->operationSpecific) + ? Operation::Move : Operation::Resize; +} + +void QWasmCompositor::WindowManipulation::onPointerDown( + const PointerEvent& event, QWindow* windowAtPoint) +{ + // Only one operation at a time. + if (operation() != Operation::None) + return; + + if (event.mouseButton != Qt::MouseButton::LeftButton) + return; + + const bool isTargetWindowResizable = + !windowAtPoint->windowStates().testFlag(Qt::WindowMaximized) && + !windowAtPoint->windowStates().testFlag(Qt::WindowFullScreen); + if (!isTargetWindowResizable) + return; + + const bool isTargetWindowBlocked = + QGuiApplicationPrivate::instance()->isWindowBlocked(windowAtPoint); + if (isTargetWindowBlocked) + return; + + const auto pointInScreenCoords = m_screen->geometry().topLeft() + event.point; + + std::unique_ptr> operationSpecific; + if (AsWasmWindow(windowAtPoint)->isPointOnTitle(pointInScreenCoords)) { + operationSpecific = std::make_unique>(MoveState { + .m_lastPointInScreenCoords = pointInScreenCoords + }); + } else if (AsWasmWindow(windowAtPoint)->isPointOnResizeRegion(pointInScreenCoords)) { + operationSpecific = std::make_unique>(ResizeState { + .m_resizeMode = AsWasmWindow(windowAtPoint)->resizeModeAtPoint(pointInScreenCoords), + .m_originInScreenCoords = pointInScreenCoords, + .m_initialWindowBounds = windowAtPoint->geometry(), + .m_minShrink = QPoint(windowAtPoint->minimumWidth() - windowAtPoint->geometry().width(), + windowAtPoint->minimumHeight() - windowAtPoint->geometry().height()), + .m_maxGrow = QPoint( + windowAtPoint->maximumWidth() - windowAtPoint->geometry().width(), + windowAtPoint->maximumHeight() - windowAtPoint->geometry().height()), + }); + } else { + return; + } + + m_state.reset(new OperationState{ + .pointerId = event.pointerId, + .window = windowAtPoint, + .operationSpecific = std::move(*operationSpecific), + }); + + m_screen->canvas().call("setPointerCapture", event.pointerId); +} + +void QWasmCompositor::WindowManipulation::onPointerMove( + const PointerEvent& event) +{ + if (operation() == Operation::None || event.pointerId != m_state->pointerId) + return; + + const auto pointInScreenCoords = m_screen->geometry().topLeft() + event.point; + + switch (operation()) { + case Operation::Move: { + const QPoint targetPointClippedToScreen( + std::max(m_screen->geometry().left(), std::min(m_screen->geometry().right(), pointInScreenCoords.x())), + std::max(m_screen->geometry().top(), std::min(m_screen->geometry().bottom(), pointInScreenCoords.y()))); + + const QPoint difference = targetPointClippedToScreen - + std::get(m_state->operationSpecific).m_lastPointInScreenCoords; + + std::get(m_state->operationSpecific).m_lastPointInScreenCoords = targetPointClippedToScreen; + + m_state->window->setPosition(m_state->window->position() + difference); + break; + } + case Operation::Resize: { + resizeWindow(pointInScreenCoords - + std::get(m_state->operationSpecific).m_originInScreenCoords); + break; + } + case Operation::None: + Q_ASSERT(0); + break; + } +} + +void QWasmCompositor::WindowManipulation::onPointerUp(const PointerEvent& event) +{ + if (operation() == Operation::None || event.mouseButtons != 0 || event.pointerId != m_state->pointerId) + return; + + m_state.reset(); + m_screen->canvas().call("releasePointerCapture", event.pointerId); +} + bool QWasmCompositor::processKeyboard(int eventType, const EmscriptenKeyboardEvent *keyEvent) { Qt::Key qtKey; @@ -1139,7 +1183,7 @@ bool QWasmCompositor::processKeyboard(int eventType, const EmscriptenKeyboardEve if (keyType == QEvent::None) return 0; - QFlags modifiers = m_eventTranslator->translateKeyboardEventModifier(keyEvent); + QFlags modifiers = KeyboardModifier::getForEvent(*keyEvent); // Clipboard fallback path: cut/copy/paste are handled by clipboard event // handlers if direct clipboard access is not available. @@ -1180,7 +1224,7 @@ bool QWasmCompositor::processWheel(int eventType, const EmscriptenWheelEvent *wh { Q_UNUSED(eventType); - EmscriptenMouseEvent mouseEvent = wheelEvent->mouse; + const EmscriptenMouseEvent* mouseEvent = &wheelEvent->mouse; int scrollFactor = 0; switch (wheelEvent->deltaMode) { @@ -1197,8 +1241,8 @@ bool QWasmCompositor::processWheel(int eventType, const EmscriptenWheelEvent *wh scrollFactor = -scrollFactor; // Web scroll deltas are inverted from Qt deltas. - Qt::KeyboardModifiers modifiers = m_eventTranslator->translateMouseEventModifier(&mouseEvent); - QPoint targetPointInCanvasCoords(mouseEvent.targetX, mouseEvent.targetY); + Qt::KeyboardModifiers modifiers = KeyboardModifier::getForEvent(*mouseEvent); + QPoint targetPointInCanvasCoords(mouseEvent->targetX, mouseEvent->targetY); QPoint targetPointInScreenCoords = screen()->geometry().topLeft() + targetPointInCanvasCoords; QWindow *targetWindow = screen()->compositor()->windowAt(targetPointInScreenCoords, 5); @@ -1287,7 +1331,7 @@ int QWasmCompositor::handleTouch(int eventType, const EmscriptenTouchEvent *touc touchPointList.append(touchPoint); } - QFlags keyModifier = m_eventTranslator->translateTouchEventModifier(touchEvent); + QFlags keyModifier = KeyboardModifier::getForEvent(*touchEvent); bool accepted = false; diff --git a/src/plugins/platforms/wasm/qwasmcompositor.h b/src/plugins/platforms/wasm/qwasmcompositor.h index 8524747a9d..b7a93d110b 100644 --- a/src/plugins/platforms/wasm/qwasmcompositor.h +++ b/src/plugins/platforms/wasm/qwasmcompositor.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -21,6 +22,7 @@ QT_BEGIN_NAMESPACE +struct PointerEvent; class QWasmWindow; class QWasmScreen; class QOpenGLContext; @@ -69,16 +71,23 @@ public: }; Q_DECLARE_FLAGS(StateFlags, QWasmStateFlag) + enum ResizeDimension { + Left = 1, + Right = 1 << 1, + Top = 1 << 2, + Bottom = 1 << 3 + }; + enum ResizeMode { ResizeNone, - ResizeTopLeft, - ResizeTop, - ResizeTopRight, - ResizeRight, - ResizeBottomRight, - ResizeBottom, - ResizeBottomLeft, - ResizeLeft + ResizeTopLeft = Top | Left, + ResizeTop = Top, + ResizeTopRight = Top | Right, + ResizeRight = Right, + ResizeBottomRight = Bottom | Right, + ResizeBottom = Bottom, + ResizeBottomLeft = Bottom | Left, + ResizeLeft = Left }; struct QWasmTitleBarOptions { @@ -125,11 +134,9 @@ 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); bool processMouseEnter(const EmscriptenMouseEvent *mouseEvent); bool processMouseLeave(); @@ -140,6 +147,47 @@ private slots: void frame(); private: + class WindowManipulation { + public: + enum class Operation { + None, + Move, + Resize, + }; + + WindowManipulation(QWasmScreen* screen); + + void onPointerDown(const PointerEvent& event, QWindow* windowAtPoint); + void onPointerMove(const PointerEvent& event); + void onPointerUp(const PointerEvent& event); + + Operation operation() const; + + private: + struct ResizeState { + ResizeMode m_resizeMode; + QPoint m_originInScreenCoords; + QRect m_initialWindowBounds; + const QPoint m_minShrink; + const QPoint m_maxGrow; + }; + struct MoveState { + QPoint m_lastPointInScreenCoords; + }; + struct OperationState + { + int pointerId; + QPointer window; + std::variant operationSpecific; + }; + + void resizeWindow(const QPoint& amount); + + QWasmScreen *m_screen; + + std::unique_ptr m_state; + }; + void notifyTopWindowChanged(QWasmWindow *window); void drawWindow(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window); void drawWindowContent(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window); @@ -156,12 +204,15 @@ private: 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); + bool processPointer(const PointerEvent& event); + static int touchCallback(int eventType, const EmscriptenTouchEvent *ev, void *userData); + WindowManipulation m_windowManipulation; + QScopedPointer m_context; QScopedPointer m_blitter; @@ -170,7 +221,6 @@ private: QRegion m_globalDamage; // damage caused by expose, window close, etc. bool m_needComposit = false; bool m_inFlush = false; - bool m_inResize = false; bool m_isEnabled = true; QSize m_targetSize; qreal m_targetDevicePixelRatio = 1; @@ -179,14 +229,15 @@ private: int m_requestAnimationFrameId = -1; bool m_inDeliverUpdateRequest = false; - QPointer m_windowBeingManipulated; QPointer m_pressedWindow; QPointer m_lastMouseTargetWindow; - Qt::MouseButtons m_pressedButtons = Qt::NoButton; - ResizeMode m_resizeMode = ResizeNone; - QPoint m_resizePoint; - QRect m_resizeStartRect; + std::unique_ptr m_pointerDownCallback; + std::unique_ptr m_pointerMoveCallback; + std::unique_ptr m_pointerUpCallback; + std::unique_ptr m_pointerLeaveCallback; + std::unique_ptr m_pointerEnterCallback; + std::unique_ptr m_touchDevice; QMap m_pressedTouchIds; diff --git a/src/plugins/platforms/wasm/qwasmdrag.cpp b/src/plugins/platforms/wasm/qwasmdrag.cpp index c87e4ce7c2..c2b50e4356 100644 --- a/src/plugins/platforms/wasm/qwasmdrag.cpp +++ b/src/plugins/platforms/wasm/qwasmdrag.cpp @@ -64,8 +64,7 @@ static void dropEvent(val event) if (wasmDrag->m_mimeData) delete wasmDrag->m_mimeData; wasmDrag->m_mimeData = new QMimeData; - int button = event["button"].as(); - wasmDrag->m_qButton = QWasmEventTranslator::translateMouseButton(button); + wasmDrag->m_qButton = MouseEvent::buttonFromWeb(event["button"].as()); wasmDrag->m_keyModifiers = Qt::NoModifier; if (event["altKey"].as()) diff --git a/src/plugins/platforms/wasm/qwasmevent.cpp b/src/plugins/platforms/wasm/qwasmevent.cpp new file mode 100644 index 0000000000..b094dbfe14 --- /dev/null +++ b/src/plugins/platforms/wasm/qwasmevent.cpp @@ -0,0 +1,53 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "qwasmevent.h" + +QT_BEGIN_NAMESPACE + +namespace KeyboardModifier +{ +template <> +QFlags getForEvent( + const EmscriptenKeyboardEvent& event) +{ + return internal::Helper::getModifierForEvent(event) | + (event.location == DOM_KEY_LOCATION_NUMPAD ? Qt::KeypadModifier : Qt::NoModifier); +} +} // namespace KeyboardModifier + +std::optional PointerEvent::fromWeb(emscripten::val event) +{ + PointerEvent ret; + + const auto eventType = ([&event]() -> std::optional { + const auto eventTypeString = event["type"].as(); + + if (eventTypeString == "pointermove") + return EventType::PointerMove; + else if (eventTypeString == "pointerup") + return EventType::PointerUp; + else if (eventTypeString == "pointerdown") + return EventType::PointerDown; + else if (eventTypeString == "pointerenter") + return EventType::PointerEnter; + else if (eventTypeString == "pointerleave") + return EventType::PointerLeave; + return std::nullopt; + })(); + if (!eventType) + return std::nullopt; + + ret.type = *eventType; + ret.pointerType = event["pointerType"].as() == "mouse" ? + PointerType::Mouse : PointerType::Other; + ret.mouseButton = MouseEvent::buttonFromWeb(event["button"].as()); + ret.mouseButtons = MouseEvent::buttonsFromWeb(event["buttons"].as()); + ret.point = QPoint(event["x"].as(), event["y"].as()); + ret.pointerId = event["pointerId"].as(); + ret.modifiers = KeyboardModifier::getForEvent(event); + + return ret; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmevent.h b/src/plugins/platforms/wasm/qwasmevent.h new file mode 100644 index 0000000000..784591fb95 --- /dev/null +++ b/src/plugins/platforms/wasm/qwasmevent.h @@ -0,0 +1,145 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef QWASMEVENT_H +#define QWASMEVENT_H + +#include "qwasmplatform.h" + +#include +#include + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +enum class EventType { + PointerDown, + PointerMove, + PointerUp, + PointerEnter, + PointerLeave, +}; + +enum class PointerType { + Mouse, + Other, +}; + +namespace KeyboardModifier { +namespace internal +{ + // Check for the existence of shiftKey, ctrlKey, altKey and metaKey in a type. + // Based on that, we can safely assume we are dealing with an emscripten event type. + template + struct IsEmscriptenEvent + { + template + struct SFINAE {}; + template static char Test( + SFINAE*); + template static int Test(...); + static const bool value = sizeof(Test(0)) == sizeof(char); + }; + + template + struct Helper; + + template + struct Helper::value>> + { + static QFlags getModifierForEvent(const T& event) { + QFlags keyModifier = Qt::NoModifier; + if (event.shiftKey) + keyModifier |= Qt::ShiftModifier; + if (event.ctrlKey) + keyModifier |= platform() == Platform::MacOS ? Qt::MetaModifier : Qt::ControlModifier; + if (event.altKey) + keyModifier |= Qt::AltModifier; + if (event.metaKey) + keyModifier |= platform() == Platform::MacOS ? Qt::ControlModifier : Qt::MetaModifier; + + return keyModifier; + } + }; + + template<> + struct Helper + { + static QFlags getModifierForEvent(const emscripten::val& event) { + QFlags keyModifier = Qt::NoModifier; + if (event["shiftKey"].as()) + keyModifier |= Qt::ShiftModifier; + if (event["ctrlKey"].as()) + keyModifier |= platform() == Platform::MacOS ? Qt::MetaModifier : Qt::ControlModifier; + if (event["altKey"].as()) + keyModifier |= Qt::AltModifier; + if (event["metaKey"].as()) + keyModifier |= platform() == Platform::MacOS ? Qt::ControlModifier : Qt::MetaModifier; + if (event["constructor"]["name"].as() == "KeyboardEvent" && + event["location"].as() == DOM_KEY_LOCATION_NUMPAD) { + keyModifier |= Qt::KeypadModifier; + } + + return keyModifier; + } + }; +} // namespace internal + +template +QFlags getForEvent(const Event& event) +{ + return internal::Helper::getModifierForEvent(event); +} + +template <> +QFlags getForEvent( + const EmscriptenKeyboardEvent& event); + +} // namespace KeyboardModifier + +struct Q_CORE_EXPORT Event +{ + EventType type; +}; + +struct Q_CORE_EXPORT MouseEvent : public Event +{ + QPoint point; + Qt::MouseButton mouseButton; + Qt::MouseButtons mouseButtons; + QFlags modifiers; + + static constexpr Qt::MouseButton buttonFromWeb(int webButton) { + switch (webButton) { + case 0: + return Qt::LeftButton; + case 1: + return Qt::MiddleButton; + case 2: + return Qt::RightButton; + default: + return Qt::NoButton; + } + } + + static constexpr Qt::MouseButtons buttonsFromWeb(unsigned short webButtons) { + // Coincidentally, Qt and web bitfields match. + return Qt::MouseButtons::fromInt(webButtons); +} +}; + +struct Q_CORE_EXPORT PointerEvent : public MouseEvent +{ + static std::optional fromWeb(emscripten::val webEvent); + + PointerType pointerType; + int pointerId; +}; + +QT_END_NAMESPACE + +#endif // QWASMEVENT_H diff --git a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp index 6f13009aef..edd323294c 100644 --- a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp +++ b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp @@ -160,54 +160,6 @@ QWasmEventTranslator::~QWasmEventTranslator() { } -template -QFlags QWasmEventTranslator::translatKeyModifier(const Event *event) -{ - // macOS CTRL <-> META switching. We most likely want to enable - // the existing switching code in QtGui, but for now do it here. - - QFlags keyModifier = Qt::NoModifier; - if (event->shiftKey) - keyModifier |= Qt::ShiftModifier; - if (event->ctrlKey) { - if (g_usePlatformMacSpecifics) - keyModifier |= Qt::MetaModifier; - else - keyModifier |= Qt::ControlModifier; - } - if (event->altKey) - keyModifier |= Qt::AltModifier; - if (event->metaKey) { - if (g_usePlatformMacSpecifics) - keyModifier |= Qt::ControlModifier; - else - keyModifier |= Qt::MetaModifier; - } - - return keyModifier; -} - -QFlags QWasmEventTranslator::translateKeyboardEventModifier(const EmscriptenKeyboardEvent *event) -{ - QFlags keyModifier = translatKeyModifier(event); - - if (event->location == DOM_KEY_LOCATION_NUMPAD) { - keyModifier |= Qt::KeypadModifier; - } - - return keyModifier; -} - -QFlags QWasmEventTranslator::translateMouseEventModifier(const EmscriptenMouseEvent *mouseEvent) -{ - return translatKeyModifier(mouseEvent); -} - -QFlags QWasmEventTranslator::translateTouchEventModifier(const EmscriptenTouchEvent *touchEvent) -{ - return translatKeyModifier(touchEvent); -} - Qt::Key QWasmEventTranslator::translateEmscriptKey(const EmscriptenKeyboardEvent *emscriptKey) { Qt::Key qtKey = Qt::Key_unknown; diff --git a/src/plugins/platforms/wasm/qwasmeventtranslator.h b/src/plugins/platforms/wasm/qwasmeventtranslator.h index d86ddf7625..76da1ebbe5 100644 --- a/src/plugins/platforms/wasm/qwasmeventtranslator.h +++ b/src/plugins/platforms/wasm/qwasmeventtranslator.h @@ -12,6 +12,8 @@ #include #include #include +#include "qwasmevent.h" +#include "qwasmplatform.h" QT_BEGIN_NAMESPACE @@ -26,25 +28,7 @@ public: explicit QWasmEventTranslator(); ~QWasmEventTranslator(); - template - QFlags translatKeyModifier(const Event *event); - static Qt::Key translateEmscriptKey(const EmscriptenKeyboardEvent *emscriptKey); - QFlags translateKeyboardEventModifier(const EmscriptenKeyboardEvent *keyEvent); - QFlags translateMouseEventModifier(const EmscriptenMouseEvent *mouseEvent); - QFlags translateTouchEventModifier(const EmscriptenTouchEvent *touchEvent); - static constexpr Qt::MouseButton translateMouseButton(unsigned short button) { - switch (button) { - case 0: - return Qt::LeftButton; - case 1: - return Qt::MiddleButton; - case 2: - return Qt::RightButton; - default: - return Qt::NoButton; - } - } static QCursor cursorForMode(QWasmCompositor::ResizeMode mode); QString getKeyText(const EmscriptenKeyboardEvent *keyEvent, Qt::Key key); diff --git a/src/plugins/platforms/wasm/qwasminputcontext.cpp b/src/plugins/platforms/wasm/qwasminputcontext.cpp index f27ea013c6..9da0a9dc34 100644 --- a/src/plugins/platforms/wasm/qwasminputcontext.cpp +++ b/src/plugins/platforms/wasm/qwasminputcontext.cpp @@ -50,7 +50,7 @@ QWasmInputContext::QWasmInputContext() m_inputElement.set("style", "position:absolute;left:-1000px;top:-1000px"); // offscreen m_inputElement.set("contentaediable","true"); - if (QWasmIntegration::get()->platform == QWasmIntegration::AndroidPlatform) { + if (platform() == Platform::Android) { emscripten::val body = document["body"]; body.call("appendChild", m_inputElement); @@ -65,8 +65,7 @@ QWasmInputContext::QWasmInputContext() &androidKeyboardCallback); } - if (QWasmIntegration::get()->platform == QWasmIntegration::MacOSPlatform || - QWasmIntegration::get()->platform == QWasmIntegration::iPhonePlatform) + if (platform() == Platform::MacOS || platform() == Platform::iPhone) { auto callback = [=](emscripten::val) { m_inputElement["parentElement"].call("removeChild", m_inputElement); @@ -81,7 +80,7 @@ QWasmInputContext::QWasmInputContext() QWasmInputContext::~QWasmInputContext() { - if (QWasmIntegration::get()->platform == QWasmIntegration::AndroidPlatform) + if (platform() == Platform::Android) emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, 0, NULL); } @@ -107,7 +106,7 @@ void QWasmInputContext::update(Qt::InputMethodQueries queries) void QWasmInputContext::showInputPanel() { - if (QWasmIntegration::get()->platform == QWasmIntegration::WindowsPlatform + if (platform() == Platform::Windows && inputPanelIsOpen) // call this only once for win32 return; // this is called each time the keyboard is touched @@ -119,9 +118,9 @@ void QWasmInputContext::showInputPanel() // captured by the keyboard event handler installed on the // canvas. - if (QWasmIntegration::get()->platform == QWasmIntegration::MacOSPlatform // keep for compatibility - || QWasmIntegration::get()->platform == QWasmIntegration::iPhonePlatform - || QWasmIntegration::get()->platform == QWasmIntegration::WindowsPlatform) { + if (platform() == Platform::MacOS // keep for compatibility + || platform() == Platform::iPhone + || platform() == Platform::Windows) { emscripten::val canvas = focusCanvas(); if (canvas == emscripten::val::undefined()) return; diff --git a/src/plugins/platforms/wasm/qwasmintegration.cpp b/src/plugins/platforms/wasm/qwasmintegration.cpp index f19ede9246..a29e18e98a 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.cpp +++ b/src/plugins/platforms/wasm/qwasmintegration.cpp @@ -86,22 +86,6 @@ QWasmIntegration::QWasmIntegration() s_instance = this; touchPoints = emscripten::val::global("navigator")["maxTouchPoints"].as(); - // The Platform Detect: expand coverage as needed - platform = GenericPlatform; - emscripten::val rawPlatform = emscripten::val::global("navigator")["platform"]; - - if (rawPlatform.call("includes", emscripten::val("Mac"))) - platform = MacOSPlatform; - if (rawPlatform.call("includes", emscripten::val("iPhone"))) - platform = iPhonePlatform; - if (rawPlatform.call("includes", emscripten::val("Win32"))) - platform = WindowsPlatform; - if (rawPlatform.call("includes", emscripten::val("Linux"))) { - platform = LinuxPlatform; - emscripten::val uAgent = emscripten::val::global("navigator")["userAgent"]; - if (uAgent.call("includes", emscripten::val("Android"))) - platform = AndroidPlatform; - } // Create screens for container elements. Each container element can be a div element (preferred), // or a canvas element (legacy). Qt versions prior to 6.x read the "qtCanvasElements" module property, diff --git a/src/plugins/platforms/wasm/qwasmintegration.h b/src/plugins/platforms/wasm/qwasmintegration.h index db1f928f76..76296ff1a7 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.h +++ b/src/plugins/platforms/wasm/qwasmintegration.h @@ -40,15 +40,6 @@ class QWasmIntegration : public QObject, public QPlatformIntegration { Q_OBJECT public: - enum Platform { - GenericPlatform, - MacOSPlatform, - WindowsPlatform, - LinuxPlatform, - AndroidPlatform, - iPhonePlatform - }; - QWasmIntegration(); ~QWasmIntegration(); @@ -89,7 +80,6 @@ public: void removeBackingStore(QWindow* window); static quint64 getTimestamp(); - Platform platform; int touchPoints; private: diff --git a/src/plugins/platforms/wasm/qwasmplatform.cpp b/src/plugins/platforms/wasm/qwasmplatform.cpp new file mode 100644 index 0000000000..c641e345e4 --- /dev/null +++ b/src/plugins/platforms/wasm/qwasmplatform.cpp @@ -0,0 +1,31 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "qwasmplatform.h" + +QT_BEGIN_NAMESPACE + +Platform platform() +{ + static const Platform qtDetectedPlatform = ([]() { + // The Platform Detect: expand coverage as needed + emscripten::val rawPlatform = emscripten::val::global("navigator")["platform"]; + + if (rawPlatform.call("includes", emscripten::val("Mac"))) + return Platform::MacOS; + if (rawPlatform.call("includes", emscripten::val("iPhone"))) + return Platform::iPhone; + if (rawPlatform.call("includes", emscripten::val("Win32"))) + return Platform::Windows; + if (rawPlatform.call("includes", emscripten::val("Linux"))) { + emscripten::val uAgent = emscripten::val::global("navigator")["userAgent"]; + if (uAgent.call("includes", emscripten::val("Android"))) + return Platform::Android; + return Platform::Linux; + } + return Platform::Generic; + })(); + return qtDetectedPlatform; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmplatform.h b/src/plugins/platforms/wasm/qwasmplatform.h new file mode 100644 index 0000000000..239efdeae9 --- /dev/null +++ b/src/plugins/platforms/wasm/qwasmplatform.h @@ -0,0 +1,29 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef QWASMPLATFORM_H +#define QWASMPLATFORM_H + +#include +#include + +#include + +#include + +QT_BEGIN_NAMESPACE + +enum class Platform { + Generic, + MacOS, + Windows, + Linux, + Android, + iPhone, +}; + +Platform platform(); + +QT_END_NAMESPACE + +#endif // QWASMPLATFORM_H