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);
}
void QWasmClipboard::installEventHandlers(const QString &canvasId)
void QWasmClipboard::installEventHandlers(const emscripten::val &canvas)
{
if (hasClipboardApi)
return;
// 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"),
val::module_property("qtClipboardCutTo"));
canvas.call<void>("addEventListener", val("copy"),

View File

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

View File

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

View File

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

View File

@ -68,20 +68,17 @@ static void browserBeforeUnload(emscripten::val)
static void addCanvasElement(emscripten::val canvas)
{
QString canvasId = QWasmString::toQString(canvas["id"]);
QWasmIntegration::get()->addScreen(canvasId);
QWasmIntegration::get()->addScreen(canvas);
}
static void removeCanvasElement(emscripten::val canvas)
{
QString canvasId = QWasmString::toQString(canvas["id"]);
QWasmIntegration::get()->removeScreen(canvasId);
QWasmIntegration::get()->removeScreen(canvas);
}
static void resizeCanvasElement(emscripten::val canvas)
{
QString canvasId = QWasmString::toQString(canvas["id"]);
QWasmIntegration::get()->resizeScreen(canvasId);
QWasmIntegration::get()->resizeScreen(canvas);
}
static void qtUpdateDpi()
@ -114,15 +111,12 @@ QWasmIntegration::QWasmIntegration()
if (!qtCanvaseElements.isUndefined()) {
int screenCount = qtCanvaseElements["length"].as<int>();
for (int i = 0; i < screenCount; ++i) {
emscripten::val canvas = qtCanvaseElements[i].as<emscripten::val>();
QString canvasId = QWasmString::toQString(canvas["id"]);
addScreen(canvasId);
addScreen(qtCanvaseElements[i].as<emscripten::val>());
}
} 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.";
QString canvasId = QWasmString::toQString(canvas["id"]);
addScreen(canvasId);
addScreen(canvas);
}
emscripten::val::global("window").set("onbeforeunload", val::module_property("qtBrowserBeforeUnload"));
@ -148,8 +142,8 @@ QWasmIntegration::~QWasmIntegration()
delete m_fontDb;
delete m_desktopServices;
for (auto it = m_screens.constBegin(); it != m_screens.constEnd(); ++it)
QWindowSystemInterface::handleScreenRemoved(*it);
for (const auto &canvasAndScreen : m_screens)
QWindowSystemInterface::handleScreenRemoved(canvasAndScreen.second);
m_screens.clear();
s_instance = nullptr;
@ -271,24 +265,37 @@ QPlatformClipboard* QWasmIntegration::clipboard() const
return m_clipboard;
}
void QWasmIntegration::addScreen(const QString &canvasId)
void QWasmIntegration::addScreen(const emscripten::val &canvas)
{
QWasmScreen *screen = new QWasmScreen(canvasId);
m_clipboard->installEventHandlers(canvasId);
m_screens.insert(canvasId, screen);
QWasmScreen *screen = new QWasmScreen(canvas);
m_screens.append(qMakePair(canvas, screen));
m_clipboard->installEventHandlers(canvas);
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
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()
@ -297,15 +304,14 @@ void QWasmIntegration::updateDpi()
if (dpi.isUndefined())
return;
qreal dpiValue = dpi.as<qreal>();
for (QWasmScreen *screen : m_screens)
QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen->screen(), dpiValue, dpiValue);
for (const auto &canvasAndScreen : m_screens)
QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(canvasAndScreen.second->screen(), dpiValue, dpiValue);
}
void QWasmIntegration::resizeAllScreens()
{
qDebug() << "resizeAllScreens";
for (QWasmScreen *screen : m_screens)
screen->updateQScreenAndCanvasRenderSize();
for (const auto &canvasAndScreen : m_screens)
canvasAndScreen.second->updateQScreenAndCanvasRenderSize();
}
QT_END_NAMESPACE

View File

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

View File

@ -50,15 +50,13 @@ using namespace emscripten;
QT_BEGIN_NAMESPACE
QWasmScreen::QWasmScreen(const QString &canvasId)
: m_canvasId(canvasId)
QWasmScreen::QWasmScreen(const emscripten::val &canvas)
: m_canvas(canvas)
{
m_compositor = new QWasmCompositor(this);
m_eventTranslator = new QWasmEventTranslator(this);
updateQScreenAndCanvasRenderSize();
emscripten::val canvas = emscripten::val::global(m_canvasId.toUtf8().constData());
canvas.call<void>("focus");
m_canvas.call<void>("focus");
}
QWasmScreen::~QWasmScreen()
@ -91,9 +89,14 @@ QWasmEventTranslator *QWasmScreen::eventTranslator()
return m_eventTranslator;
}
emscripten::val QWasmScreen::canvas() const
{
return m_canvas;
}
QString QWasmScreen::canvasId() const
{
return m_canvasId;
return QWasmString::toQString(m_canvas["id"]);
}
QRect QWasmScreen::geometry() const
@ -134,7 +137,7 @@ qreal QWasmScreen::devicePixelRatio() const
QString QWasmScreen::name() const
{
return m_canvasId;
return canvasId();
}
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
// rendering.
QByteArray canvasSelector = "#" + m_canvasId.toUtf8();
QByteArray canvasSelector = "#" + canvasId().toUtf8();
double css_width;
double css_height;
emscripten_get_element_css_size(canvasSelector.constData(), &css_width, &css_height);
QSizeF cssSize(css_width, css_height);
QSizeF canvasSize = cssSize * devicePixelRatio();
val document = val::global("document");
val canvas = document.call<val>("getElementById", QWasmString::fromQString(m_canvasId));
canvas.set("width", canvasSize.width());
canvas.set("height", canvasSize.height());
m_canvas.set("width", canvasSize.width());
m_canvas.set("height", canvasSize.height());
QPoint offset;
offset.setX(canvas["offsetTop"].as<int>());
offset.setY(canvas["offsetLeft"].as<int>());
offset.setX(m_canvas["offsetTop"].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());
setGeometry(QRect(position, cssSize.toSize()));

View File

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