diff options
Diffstat (limited to 'src/plugins/platforms/wasm/qwasmintegration.cpp')
-rw-r--r-- | src/plugins/platforms/wasm/qwasmintegration.cpp | 354 |
1 files changed, 226 insertions, 128 deletions
diff --git a/src/plugins/platforms/wasm/qwasmintegration.cpp b/src/plugins/platforms/wasm/qwasmintegration.cpp index 15d396f479..f5cc3e2eee 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.cpp +++ b/src/plugins/platforms/wasm/qwasmintegration.cpp @@ -1,85 +1,61 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 or (at your option) any later version -** approved by the KDE Free Qt Foundation. The licenses are as published by -** the Free Software Foundation and appearing in the file LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "qwasmintegration.h" -#include "qwasmeventtranslator.h" #include "qwasmeventdispatcher.h" #include "qwasmcompositor.h" #include "qwasmopenglcontext.h" #include "qwasmtheme.h" #include "qwasmclipboard.h" +#include "qwasmaccessibility.h" #include "qwasmservices.h" #include "qwasmoffscreensurface.h" -#include "qwasmstring.h" - +#include "qwasmplatform.h" #include "qwasmwindow.h" -#ifndef QT_NO_OPENGL -# include "qwasmbackingstore.h" -# include <QtOpenGL/qpa/qplatformbackingstoreopenglsupport.h> -#endif +#include "qwasmbackingstore.h" #include "qwasmfontdatabase.h" -#if defined(Q_OS_UNIX) -#include <QtGui/private/qgenericunixeventdispatcher_p.h> -#endif +#include "qwasmdrag.h" + #include <qpa/qplatformwindow.h> #include <QtGui/qscreen.h> #include <qpa/qwindowsysteminterface.h> #include <QtCore/qcoreapplication.h> #include <qpa/qplatforminputcontextfactory_p.h> +#include <qpa/qwindowsysteminterface_p.h> #include <emscripten/bind.h> #include <emscripten/val.h> // this is where EGL headers are pulled in, make sure it is last #include "qwasmscreen.h" +#include <private/qsimpledrag_p.h> -using namespace emscripten; QT_BEGIN_NAMESPACE -static void browserBeforeUnload(emscripten::val) +extern void qt_set_sequence_auto_mnemonic(bool); + +using namespace emscripten; + +using namespace Qt::StringLiterals; + +static void setContainerElements(emscripten::val elementArray) { - QWasmIntegration::QWasmBrowserExit(); + QWasmIntegration::get()->setContainerElements(elementArray); } -static void addCanvasElement(emscripten::val canvas) +static void addContainerElement(emscripten::val element) { - QWasmIntegration::get()->addScreen(canvas); + QWasmIntegration::get()->addContainerElement(element); } -static void removeCanvasElement(emscripten::val canvas) +static void removeContainerElement(emscripten::val element) { - QWasmIntegration::get()->removeScreen(canvas); + QWasmIntegration::get()->removeContainerElement(element); } -static void resizeCanvasElement(emscripten::val canvas) +static void resizeContainerElement(emscripten::val element) { - QWasmIntegration::get()->resizeScreen(canvas); + QWasmIntegration::get()->resizeScreen(element); } static void qtUpdateDpi() @@ -93,83 +69,108 @@ static void resizeAllScreens(emscripten::val event) QWasmIntegration::get()->resizeAllScreens(); } +static void loadLocalFontFamilies(emscripten::val event) +{ + QWasmIntegration::get()->loadLocalFontFamilies(event); +} + EMSCRIPTEN_BINDINGS(qtQWasmIntegraton) { - function("qtBrowserBeforeUnload", &browserBeforeUnload); - function("qtAddCanvasElement", &addCanvasElement); - function("qtRemoveCanvasElement", &removeCanvasElement); - function("qtResizeCanvasElement", &resizeCanvasElement); + function("qtSetContainerElements", &setContainerElements); + function("qtAddContainerElement", &addContainerElement); + function("qtRemoveContainerElement", &removeContainerElement); + function("qtResizeContainerElement", &resizeContainerElement); function("qtUpdateDpi", &qtUpdateDpi); function("qtResizeAllScreens", &resizeAllScreens); + function("qtLoadLocalFontFamilies", &loadLocalFontFamilies); } QWasmIntegration *QWasmIntegration::s_instance; QWasmIntegration::QWasmIntegration() - : m_fontDb(nullptr), - m_desktopServices(nullptr), - m_clipboard(new QWasmClipboard) + : m_fontDb(nullptr) + , m_desktopServices(nullptr) + , m_clipboard(new QWasmClipboard) +#if QT_CONFIG(accessibility) + , m_accessibility(new QWasmAccessibility) +#endif { s_instance = this; - // We expect that qtloader.js has populated Module.qtCanvasElements with one or more canvases. - emscripten::val qtCanvaseElements = val::module_property("qtCanvasElements"); - emscripten::val canvas = val::module_property("canvas"); // TODO: remove for Qt 6.0 - - if (!qtCanvaseElements.isUndefined()) { - int screenCount = qtCanvaseElements["length"].as<int>(); - for (int i = 0; i < screenCount; ++i) { - addScreen(qtCanvaseElements[i].as<emscripten::val>()); + if (platform() == Platform::MacOS) + qt_set_sequence_auto_mnemonic(false); + + touchPoints = emscripten::val::global("navigator")["maxTouchPoints"].as<int>(); + QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false); + + // Create screens for container elements. Each container element will ultimately become a + // div element. Qt historically supported supplying canvas for screen elements - these elements + // will be transformed into divs and warnings about deprecation will be printed. See + // QWasmScreen ctor. + emscripten::val filtered = emscripten::val::array(); + emscripten::val qtContainerElements = val::module_property("qtContainerElements"); + if (qtContainerElements.isArray()) { + for (int i = 0; i < qtContainerElements["length"].as<int>(); ++i) { + emscripten::val element = qtContainerElements[i].as<emscripten::val>(); + if (element.isNull() || element.isUndefined()) + qWarning() << "Skipping null or undefined element in qtContainerElements"; + else + filtered.call<void>("push", element); } - } else if (!canvas.isUndefined()) { - qWarning() << "Module.canvas is deprecated. A future version of Qt will stop reading this property. " - << "Instead, set Module.qtCanvasElements to be an array of canvas elements, or use qtloader.js."; - addScreen(canvas); + } else { + // No screens, which may or may not be intended + qWarning() << "The qtContainerElements module property was not set or is invalid. " + "Proceeding with no screens."; } - - emscripten::val::global("window").set("onbeforeunload", val::module_property("qtBrowserBeforeUnload")); + setContainerElements(filtered); // install browser window resize handler - auto onWindowResize = [](int eventType, const EmscriptenUiEvent *e, void *userData) -> int { - Q_UNUSED(eventType); - Q_UNUSED(e); - Q_UNUSED(userData); - - // This resize event is called when the HTML window is resized. Depending - // on the page layout the canvas(es) might also have been resized, so we - // update the Qt screen sizes (and canvas render sizes). - if (QWasmIntegration *integration = QWasmIntegration::get()) - integration->resizeAllScreens(); - return 0; - }; - emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, 1, onWindowResize); + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_TRUE, + [](int, const EmscriptenUiEvent *, void *) -> int { + // This resize event is called when the HTML window is + // resized. Depending on the page layout the elements might + // also have been resized, so we update the Qt screen sizes + // (and canvas render sizes). + if (QWasmIntegration *integration = QWasmIntegration::get()) + integration->resizeAllScreens(); + return 0; + }); // install visualViewport resize handler which picks up size and scale change on mobile. emscripten::val visualViewport = emscripten::val::global("window")["visualViewport"]; if (!visualViewport.isUndefined()) { visualViewport.call<void>("addEventListener", val("resize"), - val::module_property("qtResizeAllScreens")); + val::module_property("qtResizeAllScreens")); } + m_drag = std::make_unique<QWasmDrag>(); } QWasmIntegration::~QWasmIntegration() { + // Remove event listener + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_TRUE, nullptr); + emscripten::val visualViewport = emscripten::val::global("window")["visualViewport"]; + if (!visualViewport.isUndefined()) { + visualViewport.call<void>("removeEventListener", val("resize"), + val::module_property("qtResizeAllScreens")); + } + delete m_fontDb; delete m_desktopServices; + if (m_platformInputContext) + delete m_platformInputContext; +#if QT_CONFIG(accessibility) + delete m_accessibility; +#endif + + for (const auto &elementAndScreen : m_screens) + elementAndScreen.wasmScreen->deleteScreen(); - for (const auto &canvasAndScreen : m_screens) - QWindowSystemInterface::handleScreenRemoved(canvasAndScreen.second); m_screens.clear(); s_instance = nullptr; } -void QWasmIntegration::QWasmBrowserExit() -{ - QCoreApplication *app = QCoreApplication::instance(); - app->quit(); -} - bool QWasmIntegration::hasCapability(QPlatformIntegration::Capability cap) const { switch (cap) { @@ -186,20 +187,18 @@ bool QWasmIntegration::hasCapability(QPlatformIntegration::Capability cap) const QPlatformWindow *QWasmIntegration::createPlatformWindow(QWindow *window) const { - QWasmCompositor *compositor = QWasmScreen::get(window->screen())->compositor(); - return new QWasmWindow(window, compositor, m_backingStores.value(window)); + auto *wasmScreen = QWasmScreen::get(window->screen()); + QWasmCompositor *compositor = wasmScreen->compositor(); + return new QWasmWindow(window, wasmScreen->deadKeySupport(), compositor, + m_backingStores.value(window)); } QPlatformBackingStore *QWasmIntegration::createPlatformBackingStore(QWindow *window) const { -#ifndef QT_NO_OPENGL QWasmCompositor *compositor = QWasmScreen::get(window->screen())->compositor(); QWasmBackingStore *backingStore = new QWasmBackingStore(compositor, window); m_backingStores.insert(window, backingStore); return backingStore; -#else - return nullptr; -#endif } void QWasmIntegration::removeBackingStore(QWindow* window) @@ -207,18 +206,33 @@ void QWasmIntegration::removeBackingStore(QWindow* window) m_backingStores.remove(window); } +void QWasmIntegration::releaseRequesetUpdateHold() +{ + if (QWasmCompositor::releaseRequestUpdateHold()) + { + for (const auto &elementAndScreen : m_screens) { + elementAndScreen.wasmScreen->compositor()->requestUpdate(); + } + } +} + #ifndef QT_NO_OPENGL QPlatformOpenGLContext *QWasmIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const { - return new QWasmOpenGLContext(context->format()); + return new QWasmOpenGLContext(context); } #endif void QWasmIntegration::initialize() { - QString icStr = QPlatformInputContextFactory::requested(); - if (!icStr.isNull()) - m_inputContext.reset(QPlatformInputContextFactory::create(icStr)); + auto icStrs = QPlatformInputContextFactory::requested(); + if (icStrs.isEmpty() && touchPoints < 1) + return; + + if (!icStrs.isEmpty()) + m_inputContext.reset(QPlatformInputContextFactory::create(icStrs)); + else + m_inputContext.reset(new QWasmInputContext()); } QPlatformInputContext *QWasmIntegration::inputContext() const @@ -228,7 +242,7 @@ QPlatformInputContext *QWasmIntegration::inputContext() const QPlatformOffscreenSurface *QWasmIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const { - return new QWasmOffscrenSurface(surface); + return new QWasmOffscreenSurface(surface); } QPlatformFontDatabase *QWasmIntegration::fontDatabase() const @@ -246,16 +260,20 @@ QAbstractEventDispatcher *QWasmIntegration::createEventDispatcher() const QVariant QWasmIntegration::styleHint(QPlatformIntegration::StyleHint hint) const { - if (hint == ShowIsFullScreen) + switch (hint) { + case ShowIsFullScreen: return true; - - return QPlatformIntegration::styleHint(hint); + case UnderlineShortcut: + return platform() != Platform::MacOS; + default: + return QPlatformIntegration::styleHint(hint); + } } Qt::WindowState QWasmIntegration::defaultWindowState(Qt::WindowFlags flags) const { - // Don't maximize dialogs - if (flags & Qt::Dialog & ~Qt::Window) + // Don't maximize dialogs or popups + if (flags.testFlag(Qt::Dialog) || flags.testFlag(Qt::Popup)) return Qt::WindowNoState; return QPlatformIntegration::defaultWindowState(flags); @@ -263,12 +281,12 @@ Qt::WindowState QWasmIntegration::defaultWindowState(Qt::WindowFlags flags) cons QStringList QWasmIntegration::themeNames() const { - return QStringList() << QLatin1String("webassembly"); + return QStringList() << "webassembly"_L1; } QPlatformTheme *QWasmIntegration::createPlatformTheme(const QString &name) const { - if (name == QLatin1String("webassembly")) + if (name == "webassembly"_L1) return new QWasmTheme; return QPlatformIntegration::createPlatformTheme(name); } @@ -285,37 +303,100 @@ QPlatformClipboard* QWasmIntegration::clipboard() const return m_clipboard; } -void QWasmIntegration::addScreen(const emscripten::val &canvas) +#ifndef QT_NO_ACCESSIBILITY +QPlatformAccessibility *QWasmIntegration::accessibility() const { - QWasmScreen *screen = new QWasmScreen(canvas); - m_screens.append(qMakePair(canvas, screen)); - m_clipboard->installEventHandlers(canvas); + return m_accessibility; +} +#endif + +void QWasmIntegration::setContainerElements(emscripten::val elementArray) +{ + const auto *primaryScreenBefore = m_screens.isEmpty() ? nullptr : m_screens[0].wasmScreen; + QList<ScreenMapping> newScreens; + + QList<QWasmScreen *> screensToDelete; + std::transform(m_screens.begin(), m_screens.end(), std::back_inserter(screensToDelete), + [](const ScreenMapping &mapping) { return mapping.wasmScreen; }); + + for (int i = 0; i < elementArray["length"].as<int>(); ++i) { + const auto element = elementArray[i]; + const auto it = std::find_if( + m_screens.begin(), m_screens.end(), + [&element](const ScreenMapping &screen) { return screen.emscriptenVal == element; }); + QWasmScreen *screen; + if (it != m_screens.end()) { + screen = it->wasmScreen; + screensToDelete.erase(std::remove_if(screensToDelete.begin(), screensToDelete.end(), + [screen](const QWasmScreen *removedScreen) { + return removedScreen == screen; + }), + screensToDelete.end()); + } else { + screen = new QWasmScreen(element); + QWindowSystemInterface::handleScreenAdded(screen); + } + newScreens.push_back({element, screen}); + } + + std::for_each(screensToDelete.begin(), screensToDelete.end(), + [](QWasmScreen *removed) { removed->deleteScreen(); }); + + m_screens = newScreens; + auto *primaryScreenAfter = m_screens.isEmpty() ? nullptr : m_screens[0].wasmScreen; + if (primaryScreenAfter && primaryScreenAfter != primaryScreenBefore) + QWindowSystemInterface::handlePrimaryScreenChanged(primaryScreenAfter); +} + +void QWasmIntegration::addContainerElement(emscripten::val element) +{ + Q_ASSERT_X(m_screens.end() + == std::find_if(m_screens.begin(), m_screens.end(), + [&element](const ScreenMapping &screen) { + return screen.emscriptenVal == element; + }), + Q_FUNC_INFO, "Double-add of an element"); + + QWasmScreen *screen = new QWasmScreen(element); QWindowSystemInterface::handleScreenAdded(screen); + m_screens.push_back({element, screen}); } -void QWasmIntegration::removeScreen(const emscripten::val &canvas) +void QWasmIntegration::removeContainerElement(emscripten::val element) { - auto it = std::find_if(m_screens.begin(), m_screens.end(), - [&] (const QPair<emscripten::val, QWasmScreen *> &candidate) { return candidate.first.equals(canvas); }); + const auto *primaryScreenBefore = m_screens.isEmpty() ? nullptr : m_screens[0].wasmScreen; + + const auto it = + std::find_if(m_screens.begin(), m_screens.end(), + [&element](const ScreenMapping &screen) { return screen.emscriptenVal == element; }); if (it == m_screens.end()) { - qWarning() << "Attempting to remove non-existing screen for canvas" << QWasmString::toQString(canvas["id"]);; + qWarning() << "Attempt to remove a nonexistent screen."; return; } - QWasmScreen *exScreen = it->second; - m_screens.erase(it); - exScreen->destroy(); // clean up before deleting the screen - QWindowSystemInterface::handleScreenRemoved(exScreen); + + QWasmScreen *removedScreen = it->wasmScreen; + removedScreen->deleteScreen(); + + m_screens.erase(std::remove_if(m_screens.begin(), m_screens.end(), + [removedScreen](const ScreenMapping &mapping) { + return removedScreen == mapping.wasmScreen; + }), + m_screens.end()); + auto *primaryScreenAfter = m_screens.isEmpty() ? nullptr : m_screens[0].wasmScreen; + if (primaryScreenAfter && primaryScreenAfter != primaryScreenBefore) + QWindowSystemInterface::handlePrimaryScreenChanged(primaryScreenAfter); } -void QWasmIntegration::resizeScreen(const emscripten::val &canvas) +void QWasmIntegration::resizeScreen(const emscripten::val &element) { auto it = std::find_if(m_screens.begin(), m_screens.end(), - [&] (const QPair<emscripten::val, QWasmScreen *> &candidate) { return candidate.first.equals(canvas); }); + [&] (const ScreenMapping &candidate) { return candidate.emscriptenVal.equals(element); }); if (it == m_screens.end()) { - qWarning() << "Attempting to resize non-existing screen for canvas" << QWasmString::toQString(canvas["id"]);; + qWarning() << "Attempting to resize non-existing screen for element" + << QString::fromEcmaString(element["id"]); return; } - it->second->updateQScreenAndCanvasRenderSize(); + it->wasmScreen->updateQScreenAndCanvasRenderSize(); } void QWasmIntegration::updateDpi() @@ -324,14 +405,31 @@ void QWasmIntegration::updateDpi() if (dpi.isUndefined()) return; qreal dpiValue = dpi.as<qreal>(); - for (const auto &canvasAndScreen : m_screens) - QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(canvasAndScreen.second->screen(), dpiValue, dpiValue); + for (const auto &elementAndScreen : m_screens) + QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(elementAndScreen.wasmScreen->screen(), dpiValue, dpiValue); } void QWasmIntegration::resizeAllScreens() { - for (const auto &canvasAndScreen : m_screens) - canvasAndScreen.second->updateQScreenAndCanvasRenderSize(); + for (const auto &elementAndScreen : m_screens) + elementAndScreen.wasmScreen->updateQScreenAndCanvasRenderSize(); +} + +void QWasmIntegration::loadLocalFontFamilies(emscripten::val families) +{ + m_fontDb->populateLocalFontFamilies(families); +} + +quint64 QWasmIntegration::getTimestamp() +{ + return emscripten_performance_now(); +} + +#if QT_CONFIG(draganddrop) +QPlatformDrag *QWasmIntegration::drag() const +{ + return m_drag.get(); } +#endif // QT_CONFIG(draganddrop) QT_END_NAMESPACE |