wasm: support rendering to multiple canvases

Qt (via the the qtloader.js API) now supports rendering
to multiple canvases. The application sees each canvas
as a QScreen.

Make qtloader.js support multiple canvases:

  var qtloader = QtLoader({
    canvasElements : [array-of-canvas],
    showCanvas: function() {
      // make canvas(es) visible
    },
 });

The canvases were previously created/returned by showCanvas(),
however this function is called after the Qt app has
been started and adding screens that that point is
too late. (This worked before since there was only one
screen, and no need to connect each screen instance
to specific canvas.)

Remove QWasmScreen, QWasmCompositor, and QWasmEventTranslator
singletons from QWasmIntegration. These are are now
crated per-screen and are owned by the QWasmScreen.

Task-number: QTBUG-64079
Change-Id: I24689929fd5bfb7ff0ba076f66937728fa4bc4e4
Reviewed-by: Lorn Potter <lorn.potter@gmail.com>
This commit is contained in:
Morten Johan Sørvig 2019-02-15 22:04:09 +01:00
parent 452c644c5c
commit caa74f16d4
14 changed files with 221 additions and 153 deletions

View File

@ -50,6 +50,7 @@
// External mode.usage:
//
// var config = {
// canvasElements : [$("canvas-id")],
// showLoader: function() {
// loader.style.display = 'block'
// canvas.style.display = 'hidden'
@ -69,6 +70,8 @@
// One or more HTML elements. QtLoader will display loader elements
// on these while loading the applicaton, and replace the loader with a
// canvas on load complete.
// canvasElements : [canvas-element, ...]
// One or more canvas elements.
// showLoader : function(status, containerElement)
// Optional loading element constructor function. Implement to create
// a custom loading screen. This function may be called multiple times,
@ -146,8 +149,25 @@ function QtLoader(config)
while (element.firstChild) element.removeChild(element.firstChild);
}
// Set default state handler functions if needed
function createCanvas() {
var canvas = document.createElement("canvas");
canvas.className = "QtCanvas";
canvas.style.height = "100%";
canvas.style.width = "100%";
// Set contentEditable in order to enable clipboard events; hide the resulting focus frame.
canvas.contentEditable = true;
canvas.style.outline = "0px solid transparent";
canvas.style.cursor = "default";
return canvas;
}
// Set default state handler functions and create canvases if needed
if (config.containerElements !== undefined) {
config.canvasElements = config.containerElements.map(createCanvas);
config.showError = config.showError || function(errorText, container) {
removeChildren(container);
var errorTextElement = document.createElement("text");
@ -164,19 +184,8 @@ function QtLoader(config)
return loadingText;
};
config.showCanvas = config.showCanvas || function(container) {
config.showCanvas = config.showCanvas || function(canvas, container) {
removeChildren(container);
var canvas = document.createElement("canvas");
canvas.className = "QtCanvas"
canvas.style.height = "100%"
canvas.style.width = "100%"
// Set contentEditable in order to enable clipboard events; hide the resulting focus frame.
canvas.contentEditable = true;
canvas.style.outline = "0px solid transparent";
canvas.style.cursor = "default";
return canvas;
}
config.showExit = config.showExit || function(crashed, exitCode, container) {
@ -391,6 +400,8 @@ function QtLoader(config)
Module.mainScriptUrlOrBlob = new Blob([emscriptenModuleSource], {type: 'text/javascript'});
Module.qtCanvasElements = config.canvasElements;
config.restart = function() {
// Restart by reloading the page. This will wipe all state which means
@ -445,19 +456,17 @@ function QtLoader(config)
}
function setCanvasContent() {
var firstCanvas;
if (config.containerElements === undefined) {
firstCanvas = config.showCanvas();
} else {
for (container of config.containerElements) {
var canvasElement = config.showCanvas(container);
container.appendChild(canvasElement);
}
firstCanvas = config.containerElements[0].firstChild;
if (config.showCanvas !== undefined)
config.showCanvas();
return;
}
if (Module.canvas === undefined) {
Module.canvas = firstCanvas;
for (var i = 0; i < config.containerElements.length; ++i) {
var container = config.containerElements[i];
var canvas = config.canvasElements[i];
config.showCanvas(canvas, container);
container.appendChild(canvas);
}
}

View File

@ -123,9 +123,11 @@ EMSCRIPTEN_BINDINGS(clipboard_module) {
function("qClipboardPasteTo", &qClipboardPasteTo);
}
QWasmClipboard::QWasmClipboard() :
hasClipboardApi(false)
QWasmClipboard::QWasmClipboard()
{
val clipboard = val::global("navigator")["clipboard"];
hasClipboardApi = (!clipboard.isUndefined() && !clipboard["readText"].isUndefined());
initClipboardEvents();
}
@ -177,29 +179,32 @@ void QWasmClipboard::qWasmClipboardPaste(QMimeData *mData)
void QWasmClipboard::initClipboardEvents()
{
val navigator = val::global("navigator");
val permissions = navigator["permissions"];
val clipboard = navigator["clipboard"];
if (!hasClipboardApi)
return;
hasClipboardApi = (!clipboard.isUndefined() && !clipboard["readText"].isUndefined());
if (hasClipboardApi) {
val readPermissionsMap = val::object();
readPermissionsMap.set("name", val("clipboard-read"));
permissions.call<val>("query", readPermissionsMap);
val permissions = val::global("navigator")["permissions"];
val readPermissionsMap = val::object();
readPermissionsMap.set("name", val("clipboard-read"));
permissions.call<val>("query", readPermissionsMap);
val writePermissionsMap = val::object();
writePermissionsMap.set("name", val("clipboard-write"));
permissions.call<val>("query", writePermissionsMap);
val writePermissionsMap = val::object();
writePermissionsMap.set("name", val("clipboard-write"));
permissions.call<val>("query", writePermissionsMap);
}
} else {
val canvas = val::module_property("canvas");
canvas.call<void>("addEventListener", std::string("cut"),
val::module_property("qClipboardCutTo"));
canvas.call<void>("addEventListener", std::string("copy"),
val::module_property("qClipboardCopyTo"));
canvas.call<void>("addEventListener", std::string("paste"),
val::module_property("qClipboardPasteTo"));
}
void QWasmClipboard::installEventHandlers(const QString &canvasId)
{
if (hasClipboardApi)
return;
// Fallback path for browsers which do not support direct clipboard access
val canvas = val::global(canvasId.toUtf8().constData());
canvas.call<void>("addEventListener", std::string("cut"),
val::module_property("qClipboardCutTo"));
canvas.call<void>("addEventListener", std::string("copy"),
val::module_property("qClipboardCopyTo"));
canvas.call<void>("addEventListener", std::string("paste"),
val::module_property("qClipboardPasteTo"));
}
void QWasmClipboard::readTextFromClipboard()

View File

@ -51,6 +51,7 @@ public:
static void qWasmClipboardPaste(QMimeData *mData);
void initClipboardEvents();
void installEventHandlers(const QString &canvasId);
bool hasClipboardApi;
void readTextFromClipboard();
void writeTextToClipboard();

View File

@ -56,8 +56,9 @@ QWasmCompositedWindow::QWasmCompositedWindow()
{
}
QWasmCompositor::QWasmCompositor()
: m_frameBuffer(nullptr)
QWasmCompositor::QWasmCompositor(QWasmScreen *screen)
:QObject(screen)
, m_frameBuffer(nullptr)
, m_blitter(new QOpenGLTextureBlitter)
, m_needComposit(false)
, m_inFlush(false)
@ -107,11 +108,6 @@ void QWasmCompositor::removeWindow(QWasmWindow *window)
notifyTopWindowChanged(window);
}
void QWasmCompositor::setScreen(QWasmScreen *screen)
{
m_screen = screen;
}
void QWasmCompositor::setVisible(QWasmWindow *window, bool visible)
{
QWasmCompositedWindow &compositedWindow = m_compositedWindows[window];
@ -654,7 +650,7 @@ void QWasmCompositor::frame()
m_needComposit = false;
if (m_windowStack.empty() || !m_screen)
if (m_windowStack.empty() || !screen())
return;
QWasmWindow *someWindow = nullptr;
@ -673,7 +669,7 @@ void QWasmCompositor::frame()
if (m_context.isNull()) {
m_context.reset(new QOpenGLContext());
//mContext->setFormat(mScreen->format());
m_context->setScreen(m_screen->screen());
m_context->setScreen(screen()->screen());
m_context->create();
}
@ -682,8 +678,8 @@ void QWasmCompositor::frame()
if (!m_blitter->isCreated())
m_blitter->create();
qreal dpr = m_screen->devicePixelRatio();
glViewport(0, 0, m_screen->geometry().width() * dpr, m_screen->geometry().height() * dpr);
qreal dpr = screen()->devicePixelRatio();
glViewport(0, 0, screen()->geometry().width() * dpr, screen()->geometry().height() * dpr);
m_context->functions()->glClearColor(0.2, 0.2, 0.2, 1.0);
m_context->functions()->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
@ -697,7 +693,7 @@ void QWasmCompositor::frame()
if (!compositedWindow.visible)
continue;
drawWindow(m_blitter.data(), m_screen, window);
drawWindow(m_blitter.data(), screen(), window);
}
m_blitter->release();
@ -719,3 +715,8 @@ void QWasmCompositor::notifyTopWindowChanged(QWasmWindow *window)
requestRedraw();
QWindowSystemInterface::handleWindowActivated(window->window());
}
QWasmScreen *QWasmCompositor::screen()
{
return static_cast<QWasmScreen *>(parent());
}

View File

@ -62,7 +62,7 @@ class QWasmCompositor : public QObject
{
Q_OBJECT
public:
QWasmCompositor();
QWasmCompositor(QWasmScreen *screen);
~QWasmCompositor();
enum QWasmSubControl {
@ -103,7 +103,6 @@ public:
void addWindow(QWasmWindow *window, QWasmWindow *parentWindow = nullptr);
void removeWindow(QWasmWindow *window);
void setScreen(QWasmScreen *screen);
void setVisible(QWasmWindow *window, bool visible);
void raise(QWasmWindow *window);
@ -129,8 +128,7 @@ private slots:
void frame();
private:
void createFrameBuffer();
void flushCompletedCallback(int32_t);
QWasmScreen *screen();
void notifyTopWindowChanged(QWasmWindow *window);
void drawWindow(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window);
void drawWindowContent(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window);
@ -142,7 +140,6 @@ private:
QImage *m_frameBuffer;
QScopedPointer<QOpenGLContext> m_context;
QScopedPointer<QOpenGLTextureBlitter> m_blitter;
QWasmScreen *m_screen;
QHash<QWasmWindow *, QWasmCompositedWindow> m_compositedWindows;
QList<QWasmWindow *> m_windowStack;

View File

@ -28,20 +28,21 @@
****************************************************************************/
#include "qwasmcursor.h"
#include "qwasmscreen.h"
#include <QtCore/qdebug.h>
#include <QtGui/qwindow.h>
#include <emscripten/emscripten.h>
#include <emscripten/bind.h>
void QWasmCursor::changeCursor(QCursor *windowCursor, QWindow *window)
{
if (windowCursor == nullptr)
if (!windowCursor || !window)
return;
QScreen *screen = window->screen();
if (!screen)
return;
// FIXME: The HTML5 plugin sets the cursor on the native canvas; when using multiple windows
// multiple cursors need to be managed taking mouse postion and stacking into account.
Q_UNUSED(window);
// Bitmap and custom cursors are not implemented (will fall back to "auto")
if (windowCursor->shape() == Qt::BitmapCursor || windowCursor->shape() >= Qt::CustomCursor)
@ -52,8 +53,9 @@ void QWasmCursor::changeCursor(QCursor *windowCursor, QWindow *window)
if (htmlCursorName.isEmpty())
htmlCursorName = "auto";
// Set cursor on the main canvas
emscripten::val canvasStyle = emscripten::val::module_property("canvas")["style"];
// Set cursor on the canvas
QString canvasId = QWasmScreen::get(screen)->canvasId();
emscripten::val canvasStyle = emscripten::val::global(canvasId.toUtf8().constData())["style"];
canvasStyle.set("cursor", emscripten::val(htmlCursorName.constData()));
}

View File

@ -320,8 +320,8 @@ EMSCRIPTEN_BINDINGS(mouse_module) {
function("mouseWheelEvent", &mouseWheelEvent);
}
QWasmEventTranslator::QWasmEventTranslator(QObject *parent)
: QObject(parent)
QWasmEventTranslator::QWasmEventTranslator(QWasmScreen *screen)
: QObject(screen)
, draggedWindow(nullptr)
, lastWindow(nullptr)
, pressedButtons(Qt::NoButton)
@ -332,13 +332,16 @@ QWasmEventTranslator::QWasmEventTranslator(QObject *parent)
touchDevice->setCapabilities(QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::NormalizedPosition);
QWindowSystemInterface::registerTouchDevice(touchDevice);
QWasmScreen *wasmScreen = QWasmIntegration::get()->screen();
initEventHandlers(wasmScreen->m_canvasId);
initEventHandlers();
}
void QWasmEventTranslator::initEventHandlers(const QString &canvas)
void QWasmEventTranslator::initEventHandlers()
{
const char *canvasId = canvas.toLocal8Bit().constData();
qDebug() << "QWasmEventTranslator::initEventHandlers";
QByteArray _canvasId = screen()->canvasId().toUtf8();
const char *canvasId = _canvasId.constData();
// The Platform Detect: expand coverage and move as needed
enum Platform {
GenericPlatform,
@ -375,7 +378,7 @@ void QWasmEventTranslator::initEventHandlers(const QString &canvas)
emscripten_set_touchmove_callback(canvasId, (void *)this, 1, &touchCallback);
emscripten_set_touchcancel_callback(canvasId, (void *)this, 1, &touchCallback);
emscripten_set_resize_callback(canvasId, (void *)this, 1, uiEvent_cb);
emscripten_set_resize_callback(nullptr, (void *)this, 1, uiEvent_cb); // Note: handles browser window resize
}
@ -425,6 +428,11 @@ int QWasmEventTranslator::keyboard_cb(int eventType, const EmscriptenKeyboardEve
return accepted ? 1 : 0;
}
QWasmScreen *QWasmEventTranslator::screen()
{
return static_cast<QWasmScreen *>(parent());
}
Qt::Key QWasmEventTranslator::translateEmscriptKey(const EmscriptenKeyboardEvent *emscriptKey)
{
Qt::Key qtKey = Qt::Key_unknown;
@ -541,14 +549,14 @@ void resizeWindow(QWindow *window, QWasmWindow::ResizeMode mode,
void QWasmEventTranslator::processMouse(int eventType, const EmscriptenMouseEvent *mouseEvent)
{
auto timestamp = mouseEvent->timestamp;
QPoint point(mouseEvent->canvasX, mouseEvent->canvasY);
QPoint point(mouseEvent->targetX, mouseEvent->targetY);
QEvent::Type buttonEventType = QEvent::None;
Qt::MouseButton button = translateMouseButton(mouseEvent->button);
Qt::KeyboardModifiers modifiers = translateMouseEventModifier(mouseEvent);
QWindow *window2 = QWasmIntegration::get()->compositor()->windowAt(point, 5);
QWindow *window2 = screen()->compositor()->windowAt(point, 5);
if (window2 != nullptr)
lastWindow = window2;
@ -645,6 +653,7 @@ int QWasmEventTranslator::wheel_cb(int eventType, const EmscriptenWheelEvent *wh
{
Q_UNUSED(eventType)
QWasmEventTranslator *eventTranslator = static_cast<QWasmEventTranslator *>(userData);
EmscriptenMouseEvent mouseEvent = wheelEvent->mouse;
int scrollFactor = 0;
@ -668,7 +677,7 @@ int QWasmEventTranslator::wheel_cb(int eventType, const EmscriptenWheelEvent *wh
auto timestamp = mouseEvent.timestamp;
QPoint globalPoint(mouseEvent.canvasX, mouseEvent.canvasY);
QWindow *window2 = QWasmIntegration::get()->compositor()->windowAt(globalPoint, 5);
QWindow *window2 = eventTranslator->screen()->compositor()->windowAt(globalPoint, 5);
QPoint localPoint(globalPoint.x() - window2->geometry().x(), globalPoint.y() - window2->geometry().y());
@ -686,6 +695,7 @@ int QWasmEventTranslator::wheel_cb(int eventType, const EmscriptenWheelEvent *wh
int QWasmEventTranslator::touchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)
{
QWasmEventTranslator *eventTranslator = static_cast<QWasmEventTranslator *>(userData);
QList<QWindowSystemInterface::TouchPoint> touchPointList;
touchPointList.reserve(touchEvent->numTouches);
QWindow *window2;
@ -695,7 +705,7 @@ int QWasmEventTranslator::touchCallback(int eventType, const EmscriptenTouchEven
const EmscriptenTouchPoint *touches = &touchEvent->touches[i];
QPoint point(touches->canvasX, touches->canvasY);
window2 = QWasmIntegration::get()->compositor()->windowAt(point, 5);
window2 = eventTranslator->screen()->compositor()->windowAt(point, 5);
QWindowSystemInterface::TouchPoint touchPoint;
@ -878,15 +888,13 @@ bool QWasmEventTranslator::processKeyboard(int eventType, const EmscriptenKeyboa
int QWasmEventTranslator::uiEvent_cb(int eventType, const EmscriptenUiEvent *e, void *userData)
{
Q_UNUSED(e)
Q_UNUSED(userData)
QWasmEventTranslator *eventTranslator = static_cast<QWasmEventTranslator *>(userData);
if (eventType == EMSCRIPTEN_EVENT_RESIZE) {
// This resize event is called when the HTML window is resized. Depending
// on the page layout the the canvas might also have been resized, so we
// update the Qt screen size (and canvas render size).
QWasmScreen *wasmScreen = QWasmIntegration::get()->screen();
wasmScreen->updateQScreenAndCanvasRenderSize(wasmScreen->m_canvasId.toLocal8Bit().constData());
eventTranslator->screen()->updateQScreenAndCanvasRenderSize();
}
return 0;

View File

@ -48,7 +48,7 @@ class QWasmEventTranslator : public QObject
public:
explicit QWasmEventTranslator(QObject *parent = 0);
explicit QWasmEventTranslator(QWasmScreen *screen);
static int keyboard_cb(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData);
static int mouse_cb(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData);
@ -60,12 +60,12 @@ public:
static int uiEvent_cb(int eventType, const EmscriptenUiEvent *e, void *userData);
void processEvents();
void initEventHandlers(const QString &);
void initEventHandlers();
Q_SIGNALS:
void getWindowAt(const QPoint &point, QWindow **window);
private:
QWasmScreen *screen();
Qt::Key translateEmscriptKey(const EmscriptenKeyboardEvent *emscriptKey);
template <typename Event>
QFlags<Qt::KeyboardModifier> translatKeyModifier(const Event *event);

View File

@ -70,36 +70,35 @@ QWasmIntegration *QWasmIntegration::s_instance;
QWasmIntegration::QWasmIntegration()
: m_fontDb(nullptr),
m_compositor(new QWasmCompositor),
m_screen(new QWasmScreen(m_compositor)),
m_eventDispatcher(nullptr),
m_clipboard(new QWasmClipboard)
{
globalHtml5Integration = this;
s_instance = this;
emscripten::val defaultCanvasId = emscripten::val::global("canvas");
canvasIds.append(QString::fromStdString(defaultCanvasId["id"].as<std::string>()));
m_screen->setCanvas(canvasIds.at(0));
// We expect that qtloader.js has populated Module.qtCanvasElements with one or more canvases.
// Also check Module.canvas, which may be set if the emscripen or a custom loader is used.
emscripten::val qtCanvaseElements = val::module_property("qtCanvasElements");
emscripten::val canvas = val::module_property("canvas");
globalHtml5Integration = this;
screen()->updateQScreenAndCanvasRenderSize(m_screen->m_canvasId);
screenAdded(m_screen);
m_eventTranslator = new QWasmEventTranslator;
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 = QString::fromStdString(canvas["id"].as<std::string>());
addScreen(canvasId);
}
} else if (!canvas.isUndefined()){
QString canvasId = QString::fromStdString(canvas["id"].as<std::string>());
addScreen(canvasId);
}
emscripten::val::global("window").set("onbeforeunload", val::module_property("browserBeforeUnload"));
}
QWasmIntegration::~QWasmIntegration()
{
delete m_compositor;
destroyScreen(m_screen);
delete m_fontDb;
delete m_eventTranslator;
qDeleteAll(m_screens);
s_instance = nullptr;
}
@ -124,13 +123,15 @@ bool QWasmIntegration::hasCapability(QPlatformIntegration::Capability cap) const
QPlatformWindow *QWasmIntegration::createPlatformWindow(QWindow *window) const
{
return new QWasmWindow(window, m_compositor, m_backingStores.value(window));
QWasmCompositor *compositor = QWasmScreen::get(window->screen())->compositor();
return new QWasmWindow(window, compositor, m_backingStores.value(window));
}
QPlatformBackingStore *QWasmIntegration::createPlatformBackingStore(QWindow *window) const
{
#ifndef QT_NO_OPENGL
QWasmBackingStore *backingStore = new QWasmBackingStore(m_compositor, window);
QWasmCompositor *compositor = QWasmScreen::get(window->screen())->compositor();
QWasmBackingStore *backingStore = new QWasmBackingStore(compositor, window);
m_backingStores.insert(window, backingStore);
return backingStore;
#else
@ -177,9 +178,20 @@ QPlatformTheme *QWasmIntegration::createPlatformTheme(const QString &name) const
QPlatformClipboard* QWasmIntegration::clipboard() const
{
if (!m_clipboard)
m_clipboard = new QWasmClipboard;
return m_clipboard;
}
QVector<QWasmScreen *> QWasmIntegration::screens()
{
return m_screens;
}
void QWasmIntegration::addScreen(const QString &canvasId)
{
QWasmScreen *screen = new QWasmScreen(canvasId);
m_clipboard->installEventHandlers(canvasId);
m_screens.append(screen);
screenAdded(screen);
}
QT_END_NAMESPACE

View File

@ -71,23 +71,20 @@ public:
QPlatformTheme *createPlatformTheme(const QString &name) const override;
QPlatformClipboard *clipboard() const override;
QWasmScreen *screen() { return m_screen; }
QWasmCompositor *compositor() { return m_compositor; }
QWasmEventTranslator *eventTranslator() { return m_eventTranslator; }
QVector<QWasmScreen *>screens();
QWasmClipboard *getWasmClipboard() { return m_clipboard; }
static QWasmIntegration *get() { return s_instance; }
static void QWasmBrowserExit();
QStringList canvasIds;
private:
void addScreen(const QString &canvasId);
mutable QWasmFontDatabase *m_fontDb;
QWasmCompositor *m_compositor;
mutable QWasmScreen *m_screen;
mutable QWasmEventTranslator *m_eventTranslator;
mutable QWasmEventDispatcher *m_eventDispatcher;
mutable QHash<QWindow *, QWasmBackingStore *> m_backingStores;
QVector<QWasmScreen *> m_screens;
mutable QWasmClipboard *m_clipboard;
static QWasmIntegration *s_instance;
};

View File

@ -57,9 +57,7 @@ void QWasmOpenGLContext::maybeRecreateEmscriptenContext(QPlatformSurface *surfac
emscripten_webgl_destroy_context(m_context);
// Create new context
QWasmScreen *wasmScreen = QWasmIntegration::get()->screen();
const QString canvasId = wasmScreen->m_canvasId;
// FIXME: get the actual canvas from the surface.
const QString canvasId = QWasmScreen::get(surface->screen())->canvasId();
m_context = createEmscriptenContext(canvasId, m_requestedFormat);
// Register context-lost callback.

View File

@ -29,8 +29,10 @@
#include "qwasmscreen.h"
#include "qwasmwindow.h"
#include "qwasmeventtranslator.h"
#include "qwasmcompositor.h"
#include <emscripten/bind.h>
#include <emscripten/val.h>
#include <QtEglSupport/private/qeglconvenience_p.h>
#ifndef QT_NO_OPENGL
@ -44,12 +46,13 @@
QT_BEGIN_NAMESPACE
QWasmScreen::QWasmScreen(QWasmCompositor *compositor)
: m_compositor(compositor)
, m_depth(32)
, m_format(QImage::Format_RGB32)
QWasmScreen::QWasmScreen(const QString &canvasId)
: m_canvasId(canvasId)
{
m_compositor->setScreen(this);
m_compositor = new QWasmCompositor(this);
m_eventTranslator = new QWasmEventTranslator(this);
updateQScreenAndCanvasRenderSize();
}
QWasmScreen::~QWasmScreen()
@ -57,6 +60,31 @@ QWasmScreen::~QWasmScreen()
}
QWasmScreen *QWasmScreen::get(QPlatformScreen *screen)
{
return static_cast<QWasmScreen *>(screen);
}
QWasmScreen *QWasmScreen::get(QScreen *screen)
{
return get(screen->handle());
}
QWasmCompositor *QWasmScreen::compositor()
{
return m_compositor;
}
QWasmEventTranslator *QWasmScreen::eventTranslator()
{
return m_eventTranslator;
}
QString QWasmScreen::canvasId() const
{
return m_canvasId;
}
QRect QWasmScreen::geometry() const
{
return m_geometry;
@ -82,6 +110,11 @@ qreal QWasmScreen::devicePixelRatio() const
return qreal(htmlWindowDpr);
}
QString QWasmScreen::name() const
{
return m_canvasId;
}
QPlatformCursor *QWasmScreen::cursor() const
{
return const_cast<QWasmCursor *>(&m_cursor);
@ -114,14 +147,7 @@ void QWasmScreen::setGeometry(const QRect &rect)
resizeMaximizedWindows();
}
static void set_canvas_size(double width, double height, const char *canvasId)
{
emscripten::val canvas = emscripten::val::global(canvasId);
canvas.set("width", width);
canvas.set("height", height);
}
void QWasmScreen::updateQScreenAndCanvasRenderSize(const QString &canvasId)
void QWasmScreen::updateQScreenAndCanvasRenderSize()
{
// The HTML canvas has two sizes: the CSS size and the canvas render size.
// The CSS size is determined according to standard CSS rules, while the
@ -130,17 +156,22 @@ void QWasmScreen::updateQScreenAndCanvasRenderSize(const QString &canvasId)
// Setting the render size to a value larger than the CSS size enables high-dpi
// rendering.
QByteArray canvasId = m_canvasId.toUtf8();
double css_width;
double css_height;
emscripten_get_element_css_size(canvasId.toLocal8Bit().constData(), &css_width, &css_height);
emscripten_get_element_css_size(canvasId.constData(), &css_width, &css_height);
QSizeF cssSize(css_width, css_height);
QWasmScreen *screen = QWasmIntegration::get()->screen();
QSizeF canvasSize = cssSize * screen->devicePixelRatio();
QSizeF canvasSize = cssSize * devicePixelRatio();
emscripten::val canvas = emscripten::val::global(canvasId.constData());
canvas.set("width", canvasSize.width());
canvas.set("height", canvasSize.height());
set_canvas_size(canvasSize.width(), canvasSize.height(), canvasId.toLocal8Bit().constData());
screen->setGeometry(QRect(QPoint(0, 0), cssSize.toSize()));
QWasmIntegration::get()->compositor()->redrawWindowContent();
emscripten::val rect = canvas.call<emscripten::val>("getBoundingClientRect");
QPoint position(rect["left"].as<int>(), rect["top"].as<int>());
setGeometry(QRect(position, cssSize.toSize()));
m_compositor->redrawWindowContent();
}
QT_END_NAMESPACE

View File

@ -43,20 +43,28 @@ class QPlatformOpenGLContext;
class QWasmWindow;
class QWasmBackingStore;
class QWasmCompositor;
class QWasmEventTranslator;
class QOpenGLContext;
class QWasmScreen : public QObject, public QPlatformScreen
{
Q_OBJECT
public:
QWasmScreen(QWasmCompositor *compositor);
QWasmScreen(const QString &canvasId);
~QWasmScreen();
static QWasmScreen *get(QPlatformScreen *screen);
static QWasmScreen *get(QScreen *screen);
QString canvasId() const;
QWasmCompositor *compositor();
QWasmEventTranslator *eventTranslator();
QRect geometry() const override;
int depth() const override;
QImage::Format format() const override;
qreal devicePixelRatio() const override;
QString name() const override;
QPlatformCursor *cursor() const override;
void resizeMaximizedWindows();
@ -64,19 +72,18 @@ public:
QWindow *topLevelAt(const QPoint &p) const override;
void invalidateSize();
static void updateQScreenAndCanvasRenderSize(const QString &);
QString m_canvasId;
void setCanvas(const QString &canvasId) { m_canvasId = canvasId; }
void updateQScreenAndCanvasRenderSize();
public slots:
void setGeometry(const QRect &rect);
private:
QWasmCompositor *m_compositor;
QString m_canvasId;
QWasmCompositor *m_compositor = nullptr;
QWasmEventTranslator *m_eventTranslator = nullptr;
QRect m_geometry = QRect(0, 0, 100, 100);
int m_depth;
QImage::Format m_format;
int m_depth = 32;
QImage::Format m_format = QImage::Format_RGB32;
QWasmCursor m_cursor;
};

View File

@ -15,23 +15,24 @@
</style>
</head>
<body onload="init()">
<figure style="overflow:visible;" id="spinner">
<figure style="overflow:visible;" id="qtspinner">
<center style="margin-top:1.5em; line-height:150%">
<img src="qtlogo.svg"; width=320; height=200; style="display:block"> </img>
<strong>Qt for WebAssembly: APPNAME</strong>
<div id="status"></div>
<div id="qtstatus"></div>
<noscript>JavaScript is disabled. Please enable JavaScript to use this application.</noscript>
</center>
</figure>
<canvas id="canvas" oncontextmenu="event.preventDefault()" contenteditable="true"></canvas>
<canvas id="qtcanvas" oncontextmenu="event.preventDefault()" contenteditable="true"></canvas>
<script type='text/javascript'>
function init() {
var spinner = document.getElementById('spinner');
var canvas = document.getElementById('canvas');
var status = document.getElementById('status')
var spinner = document.getElementById('qtspinner');
var canvas = document.getElementById('qtcanvas');
var status = document.getElementById('qtstatus')
var qtLoader = QtLoader({
canvasElements : [canvas],
showLoader: function(loaderStatus) {
spinner.style.display = 'block';
canvas.style.display = 'none';
@ -54,7 +55,6 @@
showCanvas: function() {
spinner.style.display = 'none';
canvas.style.display = 'block';
return canvas;
},
});
qtLoader.loadEmscriptenModule("APPNAME");