123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458 | // Copyright (C) 2018 The Qt Company Ltd.// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only#include"qwasmintegration.h"#include"qwasmeventdispatcher.h"#include"qwasmcompositor.h"#include"qwasmopenglcontext.h"#include"qwasmtheme.h"#if QT_CONFIG(clipboard)#include"qwasmclipboard.h"#endif#include"qwasmaccessibility.h"#include"qwasmservices.h"#include"qwasmoffscreensurface.h"#include"qwasmplatform.h"#include"qwasmwindow.h"#include"qwasmbackingstore.h"#include"qwasmfontdatabase.h"#if QT_CONFIG(draganddrop)#include"qwasmdrag.h"#endif#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"private/qwasmsuspendresumecontrol_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"#if QT_CONFIG(draganddrop)#include <private/qsimpledrag_p.h>#endif QT_BEGIN_NAMESPACE externvoidqt_set_sequence_auto_mnemonic(bool);using namespace emscripten;using namespaceQt::StringLiterals;static voidsetContainerElements(emscripten::val elementArray){QWasmIntegration::get()->setContainerElements(elementArray);}static voidaddContainerElement(emscripten::val element){QWasmIntegration::get()->addContainerElement(element);}static voidremoveContainerElement(emscripten::val element){QWasmIntegration::get()->removeContainerElement(element);}static voidresizeContainerElement(emscripten::val element){QWasmIntegration::get()->resizeScreen(element);}static voidqtUpdateDpi(){QWasmIntegration::get()->updateDpi();}static voidresizeAllScreens(emscripten::val event){Q_UNUSED(event);QWasmIntegration::get()->resizeAllScreens();}static voidloadLocalFontFamilies(emscripten::val event){QWasmIntegration::get()->loadLocalFontFamilies(event);}EMSCRIPTEN_BINDINGS(qtQWasmIntegraton){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_suspendResume(std::make_shared<QWasmSuspendResumeControl>())// create early in order to register event handlers at startup,m_fontDb(nullptr),m_desktopServices(nullptr)#if QT_CONFIG(clipboard),m_clipboard(new QWasmClipboard)#endif#if QT_CONFIG(accessibility),m_accessibility(new QWasmAccessibility)#endif{ s_instance =this;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{// No screens, which may or may not be intendedqWarning() <<"The qtContainerElements module property was not set or is invalid. ""Proceeding with no screens.";}setContainerElements(filtered);// install browser window resize handleremscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW,nullptr, EM_TRUE,[](int,const EmscriptenUiEvent *,void*) -> EM_BOOL {// 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 EM_FALSE;});// 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"));}#if QT_CONFIG(draganddrop) m_drag =std::make_unique<QWasmDrag>();#endif}QWasmIntegration::~QWasmIntegration(){// Remove event listeneremscripten_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 QT_CONFIG(accessibility)delete m_accessibility;#endiffor(constauto&elementAndScreen : m_screens) elementAndScreen.wasmScreen->deleteScreen(); m_screens.clear(); s_instance =nullptr;}boolQWasmIntegration::hasCapability(QPlatformIntegration::Capability cap)const{switch(cap) {case ThreadedPixmaps:return true;case OpenGL:return true;case ThreadedOpenGL:return false;case RasterGLSurface:return false;// to enable this you need to fix qopenglwidget and quickwidget for wasmcase MultipleWindows:return true;case WindowManagement:return true;case ForeignWindows:return true;case OpenGLOnRasterSurface:return true;default:returnQPlatformIntegration::hasCapability(cap);}} QWasmWindow *QWasmIntegration::createWindow(QWindow *window, WId nativeHandle)const{auto*wasmScreen =QWasmScreen::get(window->screen()); QWasmCompositor *compositor = wasmScreen->compositor();return newQWasmWindow(window, wasmScreen->deadKeySupport(), compositor, m_backingStores.value(window), nativeHandle);} QPlatformWindow *QWasmIntegration::createPlatformWindow(QWindow *window)const{returncreateWindow(window,0);} QPlatformWindow *QWasmIntegration::createForeignWindow(QWindow *window, WId nativeHandle)const{returncreateWindow(window, nativeHandle);} QPlatformBackingStore *QWasmIntegration::createPlatformBackingStore(QWindow *window)const{ QWasmCompositor *compositor =QWasmScreen::get(window->screen())->compositor(); QWasmBackingStore *backingStore =newQWasmBackingStore(compositor, window); m_backingStores.insert(window, backingStore);return backingStore;}voidQWasmIntegration::removeBackingStore(QWindow* window){ m_backingStores.remove(window);}voidQWasmIntegration::releaseRequesetUpdateHold(){if(QWasmCompositor::releaseRequestUpdateHold()){for(constauto&elementAndScreen : m_screens) { elementAndScreen.wasmScreen->compositor()->requestUpdate();}}}#ifndef QT_NO_OPENGL QPlatformOpenGLContext *QWasmIntegration::createPlatformOpenGLContext(QOpenGLContext *context)const{return newQWasmOpenGLContext(context);}#endifvoidQWasmIntegration::initialize(){auto icStrs =QPlatformInputContextFactory::requested();if(!icStrs.isEmpty()) { m_inputContext.reset(QPlatformInputContextFactory::create(icStrs)); m_wasmInputContext =nullptr;}else{ m_inputContext.reset(newQWasmInputContext()); m_wasmInputContext =static_cast<QWasmInputContext *>(m_inputContext.data());}} QPlatformInputContext *QWasmIntegration::inputContext()const{return m_inputContext.data();} QPlatformOffscreenSurface *QWasmIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface)const{return newQWasmOffscreenSurface(surface);} QPlatformFontDatabase *QWasmIntegration::fontDatabase()const{if(m_fontDb ==nullptr) m_fontDb =new QWasmFontDatabase;return m_fontDb;} QAbstractEventDispatcher *QWasmIntegration::createEventDispatcher()const{return newQWasmEventDispatcher(m_suspendResume);} QVariant QWasmIntegration::styleHint(QPlatformIntegration::StyleHint hint)const{switch(hint) {case ShowIsFullScreen:return true;case UnderlineShortcut:returnplatform() !=Platform::MacOS;default:returnQPlatformIntegration::styleHint(hint);}}Qt::WindowState QWasmIntegration::defaultWindowState(Qt::WindowFlags flags)const{// Don't maximize dialogs or popupsif(flags.testFlag(Qt::Dialog) || flags.testFlag(Qt::Popup))returnQt::WindowNoState;returnQPlatformIntegration::defaultWindowState(flags);} QStringList QWasmIntegration::themeNames()const{returnQStringList() <<"webassembly"_L1;} QPlatformTheme *QWasmIntegration::createPlatformTheme(const QString &name)const{if(name =="webassembly"_L1)return new QWasmTheme;returnQPlatformIntegration::createPlatformTheme(name);} QPlatformServices *QWasmIntegration::services()const{if(m_desktopServices ==nullptr) m_desktopServices =newQWasmServices();return m_desktopServices;}#if QT_CONFIG(clipboard) QPlatformClipboard*QWasmIntegration::clipboard()const{return m_clipboard;}#endif#ifndef QT_NO_ACCESSIBILITY QPlatformAccessibility *QWasmIntegration::accessibility()const{return m_accessibility;}#endifvoidQWasmIntegration::setContainerElements(emscripten::val elementArray){constauto*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) {constauto element = elementArray[i];constauto 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 =newQWasmScreen(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);}voidQWasmIntegration::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 =newQWasmScreen(element);QWindowSystemInterface::handleScreenAdded(screen); m_screens.push_back({element, screen});}voidQWasmIntegration::removeContainerElement(emscripten::val element){constauto*primaryScreenBefore = m_screens.isEmpty() ?nullptr: m_screens[0].wasmScreen;constauto it =std::find_if(m_screens.begin(), m_screens.end(),[&element](const ScreenMapping &screen) {return screen.emscriptenVal == element; });if(it == m_screens.end()) {qWarning() <<"Attempt to remove a nonexistent screen.";return;} 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);}voidQWasmIntegration::resizeScreen(constemscripten::val &element){auto it =std::find_if(m_screens.begin(), m_screens.end(),[&] (const ScreenMapping &candidate) {return candidate.emscriptenVal.equals(element); });if(it == m_screens.end()) {qWarning() <<"Attempting to resize non-existing screen for element"<<QString::fromEcmaString(element["id"]);return;} it->wasmScreen->updateQScreenSize();}voidQWasmIntegration::updateDpi(){emscripten::val dpi =emscripten::val::module_property("qtFontDpi");if(dpi.isUndefined())return; qreal dpiValue = dpi.as<qreal>();for(constauto&elementAndScreen : m_screens)QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(elementAndScreen.wasmScreen->screen(), dpiValue, dpiValue);}voidQWasmIntegration::resizeAllScreens(){for(constauto&elementAndScreen : m_screens) elementAndScreen.wasmScreen->updateQScreenSize();}voidQWasmIntegration::loadLocalFontFamilies(emscripten::val families){ m_fontDb->populateLocalFontFamilies(families);} quint64 QWasmIntegration::getTimestamp(){returnemscripten_performance_now();}#if QT_CONFIG(draganddrop) QPlatformDrag *QWasmIntegration::drag()const{return m_drag.get();}#endif// QT_CONFIG(draganddrop) QT_END_NAMESPACE
|