wasm: store and pass canvases as emscripten::val

Store and pass canvases as emscripten::val instead of
a QString containing the element id.

This simplifies code which interacts with the canvas
using the emscripten::val API, by removing the need to
look up with getElementById.

The Emscripten C event API does not accept emscripten::val,
and using the element id is still needed here.

emscripten::val does not provide a hash key suitable
for use with QHash, but does provide an equality-compare
in the form of val::equals(). Change the canvas->screen
mapping code to use a QVector instead of a QHash.

Change-Id: I1dbdbbc8fb06bb869031f1500e83ae2d64780a7f
Reviewed-by: Lorn Potter <lorn.potter@gmail.com>
This commit is contained in:
Morten Johan Sørvig 2020-02-02 01:22:24 +01:00
parent e290ebae9f
commit 2b0af50c8b
8 changed files with 63 additions and 59 deletions

View File

@ -192,15 +192,12 @@ void QWasmClipboard::initClipboardEvents()
permissions.call<val>("query", writePermissionsMap); permissions.call<val>("query", writePermissionsMap);
} }
void QWasmClipboard::installEventHandlers(const QString &canvasId) void QWasmClipboard::installEventHandlers(const emscripten::val &canvas)
{ {
if (hasClipboardApi) if (hasClipboardApi)
return; return;
// Fallback path for browsers which do not support direct clipboard access // Fallback path for browsers which do not support direct clipboard access
val document = val::global("document");
val canvas = document.call<val>("getElementById", QWasmString::fromQString(canvasId));
canvas.call<void>("addEventListener", val("cut"), canvas.call<void>("addEventListener", val("cut"),
val::module_property("qtClipboardCutTo")); val::module_property("qtClipboardCutTo"));
canvas.call<void>("addEventListener", val("copy"), canvas.call<void>("addEventListener", val("copy"),

View File

@ -36,6 +36,7 @@
#include <QMimeData> #include <QMimeData>
#include <emscripten/bind.h> #include <emscripten/bind.h>
#include <emscripten/val.h>
class QWasmClipboard : public QObject, public QPlatformClipboard class QWasmClipboard : public QObject, public QPlatformClipboard
{ {
@ -51,7 +52,7 @@ public:
static void qWasmClipboardPaste(QMimeData *mData); static void qWasmClipboardPaste(QMimeData *mData);
void initClipboardEvents(); void initClipboardEvents();
void installEventHandlers(const QString &canvasId); void installEventHandlers(const emscripten::val &canvas);
bool hasClipboardApi; bool hasClipboardApi;
void readTextFromClipboard(); void readTextFromClipboard();
void writeTextToClipboard(); void writeTextToClipboard();

View File

@ -57,9 +57,7 @@ void QWasmCursor::changeCursor(QCursor *windowCursor, QWindow *window)
htmlCursorName = "auto"; htmlCursorName = "auto";
// Set cursor on the canvas // Set cursor on the canvas
val jsCanvasId = QWasmString::fromQString(QWasmScreen::get(screen)->canvasId()); val canvas = QWasmScreen::get(screen)->canvas();
val document = val::global("document");
val canvas = document.call<val>("getElementById", jsCanvasId);
val canvasStyle = canvas["style"]; val canvasStyle = canvas["style"];
canvasStyle.set("cursor", val(htmlCursorName.constData())); canvasStyle.set("cursor", val(htmlCursorName.constData()));
} }

View File

@ -354,9 +354,7 @@ void QWasmEventTranslator::initEventHandlers()
g_useNaturalScrolling = false; // make this !default on macOS g_useNaturalScrolling = false; // make this !default on macOS
if (emscripten::val::global("window")["safari"].isUndefined()) { if (emscripten::val::global("window")["safari"].isUndefined()) {
val document = val::global("document"); val canvas = screen()->canvas();
val jsCanvasId = QWasmString::fromQString(screen()->canvasId());
val canvas = document.call<val>("getElementById", jsCanvasId);
canvas.call<void>("addEventListener", canvas.call<void>("addEventListener",
val("wheel"), val("wheel"),
val::module_property("qtMouseWheelEvent")); val::module_property("qtMouseWheelEvent"));

View File

@ -68,20 +68,17 @@ static void browserBeforeUnload(emscripten::val)
static void addCanvasElement(emscripten::val canvas) static void addCanvasElement(emscripten::val canvas)
{ {
QString canvasId = QWasmString::toQString(canvas["id"]); QWasmIntegration::get()->addScreen(canvas);
QWasmIntegration::get()->addScreen(canvasId);
} }
static void removeCanvasElement(emscripten::val canvas) static void removeCanvasElement(emscripten::val canvas)
{ {
QString canvasId = QWasmString::toQString(canvas["id"]); QWasmIntegration::get()->removeScreen(canvas);
QWasmIntegration::get()->removeScreen(canvasId);
} }
static void resizeCanvasElement(emscripten::val canvas) static void resizeCanvasElement(emscripten::val canvas)
{ {
QString canvasId = QWasmString::toQString(canvas["id"]); QWasmIntegration::get()->resizeScreen(canvas);
QWasmIntegration::get()->resizeScreen(canvasId);
} }
static void qtUpdateDpi() static void qtUpdateDpi()
@ -114,15 +111,12 @@ QWasmIntegration::QWasmIntegration()
if (!qtCanvaseElements.isUndefined()) { if (!qtCanvaseElements.isUndefined()) {
int screenCount = qtCanvaseElements["length"].as<int>(); int screenCount = qtCanvaseElements["length"].as<int>();
for (int i = 0; i < screenCount; ++i) { for (int i = 0; i < screenCount; ++i) {
emscripten::val canvas = qtCanvaseElements[i].as<emscripten::val>(); addScreen(qtCanvaseElements[i].as<emscripten::val>());
QString canvasId = QWasmString::toQString(canvas["id"]);
addScreen(canvasId);
} }
} else if (!canvas.isUndefined()) { } else if (!canvas.isUndefined()) {
qWarning() << "Module.canvas is deprecated. A future version of Qt will stop reading this property. " 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."; << "Instead, set Module.qtCanvasElements to be an array of canvas elements, or use qtloader.js.";
QString canvasId = QWasmString::toQString(canvas["id"]); addScreen(canvas);
addScreen(canvasId);
} }
emscripten::val::global("window").set("onbeforeunload", val::module_property("qtBrowserBeforeUnload")); emscripten::val::global("window").set("onbeforeunload", val::module_property("qtBrowserBeforeUnload"));
@ -148,8 +142,8 @@ QWasmIntegration::~QWasmIntegration()
delete m_fontDb; delete m_fontDb;
delete m_desktopServices; delete m_desktopServices;
for (auto it = m_screens.constBegin(); it != m_screens.constEnd(); ++it) for (const auto &canvasAndScreen : m_screens)
QWindowSystemInterface::handleScreenRemoved(*it); QWindowSystemInterface::handleScreenRemoved(canvasAndScreen.second);
m_screens.clear(); m_screens.clear();
s_instance = nullptr; s_instance = nullptr;
@ -271,24 +265,37 @@ QPlatformClipboard* QWasmIntegration::clipboard() const
return m_clipboard; return m_clipboard;
} }
void QWasmIntegration::addScreen(const QString &canvasId) void QWasmIntegration::addScreen(const emscripten::val &canvas)
{ {
QWasmScreen *screen = new QWasmScreen(canvasId); QWasmScreen *screen = new QWasmScreen(canvas);
m_clipboard->installEventHandlers(canvasId); m_screens.append(qMakePair(canvas, screen));
m_screens.insert(canvasId, screen); m_clipboard->installEventHandlers(canvas);
QWindowSystemInterface::handleScreenAdded(screen); QWindowSystemInterface::handleScreenAdded(screen);
} }
void QWasmIntegration::removeScreen(const QString &canvasId) void QWasmIntegration::removeScreen(const emscripten::val &canvas)
{ {
QWasmScreen *exScreen = m_screens.take(canvasId); auto it = std::find_if(m_screens.begin(), m_screens.end(),
[&] (const QPair<emscripten::val, QWasmScreen *> &candidate) { return candidate.first.equals(canvas); });
if (it == m_screens.end()) {
qWarning() << "Attempting to remove non-existing screen for canvas" << QWasmString::toQString(canvas["id"]);;
return;
}
QWasmScreen *exScreen = it->second;
m_screens.erase(it);
exScreen->destroy(); // clean up before deleting the screen exScreen->destroy(); // clean up before deleting the screen
QWindowSystemInterface::handleScreenRemoved(exScreen); QWindowSystemInterface::handleScreenRemoved(exScreen);
} }
void QWasmIntegration::resizeScreen(const QString &canvasId) void QWasmIntegration::resizeScreen(const emscripten::val &canvas)
{ {
m_screens.value(canvasId)->updateQScreenAndCanvasRenderSize(); auto it = std::find_if(m_screens.begin(), m_screens.end(),
[&] (const QPair<emscripten::val, QWasmScreen *> &candidate) { return candidate.first.equals(canvas); });
if (it == m_screens.end()) {
qWarning() << "Attempting to resize non-existing screen for canvas" << QWasmString::toQString(canvas["id"]);;
return;
}
it->second->updateQScreenAndCanvasRenderSize();
} }
void QWasmIntegration::updateDpi() void QWasmIntegration::updateDpi()
@ -297,15 +304,14 @@ void QWasmIntegration::updateDpi()
if (dpi.isUndefined()) if (dpi.isUndefined())
return; return;
qreal dpiValue = dpi.as<qreal>(); qreal dpiValue = dpi.as<qreal>();
for (QWasmScreen *screen : m_screens) for (const auto &canvasAndScreen : m_screens)
QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen->screen(), dpiValue, dpiValue); QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(canvasAndScreen.second->screen(), dpiValue, dpiValue);
} }
void QWasmIntegration::resizeAllScreens() void QWasmIntegration::resizeAllScreens()
{ {
qDebug() << "resizeAllScreens"; for (const auto &canvasAndScreen : m_screens)
for (QWasmScreen *screen : m_screens) canvasAndScreen.second->updateQScreenAndCanvasRenderSize();
screen->updateQScreenAndCanvasRenderSize();
} }
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -40,6 +40,7 @@
#include <emscripten.h> #include <emscripten.h>
#include <emscripten/html5.h> #include <emscripten/html5.h>
#include <emscripten/val.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -83,9 +84,9 @@ public:
static QWasmIntegration *get() { return s_instance; } static QWasmIntegration *get() { return s_instance; }
static void QWasmBrowserExit(); static void QWasmBrowserExit();
void addScreen(const QString &canvasId); void addScreen(const emscripten::val &canvas);
void removeScreen(const QString &canvasId); void removeScreen(const emscripten::val &canvas);
void resizeScreen(const QString &canvasId); void resizeScreen(const emscripten::val &canvas);
void resizeAllScreens(); void resizeAllScreens();
void updateDpi(); void updateDpi();
@ -93,8 +94,7 @@ private:
mutable QWasmFontDatabase *m_fontDb; mutable QWasmFontDatabase *m_fontDb;
mutable QWasmServices *m_desktopServices; mutable QWasmServices *m_desktopServices;
mutable QHash<QWindow *, QWasmBackingStore *> m_backingStores; mutable QHash<QWindow *, QWasmBackingStore *> m_backingStores;
QVector<QPair<emscripten::val, QWasmScreen *>> m_screens;
QHash<QString, QWasmScreen *> m_screens;
mutable QWasmClipboard *m_clipboard; mutable QWasmClipboard *m_clipboard;
qreal m_fontDpi = -1; qreal m_fontDpi = -1;
mutable QScopedPointer<QPlatformInputContext> m_inputContext; mutable QScopedPointer<QPlatformInputContext> m_inputContext;

View File

@ -50,15 +50,13 @@ using namespace emscripten;
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
QWasmScreen::QWasmScreen(const QString &canvasId) QWasmScreen::QWasmScreen(const emscripten::val &canvas)
: m_canvasId(canvasId) : m_canvas(canvas)
{ {
m_compositor = new QWasmCompositor(this); m_compositor = new QWasmCompositor(this);
m_eventTranslator = new QWasmEventTranslator(this); m_eventTranslator = new QWasmEventTranslator(this);
updateQScreenAndCanvasRenderSize(); updateQScreenAndCanvasRenderSize();
emscripten::val canvas = emscripten::val::global(m_canvasId.toUtf8().constData()); m_canvas.call<void>("focus");
canvas.call<void>("focus");
} }
QWasmScreen::~QWasmScreen() QWasmScreen::~QWasmScreen()
@ -91,9 +89,14 @@ QWasmEventTranslator *QWasmScreen::eventTranslator()
return m_eventTranslator; return m_eventTranslator;
} }
emscripten::val QWasmScreen::canvas() const
{
return m_canvas;
}
QString QWasmScreen::canvasId() const QString QWasmScreen::canvasId() const
{ {
return m_canvasId; return QWasmString::toQString(m_canvas["id"]);
} }
QRect QWasmScreen::geometry() const QRect QWasmScreen::geometry() const
@ -134,7 +137,7 @@ qreal QWasmScreen::devicePixelRatio() const
QString QWasmScreen::name() const QString QWasmScreen::name() const
{ {
return m_canvasId; return canvasId();
} }
QPlatformCursor *QWasmScreen::cursor() const QPlatformCursor *QWasmScreen::cursor() const
@ -180,24 +183,22 @@ void QWasmScreen::updateQScreenAndCanvasRenderSize()
// Setting the render size to a value larger than the CSS size enables high-dpi // Setting the render size to a value larger than the CSS size enables high-dpi
// rendering. // rendering.
QByteArray canvasSelector = "#" + m_canvasId.toUtf8(); QByteArray canvasSelector = "#" + canvasId().toUtf8();
double css_width; double css_width;
double css_height; double css_height;
emscripten_get_element_css_size(canvasSelector.constData(), &css_width, &css_height); emscripten_get_element_css_size(canvasSelector.constData(), &css_width, &css_height);
QSizeF cssSize(css_width, css_height); QSizeF cssSize(css_width, css_height);
QSizeF canvasSize = cssSize * devicePixelRatio(); QSizeF canvasSize = cssSize * devicePixelRatio();
val document = val::global("document");
val canvas = document.call<val>("getElementById", QWasmString::fromQString(m_canvasId));
canvas.set("width", canvasSize.width()); m_canvas.set("width", canvasSize.width());
canvas.set("height", canvasSize.height()); m_canvas.set("height", canvasSize.height());
QPoint offset; QPoint offset;
offset.setX(canvas["offsetTop"].as<int>()); offset.setX(m_canvas["offsetTop"].as<int>());
offset.setY(canvas["offsetLeft"].as<int>()); offset.setY(m_canvas["offsetLeft"].as<int>());
emscripten::val rect = canvas.call<emscripten::val>("getBoundingClientRect"); emscripten::val rect = m_canvas.call<emscripten::val>("getBoundingClientRect");
QPoint position(rect["left"].as<int>() - offset.x(), rect["top"].as<int>() - offset.y()); QPoint position(rect["left"].as<int>() - offset.x(), rect["top"].as<int>() - offset.y());
setGeometry(QRect(position, cssSize.toSize())); setGeometry(QRect(position, cssSize.toSize()));

View File

@ -37,6 +37,8 @@
#include <QtCore/qscopedpointer.h> #include <QtCore/qscopedpointer.h>
#include <QtCore/qtextstream.h> #include <QtCore/qtextstream.h>
#include <emscripten/val.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QPlatformOpenGLContext; class QPlatformOpenGLContext;
@ -50,12 +52,13 @@ class QWasmScreen : public QObject, public QPlatformScreen
{ {
Q_OBJECT Q_OBJECT
public: public:
QWasmScreen(const QString &canvasId); QWasmScreen(const emscripten::val &canvas);
~QWasmScreen(); ~QWasmScreen();
void destroy(); void destroy();
static QWasmScreen *get(QPlatformScreen *screen); static QWasmScreen *get(QPlatformScreen *screen);
static QWasmScreen *get(QScreen *screen); static QWasmScreen *get(QScreen *screen);
emscripten::val canvas() const;
QString canvasId() const; QString canvasId() const;
QWasmCompositor *compositor(); QWasmCompositor *compositor();
@ -80,7 +83,7 @@ public slots:
void setGeometry(const QRect &rect); void setGeometry(const QRect &rect);
private: private:
QString m_canvasId; emscripten::val m_canvas;
QWasmCompositor *m_compositor = nullptr; QWasmCompositor *m_compositor = nullptr;
QWasmEventTranslator *m_eventTranslator = nullptr; QWasmEventTranslator *m_eventTranslator = nullptr;
QRect m_geometry = QRect(0, 0, 100, 100); QRect m_geometry = QRect(0, 0, 100, 100);