Source code
Revision control
Copy as Markdown
// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// Qt-Security score:significant reason:default
#include "qwasmcompositor.h"
#include "qwasmwindow.h"
#include <private/qeventdispatcher_wasm_p.h>
#include <private/qwasmsuspendresumecontrol_p.h>
#include <qpa/qwindowsysteminterface.h>
#include <emscripten/html5.h>
using namespace emscripten;
QWasmCompositor::QWasmCompositor(QWasmScreen *screen)
: QObject(screen)
{
QWindowSystemInterface::setSynchronousWindowSystemEvents(true);
}
QWasmCompositor::~QWasmCompositor()
{
if (m_drawCallbackHandle != 0)
QWasmAnimationFrameMultiHandler::instance()->unregisterDrawCallback(m_drawCallbackHandle);
// TODO(mikolaj.boc): Investigate if m_isEnabled is needed at all. It seems like a frame should
// not be generated after this instead.
m_isEnabled = false; // prevent frame() from creating a new m_context
}
void QWasmCompositor::onWindowAdded(QWasmWindow *)
{
updateEnabledForWindows();
}
void QWasmCompositor::onWindowRemoved(QWasmWindow *window)
{
m_requestUpdateWindows.remove(window);
updateEnabledForWindows();
}
void QWasmCompositor::updateEnabledForWindows()
{
auto allWindows = screen()->allWindows();
setEnabled(std::any_of(allWindows.begin(), allWindows.end(), [](QWasmWindow *element) {
return !element->context2d().isUndefined();
}));
}
void QWasmCompositor::setEnabled(bool enabled)
{
m_isEnabled = enabled;
}
void QWasmCompositor::requestUpdateWindow(QWasmWindow *window, const QRect &updateRect, UpdateRequestDeliveryType updateType)
{
auto it = m_requestUpdateWindows.find(window);
if (it == m_requestUpdateWindows.end()) {
m_requestUpdateWindows.insert(window, std::make_tuple(updateRect, updateType));
} else {
// Already registered, but upgrade ExposeEventDeliveryType to UpdateRequestDeliveryType.
// if needed, to make sure QWindow::updateRequest's are matched.
if (std::get<0>(it.value()) != updateRect) {
QRegion region;
region |= std::get<0>(it.value());
region |= updateRect;
std::get<0>(it.value()) = region.boundingRect();
}
if (std::get<1>(it.value()) == ExposeEventDelivery &&
updateType == UpdateRequestDelivery)
std::get<1>(it.value()) = UpdateRequestDelivery;
}
requestUpdate();
}
// Requests an update/new frame using RequestAnimationFrame
void QWasmCompositor::requestUpdate()
{
if (m_drawCallbackHandle != 0)
return;
m_drawCallbackHandle = QWasmAnimationFrameMultiHandler::instance()->registerDrawCallback(
[this](double frametime) {
Q_UNUSED(frametime);
m_drawCallbackHandle = 0;
deliverUpdateRequests();
});
}
void QWasmCompositor::deliverUpdateRequests()
{
// We may get new update requests during the window content update below:
// prepare for recording the new update set by setting aside the current
// update set.
auto requestUpdateWindows = m_requestUpdateWindows;
m_requestUpdateWindows.clear();
// Update window content, either all windows or a spesific set of windows. Use the correct
// update type: QWindow subclasses expect that requested and delivered updateRequests matches
// exactly.
m_inDeliverUpdateRequest = true;
for (auto it = requestUpdateWindows.constBegin(); it != requestUpdateWindows.constEnd(); ++it) {
auto *window = it.key();
const QRect updateRect = std::get<0>(it.value());
const UpdateRequestDeliveryType updateType = std::get<1>(it.value());
deliverUpdateRequest(window, updateRect, updateType);
}
m_inDeliverUpdateRequest = false;
frame(requestUpdateWindows.keys());
}
void QWasmCompositor::deliverUpdateRequest(
QWasmWindow *window,
const QRect &updateRect,
UpdateRequestDeliveryType updateType)
{
QWindow *qwindow = window->window();
// Make sure the DPR value for the window is up to date on expose/repaint.
// FIXME: listen to native DPR change events instead, if/when available.
QWindowSystemInterface::handleWindowDevicePixelRatioChanged(qwindow);
// Update by deliverUpdateRequest and expose event according to requested update
// type. If the window has not yet been exposed then we must expose it first regardless
// of update type. The deliverUpdateRequest must still be sent in this case in order
// to maintain correct window update state.
if (updateType == UpdateRequestDelivery) {
if (qwindow->isExposed() == false)
QWindowSystemInterface::handleExposeEvent(qwindow, updateRect);
window->deliverUpdateRequest();
} else {
QWindowSystemInterface::handleExposeEvent(qwindow, updateRect);
}
}
void QWasmCompositor::handleBackingStoreFlush(QWindow *window, const QRect &updateRect)
{
// Request update to flush the updated backing store content, unless we are currently
// processing an update, in which case the new content will flushed as a part of that update.
if (!m_inDeliverUpdateRequest)
requestUpdateWindow(static_cast<QWasmWindow *>(window->handle()), updateRect);
}
void QWasmCompositor::frame(const QList<QWasmWindow *> &windows)
{
if (!m_isEnabled || !screen())
return;
for (QWasmWindow *window : windows)
window->paint();
}
QWasmScreen *QWasmCompositor::screen()
{
return static_cast<QWasmScreen *>(parent());
}