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:
parent
452c644c5c
commit
caa74f16d4
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
@ -51,6 +51,7 @@ public:
|
||||
|
||||
static void qWasmClipboardPaste(QMimeData *mData);
|
||||
void initClipboardEvents();
|
||||
void installEventHandlers(const QString &canvasId);
|
||||
bool hasClipboardApi;
|
||||
void readTextFromClipboard();
|
||||
void writeTextToClipboard();
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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()));
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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");
|
||||
|
Loading…
Reference in New Issue
Block a user