qt6-bb10/src/plugins/platforms/wasm/qwasmcompositor.cpp

253 lines
7.9 KiB
C++

// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "qwasmcompositor.h"
#include "qwasmwindow.h"
#include "qwasmeventdispatcher.h"
#include "qwasmclipboard.h"
#include "qwasmevent.h"
#include <QtGui/private/qwindow_p.h>
#include <private/qguiapplication_p.h>
#include <qpa/qwindowsysteminterface.h>
#include <QtCore/qcoreapplication.h>
#include <QtGui/qguiapplication.h>
#include <emscripten/bind.h>
namespace {
QWasmWindow *asWasmWindow(QWindow *window)
{
return static_cast<QWasmWindow*>(window->handle());
}
} // namespace
using namespace emscripten;
Q_GUI_EXPORT int qt_defaultDpiX();
QWasmCompositor::QWasmCompositor(QWasmScreen *screen)
: QObject(screen), m_windowStack(std::bind(&QWasmCompositor::onTopWindowChanged, this))
{
QWindowSystemInterface::setSynchronousWindowSystemEvents(true);
}
QWasmCompositor::~QWasmCompositor()
{
if (m_requestAnimationFrameId != -1)
emscripten_cancel_animation_frame(m_requestAnimationFrameId);
destroy();
}
void QWasmCompositor::onScreenDeleting()
{
deregisterEventHandlers();
}
void QWasmCompositor::deregisterEventHandlers()
{
QByteArray screenElementSelector = screen()->eventTargetId().toUtf8();
emscripten_set_touchstart_callback(screenElementSelector.constData(), 0, 0, NULL);
emscripten_set_touchend_callback(screenElementSelector.constData(), 0, 0, NULL);
emscripten_set_touchmove_callback(screenElementSelector.constData(), 0, 0, NULL);
emscripten_set_touchcancel_callback(screenElementSelector.constData(), 0, 0, NULL);
}
void QWasmCompositor::destroy()
{
// 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::addWindow(QWasmWindow *window)
{
m_windowStack.pushWindow(window);
m_windowStack.topWindow()->requestActivateWindow();
updateEnabledState();
}
void QWasmCompositor::removeWindow(QWasmWindow *window)
{
m_requestUpdateWindows.remove(window);
m_windowStack.removeWindow(window);
if (m_windowStack.topWindow())
m_windowStack.topWindow()->requestActivateWindow();
updateEnabledState();
}
void QWasmCompositor::updateEnabledState()
{
m_isEnabled = std::any_of(m_windowStack.begin(), m_windowStack.end(), [](QWasmWindow *window) {
return !window->context2d().isUndefined();
});
}
void QWasmCompositor::raise(QWasmWindow *window)
{
m_windowStack.raise(window);
}
void QWasmCompositor::lower(QWasmWindow *window)
{
m_windowStack.lower(window);
}
QWindow *QWasmCompositor::windowAt(QPoint targetPointInScreenCoords, int padding) const
{
const auto found = std::find_if(
m_windowStack.begin(), m_windowStack.end(),
[padding, &targetPointInScreenCoords](const QWasmWindow *window) {
const QRect geometry = window->windowFrameGeometry().adjusted(-padding, -padding,
padding, padding);
return window->isVisible() && geometry.contains(targetPointInScreenCoords);
});
return found != m_windowStack.end() ? (*found)->window() : nullptr;
}
QWindow *QWasmCompositor::keyWindow() const
{
return m_windowStack.topWindow() ? m_windowStack.topWindow()->window() : nullptr;
}
void QWasmCompositor::requestUpdateAllWindows()
{
m_requestUpdateAllWindows = true;
requestUpdate();
}
void QWasmCompositor::requestUpdateWindow(QWasmWindow *window, UpdateRequestDeliveryType updateType)
{
auto it = m_requestUpdateWindows.find(window);
if (it == m_requestUpdateWindows.end()) {
m_requestUpdateWindows.insert(window, updateType);
} else {
// Already registered, but upgrade ExposeEventDeliveryType to UpdateRequestDeliveryType.
// if needed, to make sure QWindow::updateRequest's are matched.
if (it.value() == ExposeEventDelivery && updateType == UpdateRequestDelivery)
it.value() = UpdateRequestDelivery;
}
requestUpdate();
}
// Requests an update/new frame using RequestAnimationFrame
void QWasmCompositor::requestUpdate()
{
if (m_requestAnimationFrameId != -1)
return;
static auto frame = [](double frameTime, void *context) -> int {
Q_UNUSED(frameTime);
QWasmCompositor *compositor = reinterpret_cast<QWasmCompositor *>(context);
compositor->m_requestAnimationFrameId = -1;
compositor->deliverUpdateRequests();
return 0;
};
m_requestAnimationFrameId = emscripten_request_animation_frame(frame, this);
}
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();
bool requestUpdateAllWindows = m_requestUpdateAllWindows;
m_requestUpdateAllWindows = false;
// 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;
if (requestUpdateAllWindows) {
for (QWasmWindow *window : m_windowStack) {
auto it = requestUpdateWindows.find(window);
UpdateRequestDeliveryType updateType =
(it == m_requestUpdateWindows.end() ? ExposeEventDelivery : it.value());
deliverUpdateRequest(window, updateType);
}
} else {
for (auto it = requestUpdateWindows.constBegin(); it != requestUpdateWindows.constEnd(); ++it) {
auto *window = it.key();
UpdateRequestDeliveryType updateType = it.value();
deliverUpdateRequest(window, updateType);
}
}
m_inDeliverUpdateRequest = false;
frame(requestUpdateAllWindows, requestUpdateWindows.keys());
}
void QWasmCompositor::deliverUpdateRequest(QWasmWindow *window, UpdateRequestDeliveryType updateType)
{
// update by deliverUpdateRequest and expose event accordingly.
if (updateType == UpdateRequestDelivery) {
window->QPlatformWindow::deliverUpdateRequest();
} else {
QWindow *qwindow = window->window();
QWindowSystemInterface::handleExposeEvent(
qwindow, QRect(QPoint(0, 0), qwindow->geometry().size()));
}
}
void QWasmCompositor::handleBackingStoreFlush(QWindow *window)
{
// 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(asWasmWindow(window));
}
int dpiScaled(qreal value)
{
return value * (qreal(qt_defaultDpiX()) / 96.0);
}
void QWasmCompositor::frame(bool all, const QList<QWasmWindow *> &windows)
{
if (!m_isEnabled || m_windowStack.empty() || !screen())
return;
if (all) {
std::for_each(m_windowStack.rbegin(), m_windowStack.rend(),
[](QWasmWindow *window) { window->paint(); });
} else {
std::for_each(windows.begin(), windows.end(), [](QWasmWindow *window) { window->paint(); });
}
}
void QWasmCompositor::onTopWindowChanged()
{
constexpr int zOrderForElementInFrontOfScreen = 3;
int z = zOrderForElementInFrontOfScreen;
std::for_each(m_windowStack.rbegin(), m_windowStack.rend(),
[&z](QWasmWindow *window) { window->setZOrder(z++); });
auto it = m_windowStack.begin();
if (it == m_windowStack.end()) {
return;
}
(*it)->onActivationChanged(true);
++it;
for (; it != m_windowStack.end(); ++it) {
(*it)->onActivationChanged(false);
}
}
QWasmScreen *QWasmCompositor::screen()
{
return static_cast<QWasmScreen *>(parent());
}