diff options
Diffstat (limited to 'src/plugins/platforms/wasm/qwasmcompositor.cpp')
-rw-r--r-- | src/plugins/platforms/wasm/qwasmcompositor.cpp | 367 |
1 files changed, 67 insertions, 300 deletions
diff --git a/src/plugins/platforms/wasm/qwasmcompositor.cpp b/src/plugins/platforms/wasm/qwasmcompositor.cpp index a66c5f5beb..c534cce9ef 100644 --- a/src/plugins/platforms/wasm/qwasmcompositor.cpp +++ b/src/plugins/platforms/wasm/qwasmcompositor.cpp @@ -3,45 +3,19 @@ #include "qwasmcompositor.h" #include "qwasmwindow.h" -#include "qwasmeventtranslator.h" -#include "qwasmeventdispatcher.h" -#include "qwasmclipboard.h" -#include "qwasmevent.h" -#include <QtGui/private/qwindow_p.h> - -#include <private/qguiapplication_p.h> +#include <private/qeventdispatcher_wasm_p.h> #include <qpa/qwindowsysteminterface.h> -#include <QtCore/qcoreapplication.h> -#include <QtGui/qguiapplication.h> - -#include <emscripten/bind.h> -namespace { -QWasmWindow *asWasmWindow(QWindow *window) -{ - return static_cast<QWasmWindow*>(window->handle()); -} -} // namespace +#include <emscripten/html5.h> using namespace emscripten; -Q_GUI_EXPORT int qt_defaultDpiX(); +bool QWasmCompositor::m_requestUpdateHoldEnabled = true; -QWasmCompositor::QWasmCompositor(QWasmScreen *screen) - : QObject(screen), - m_windowStack(std::bind(&QWasmCompositor::onTopWindowChanged, this)), - m_eventTranslator(std::make_unique<QWasmEventTranslator>()) +QWasmCompositor::QWasmCompositor(QWasmScreen *screen) : QObject(screen) { - m_touchDevice = std::make_unique<QPointingDevice>( - "touchscreen", 1, QInputDevice::DeviceType::TouchScreen, - QPointingDevice::PointerType::Finger, - QPointingDevice::Capability::Position | QPointingDevice::Capability::Area - | QPointingDevice::Capability::NormalizedPosition, - 10, 0); - - QWindowSystemInterface::registerInputDevice(m_touchDevice.get()); QWindowSystemInterface::setSynchronousWindowSystemEvents(true); } @@ -50,122 +24,54 @@ QWasmCompositor::~QWasmCompositor() if (m_requestAnimationFrameId != -1) emscripten_cancel_animation_frame(m_requestAnimationFrameId); - destroy(); -} - -void QWasmCompositor::onScreenDeleting() -{ - deregisterEventHandlers(); -} - -void QWasmCompositor::deregisterEventHandlers() -{ - QByteArray screenElementSelector = screen()->eventTargetId().toUtf8(); - emscripten_set_keydown_callback(screenElementSelector.constData(), 0, 0, NULL); - emscripten_set_keyup_callback(screenElementSelector.constData(), 0, 0, NULL); - - emscripten_set_touchstart_callback(screenElementSelector.constData(), 0, 0, NULL); - emscripten_set_touchend_callback(screenElementSelector.constData(), 0, 0, NULL); - emscripten_set_touchmove_callback(screenElementSelector.constData(), 0, 0, NULL); - emscripten_set_touchcancel_callback(screenElementSelector.constData(), 0, 0, NULL); -} - -void QWasmCompositor::destroy() -{ // TODO(mikolaj.boc): Investigate if m_isEnabled is needed at all. It seems like a frame should // not be generated after this instead. m_isEnabled = false; // prevent frame() from creating a new m_context } -void QWasmCompositor::initEventHandlers() -{ - constexpr EM_BOOL UseCapture = 1; - - const QByteArray screenElementSelector = screen()->eventTargetId().toUtf8(); - emscripten_set_keydown_callback(screenElementSelector.constData(), (void *)this, UseCapture, - &keyboard_cb); - emscripten_set_keyup_callback(screenElementSelector.constData(), (void *)this, UseCapture, - &keyboard_cb); - - emscripten_set_touchstart_callback(screenElementSelector.constData(), (void *)this, UseCapture, - &touchCallback); - emscripten_set_touchend_callback(screenElementSelector.constData(), (void *)this, UseCapture, - &touchCallback); - emscripten_set_touchmove_callback(screenElementSelector.constData(), (void *)this, UseCapture, - &touchCallback); - emscripten_set_touchcancel_callback(screenElementSelector.constData(), (void *)this, UseCapture, - &touchCallback); -} - -void QWasmCompositor::addWindow(QWasmWindow *window) -{ - m_windowStack.pushWindow(window); - m_windowStack.topWindow()->requestActivateWindow(); - - updateEnabledState(); -} - -void QWasmCompositor::removeWindow(QWasmWindow *window) +void QWasmCompositor::onWindowTreeChanged(QWasmWindowTreeNodeChangeType changeType, + QWasmWindow *window) { - m_requestUpdateWindows.remove(window); - m_windowStack.removeWindow(window); - if (m_windowStack.topWindow()) - m_windowStack.topWindow()->requestActivateWindow(); - - updateEnabledState(); + auto allWindows = screen()->allWindows(); + setEnabled(std::any_of(allWindows.begin(), allWindows.end(), [](QWasmWindow *element) { + return !element->context2d().isUndefined(); + })); + if (changeType == QWasmWindowTreeNodeChangeType::NodeRemoval) + m_requestUpdateWindows.remove(window); } -void QWasmCompositor::updateEnabledState() +void QWasmCompositor::setEnabled(bool enabled) { - m_isEnabled = std::any_of(m_windowStack.begin(), m_windowStack.end(), [](QWasmWindow *window) { - return !window->context2d().isUndefined(); - }); + m_isEnabled = enabled; } -void QWasmCompositor::raise(QWasmWindow *window) +// requestUpdate delivery is initially disabled at startup, while Qt completes +// startup tasks such as font loading. This function enables requestUpdate delivery +// again. +bool QWasmCompositor::releaseRequestUpdateHold() { - m_windowStack.raise(window); + const bool wasEnabled = m_requestUpdateHoldEnabled; + m_requestUpdateHoldEnabled = false; + return wasEnabled; } -void QWasmCompositor::lower(QWasmWindow *window) -{ - m_windowStack.lower(window); -} - -QWindow *QWasmCompositor::windowAt(QPoint targetPointInScreenCoords, int padding) const -{ - const auto found = std::find_if( - m_windowStack.begin(), m_windowStack.end(), - [padding, &targetPointInScreenCoords](const QWasmWindow *window) { - const QRect geometry = window->windowFrameGeometry().adjusted(-padding, -padding, - padding, padding); - - return window->isVisible() && geometry.contains(targetPointInScreenCoords); - }); - return found != m_windowStack.end() ? (*found)->window() : nullptr; -} - -QWindow *QWasmCompositor::keyWindow() const -{ - return m_windowStack.topWindow() ? m_windowStack.topWindow()->window() : nullptr; -} - -void QWasmCompositor::requestUpdateAllWindows() -{ - m_requestUpdateAllWindows = true; - requestUpdate(); -} - -void QWasmCompositor::requestUpdateWindow(QWasmWindow *window, UpdateRequestDeliveryType updateType) +void QWasmCompositor::requestUpdateWindow(QWasmWindow *window, const QRect &updateRect, UpdateRequestDeliveryType updateType) { auto it = m_requestUpdateWindows.find(window); if (it == m_requestUpdateWindows.end()) { - m_requestUpdateWindows.insert(window, updateType); + m_requestUpdateWindows.insert(window, std::make_tuple(updateRect, updateType)); } else { // Already registered, but upgrade ExposeEventDeliveryType to UpdateRequestDeliveryType. // if needed, to make sure QWindow::updateRequest's are matched. - if (it.value() == ExposeEventDelivery && updateType == UpdateRequestDelivery) - it.value() = UpdateRequestDelivery; + if (std::get<0>(it.value()) != updateRect) { + QRegion region; + region |= std::get<0>(it.value()); + region |= updateRect; + std::get<0>(it.value()) = region.boundingRect(); + } + if (std::get<1>(it.value()) == ExposeEventDelivery && + updateType == UpdateRequestDelivery) + std::get<1>(it.value()) = UpdateRequestDelivery; } requestUpdate(); @@ -177,6 +83,9 @@ void QWasmCompositor::requestUpdate() if (m_requestAnimationFrameId != -1) return; + if (m_requestUpdateHoldEnabled) + return; + static auto frame = [](double frameTime, void *context) -> int { Q_UNUSED(frameTime); @@ -197,207 +106,65 @@ void QWasmCompositor::deliverUpdateRequests() // update set. auto requestUpdateWindows = m_requestUpdateWindows; m_requestUpdateWindows.clear(); - bool requestUpdateAllWindows = m_requestUpdateAllWindows; - m_requestUpdateAllWindows = false; // Update window content, either all windows or a spesific set of windows. Use the correct // update type: QWindow subclasses expect that requested and delivered updateRequests matches // exactly. m_inDeliverUpdateRequest = true; - if (requestUpdateAllWindows) { - for (QWasmWindow *window : m_windowStack) { - auto it = requestUpdateWindows.find(window); - UpdateRequestDeliveryType updateType = - (it == m_requestUpdateWindows.end() ? ExposeEventDelivery : it.value()); - deliverUpdateRequest(window, updateType); - } - } else { - for (auto it = requestUpdateWindows.constBegin(); it != requestUpdateWindows.constEnd(); ++it) { - auto *window = it.key(); - UpdateRequestDeliveryType updateType = it.value(); - deliverUpdateRequest(window, updateType); - } + for (auto it = requestUpdateWindows.constBegin(); it != requestUpdateWindows.constEnd(); ++it) { + auto *window = it.key(); + + const QRect updateRect = std::get<0>(it.value()); + const UpdateRequestDeliveryType updateType = std::get<1>(it.value()); + deliverUpdateRequest(window, updateRect, updateType); } + m_inDeliverUpdateRequest = false; - frame(requestUpdateAllWindows, requestUpdateWindows.keys()); + frame(requestUpdateWindows.keys()); } -void QWasmCompositor::deliverUpdateRequest(QWasmWindow *window, UpdateRequestDeliveryType updateType) +void QWasmCompositor::deliverUpdateRequest( + QWasmWindow *window, + const QRect &updateRect, + UpdateRequestDeliveryType updateType) { - // update by deliverUpdateRequest and expose event accordingly. + QWindow *qwindow = window->window(); + + // Make sure the DPR value for the window is up to date on expose/repaint. + // FIXME: listen to native DPR change events instead, if/when available. + QWindowSystemInterface::handleWindowDevicePixelRatioChanged(qwindow); + + // Update by deliverUpdateRequest and expose event according to requested update + // type. If the window has not yet been exposed then we must expose it first regardless + // of update type. The deliverUpdateRequest must still be sent in this case in order + // to maintain correct window update state. if (updateType == UpdateRequestDelivery) { - window->QPlatformWindow::deliverUpdateRequest(); + if (qwindow->isExposed() == false) + QWindowSystemInterface::handleExposeEvent(qwindow, updateRect); + window->deliverUpdateRequest(); } else { - QWindow *qwindow = window->window(); - QWindowSystemInterface::handleExposeEvent( - qwindow, QRect(QPoint(0, 0), qwindow->geometry().size())); + QWindowSystemInterface::handleExposeEvent(qwindow, updateRect); } } -void QWasmCompositor::handleBackingStoreFlush(QWindow *window) +void QWasmCompositor::handleBackingStoreFlush(QWindow *window, const QRect &updateRect) { // Request update to flush the updated backing store content, unless we are currently // processing an update, in which case the new content will flushed as a part of that update. if (!m_inDeliverUpdateRequest) - requestUpdateWindow(asWasmWindow(window)); + requestUpdateWindow(static_cast<QWasmWindow *>(window->handle()), updateRect); } -int dpiScaled(qreal value) +void QWasmCompositor::frame(const QList<QWasmWindow *> &windows) { - return value * (qreal(qt_defaultDpiX()) / 96.0); -} - -void QWasmCompositor::frame(bool all, const QList<QWasmWindow *> &windows) -{ - if (!m_isEnabled || m_windowStack.empty() || !screen()) + if (!m_isEnabled || !screen()) return; - if (all) { - std::for_each(m_windowStack.rbegin(), m_windowStack.rend(), - [](QWasmWindow *window) { window->paint(); }); - } else { - std::for_each(windows.begin(), windows.end(), [](QWasmWindow *window) { window->paint(); }); - } -} - -void QWasmCompositor::onTopWindowChanged() -{ - constexpr int zOrderForElementInFrontOfScreen = 3; - int z = zOrderForElementInFrontOfScreen; - std::for_each(m_windowStack.rbegin(), m_windowStack.rend(), - [&z](QWasmWindow *window) { window->setZOrder(z++); }); - - auto it = m_windowStack.begin(); - if (it == m_windowStack.end()) { - return; - } - (*it)->onActivationChanged(true); - ++it; - for (; it != m_windowStack.end(); ++it) { - (*it)->onActivationChanged(false); - } + for (QWasmWindow *window : windows) + window->paint(); } QWasmScreen *QWasmCompositor::screen() { return static_cast<QWasmScreen *>(parent()); } - -int QWasmCompositor::keyboard_cb(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData) -{ - QWasmCompositor *wasmCompositor = reinterpret_cast<QWasmCompositor *>(userData); - return static_cast<int>(wasmCompositor->processKeyboard(eventType, keyEvent)); -} - -int QWasmCompositor::touchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData) -{ - auto compositor = reinterpret_cast<QWasmCompositor*>(userData); - return static_cast<int>(compositor->processTouch(eventType, touchEvent)); -} - -bool QWasmCompositor::processKeyboard(int eventType, const EmscriptenKeyboardEvent *emKeyEvent) -{ - constexpr bool ProceedToNativeEvent = false; - Q_ASSERT(eventType == EMSCRIPTEN_EVENT_KEYDOWN || eventType == EMSCRIPTEN_EVENT_KEYUP); - - auto translatedEvent = m_eventTranslator->translateKeyEvent(eventType, emKeyEvent); - - const QFlags<Qt::KeyboardModifier> modifiers = KeyboardModifier::getForEvent(*emKeyEvent); - - const auto clipboardResult = QWasmIntegration::get()->getWasmClipboard()->processKeyboard( - translatedEvent, modifiers); - - using ProcessKeyboardResult = QWasmClipboard::ProcessKeyboardResult; - if (clipboardResult == ProcessKeyboardResult::NativeClipboardEventNeeded) - return ProceedToNativeEvent; - - if (translatedEvent.text.isEmpty()) - translatedEvent.text = QString(emKeyEvent->key); - if (translatedEvent.text.size() > 1) - translatedEvent.text.clear(); - const auto result = - QWindowSystemInterface::handleKeyEvent( - 0, translatedEvent.type, translatedEvent.key, modifiers, translatedEvent.text); - return clipboardResult == ProcessKeyboardResult::NativeClipboardEventAndCopiedDataNeeded - ? ProceedToNativeEvent - : result; -} - -bool QWasmCompositor::processTouch(int eventType, const EmscriptenTouchEvent *touchEvent) -{ - QList<QWindowSystemInterface::TouchPoint> touchPointList; - touchPointList.reserve(touchEvent->numTouches); - QWindow *targetWindow = nullptr; - - for (int i = 0; i < touchEvent->numTouches; i++) { - - const EmscriptenTouchPoint *touches = &touchEvent->touches[i]; - - QPoint targetPointInScreenCoords = - screen()->mapFromLocal(QPoint(touches->targetX, touches->targetY)); - - targetWindow = screen()->compositor()->windowAt(targetPointInScreenCoords, 5); - if (targetWindow == nullptr) - continue; - - QWindowSystemInterface::TouchPoint touchPoint; - - touchPoint.area = QRect(0, 0, 8, 8); - touchPoint.id = touches->identifier; - touchPoint.pressure = 1.0; - - touchPoint.area.moveCenter(targetPointInScreenCoords); - - const auto tp = m_pressedTouchIds.constFind(touchPoint.id); - if (tp != m_pressedTouchIds.constEnd()) - touchPoint.normalPosition = tp.value(); - - QPointF pointInTargetWindowCoords = QPointF(targetWindow->mapFromGlobal(targetPointInScreenCoords)); - QPointF normalPosition(pointInTargetWindowCoords.x() / targetWindow->width(), - pointInTargetWindowCoords.y() / targetWindow->height()); - - const bool stationaryTouchPoint = (normalPosition == touchPoint.normalPosition); - touchPoint.normalPosition = normalPosition; - - switch (eventType) { - case EMSCRIPTEN_EVENT_TOUCHSTART: - if (tp != m_pressedTouchIds.constEnd()) { - touchPoint.state = (stationaryTouchPoint - ? QEventPoint::State::Stationary - : QEventPoint::State::Updated); - } else { - touchPoint.state = QEventPoint::State::Pressed; - } - m_pressedTouchIds.insert(touchPoint.id, touchPoint.normalPosition); - - break; - case EMSCRIPTEN_EVENT_TOUCHEND: - touchPoint.state = QEventPoint::State::Released; - m_pressedTouchIds.remove(touchPoint.id); - break; - case EMSCRIPTEN_EVENT_TOUCHMOVE: - touchPoint.state = (stationaryTouchPoint - ? QEventPoint::State::Stationary - : QEventPoint::State::Updated); - - m_pressedTouchIds.insert(touchPoint.id, touchPoint.normalPosition); - break; - default: - break; - } - - touchPointList.append(touchPoint); - } - - QFlags<Qt::KeyboardModifier> keyModifier = KeyboardModifier::getForEvent(*touchEvent); - - bool accepted = false; - - if (eventType == EMSCRIPTEN_EVENT_TOUCHCANCEL) - accepted = QWindowSystemInterface::handleTouchCancelEvent(targetWindow, QWasmIntegration::getTimestamp(), m_touchDevice.get(), keyModifier); - else - accepted = QWindowSystemInterface::handleTouchEvent( - targetWindow, QWasmIntegration::getTimestamp(), m_touchDevice.get(), touchPointList, keyModifier); - - return static_cast<int>(accepted); -} |