mirror of https://github.com/qt/qtbase.git
810 lines
27 KiB
C++
810 lines
27 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2018 The Qt Company Ltd.
|
|
** Contact: https://www.qt.io/licensing/
|
|
**
|
|
** This file is part of the plugins of the Qt Toolkit.
|
|
**
|
|
** $QT_BEGIN_LICENSE:GPL$
|
|
** Commercial License Usage
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
** accordance with the commercial license agreement provided with the
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
|
**
|
|
** GNU General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
** General Public License version 3 or (at your option) any later version
|
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
|
** included in the packaging of this file. Please review the following
|
|
** information to ensure the GNU General Public License requirements will
|
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
|
**
|
|
** $QT_END_LICENSE$
|
|
**
|
|
****************************************************************************/
|
|
|
|
#include "qwasmcompositor.h"
|
|
#include "qwasmeventdispatcher.h"
|
|
#include "qwasmwindow.h"
|
|
#include "qwasmstylepixmaps_p.h"
|
|
|
|
#include <QtOpenGL/qopengltexture.h>
|
|
|
|
#include <QtGui/private/qwindow_p.h>
|
|
#include <QtGui/qopenglcontext.h>
|
|
#include <QtGui/qopenglfunctions.h>
|
|
#include <QtGui/qoffscreensurface.h>
|
|
#include <QtGui/qpainter.h>
|
|
#include <private/qpixmapcache_p.h>
|
|
|
|
#include <private/qguiapplication_p.h>
|
|
|
|
#include <qpa/qwindowsysteminterface.h>
|
|
#include <QtCore/qcoreapplication.h>
|
|
#include <QtGui/qguiapplication.h>
|
|
|
|
Q_GUI_EXPORT int qt_defaultDpiX();
|
|
|
|
QWasmCompositedWindow::QWasmCompositedWindow()
|
|
: window(nullptr)
|
|
, parentWindow(nullptr)
|
|
, flushPending(false)
|
|
, visible(false)
|
|
{
|
|
}
|
|
|
|
QWasmCompositor::QWasmCompositor(QWasmScreen *screen)
|
|
:QObject(screen)
|
|
, m_blitter(new QOpenGLTextureBlitter)
|
|
, m_needComposit(false)
|
|
, m_inFlush(false)
|
|
, m_inResize(false)
|
|
, m_isEnabled(true)
|
|
, m_targetDevicePixelRatio(1)
|
|
{
|
|
}
|
|
|
|
QWasmCompositor::~QWasmCompositor()
|
|
{
|
|
if (m_requestAnimationFrameId != -1)
|
|
emscripten_cancel_animation_frame(m_requestAnimationFrameId);
|
|
destroy();
|
|
}
|
|
|
|
void QWasmCompositor::destroy()
|
|
{
|
|
// Destroy OpenGL resources. This is done here in a separate function
|
|
// which can be called while screen() still returns a valid screen
|
|
// (which it might not, during destruction). A valid QScreen is
|
|
// a requirement for QOffscreenSurface on Wasm since the native
|
|
// context is tied to a single canvas.
|
|
if (m_context) {
|
|
QOffscreenSurface offScreenSurface(screen()->screen());
|
|
offScreenSurface.setFormat(m_context->format());
|
|
offScreenSurface.create();
|
|
m_context->makeCurrent(&offScreenSurface);
|
|
for (QWasmWindow *window : m_windowStack)
|
|
window->destroy();
|
|
m_blitter.reset(nullptr);
|
|
m_context.reset(nullptr);
|
|
}
|
|
|
|
m_isEnabled = false; // prevent frame() from creating a new m_context
|
|
}
|
|
|
|
void QWasmCompositor::setEnabled(bool enabled)
|
|
{
|
|
m_isEnabled = enabled;
|
|
}
|
|
|
|
void QWasmCompositor::addWindow(QWasmWindow *window, QWasmWindow *parentWindow)
|
|
{
|
|
QWasmCompositedWindow compositedWindow;
|
|
compositedWindow.window = window;
|
|
compositedWindow.parentWindow = parentWindow;
|
|
m_compositedWindows.insert(window, compositedWindow);
|
|
|
|
if (parentWindow == 0)
|
|
m_windowStack.append(window);
|
|
else
|
|
m_compositedWindows[parentWindow].childWindows.append(window);
|
|
|
|
if (!QGuiApplication::focusWindow()) {
|
|
window->requestActivateWindow();
|
|
}
|
|
notifyTopWindowChanged(window);
|
|
}
|
|
|
|
void QWasmCompositor::removeWindow(QWasmWindow *window)
|
|
{
|
|
QWasmWindow *platformWindow = m_compositedWindows[window].parentWindow;
|
|
|
|
if (platformWindow) {
|
|
QWasmWindow *parentWindow = window;
|
|
m_compositedWindows[parentWindow].childWindows.removeAll(window);
|
|
}
|
|
|
|
m_windowStack.removeAll(window);
|
|
m_compositedWindows.remove(window);
|
|
|
|
if (!m_windowStack.isEmpty() && !QGuiApplication::focusWindow()) {
|
|
auto lastWindow = m_windowStack.last();
|
|
lastWindow->requestActivateWindow();
|
|
notifyTopWindowChanged(lastWindow);
|
|
}
|
|
}
|
|
|
|
void QWasmCompositor::setVisible(QWasmWindow *window, bool visible)
|
|
{
|
|
QWasmCompositedWindow &compositedWindow = m_compositedWindows[window];
|
|
if (compositedWindow.visible == visible)
|
|
return;
|
|
|
|
compositedWindow.visible = visible;
|
|
compositedWindow.flushPending = true;
|
|
if (visible)
|
|
compositedWindow.damage = compositedWindow.window->geometry();
|
|
else
|
|
m_globalDamage = compositedWindow.window->geometry(); // repaint previously covered area.
|
|
|
|
requestUpdateWindow(window, QWasmCompositor::ExposeEventDelivery);
|
|
}
|
|
|
|
void QWasmCompositor::raise(QWasmWindow *window)
|
|
{
|
|
if (m_compositedWindows.size() <= 1)
|
|
return;
|
|
|
|
QWasmCompositedWindow &compositedWindow = m_compositedWindows[window];
|
|
compositedWindow.damage = compositedWindow.window->geometry();
|
|
m_windowStack.removeAll(window);
|
|
m_windowStack.append(window);
|
|
|
|
notifyTopWindowChanged(window);
|
|
}
|
|
|
|
void QWasmCompositor::lower(QWasmWindow *window)
|
|
{
|
|
if (m_compositedWindows.size() <= 1)
|
|
return;
|
|
|
|
m_windowStack.removeAll(window);
|
|
m_windowStack.prepend(window);
|
|
QWasmCompositedWindow &compositedWindow = m_compositedWindows[window];
|
|
m_globalDamage = compositedWindow.window->geometry(); // repaint previously covered area.
|
|
|
|
notifyTopWindowChanged(window);
|
|
}
|
|
|
|
void QWasmCompositor::setParent(QWasmWindow *window, QWasmWindow *parent)
|
|
{
|
|
m_compositedWindows[window].parentWindow = parent;
|
|
|
|
requestUpdate();
|
|
}
|
|
|
|
int QWasmCompositor::windowCount() const
|
|
{
|
|
return m_windowStack.count();
|
|
}
|
|
|
|
QWindow *QWasmCompositor::windowAt(QPoint globalPoint, int padding) const
|
|
{
|
|
int index = m_windowStack.count() - 1;
|
|
// qDebug() << "window at" << "point" << p << "window count" << index;
|
|
|
|
while (index >= 0) {
|
|
const QWasmCompositedWindow &compositedWindow = m_compositedWindows[m_windowStack.at(index)];
|
|
//qDebug() << "windwAt testing" << compositedWindow.window <<
|
|
|
|
QRect geometry = compositedWindow.window->windowFrameGeometry()
|
|
.adjusted(-padding, -padding, padding, padding);
|
|
|
|
if (compositedWindow.visible && geometry.contains(globalPoint))
|
|
return m_windowStack.at(index)->window();
|
|
--index;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
QWindow *QWasmCompositor::keyWindow() const
|
|
{
|
|
return m_windowStack.at(m_windowStack.count() - 1)->window();
|
|
}
|
|
|
|
void QWasmCompositor::blit(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, const QOpenGLTexture *texture, QRect targetGeometry)
|
|
{
|
|
QMatrix4x4 m;
|
|
m.translate(-1.0f, -1.0f);
|
|
|
|
m.scale(2.0f / (float)screen->geometry().width(),
|
|
2.0f / (float)screen->geometry().height());
|
|
|
|
m.translate((float)targetGeometry.width() / 2.0f,
|
|
(float)-targetGeometry.height() / 2.0f);
|
|
|
|
m.translate(targetGeometry.x(), screen->geometry().height() - targetGeometry.y());
|
|
|
|
m.scale(0.5f * (float)targetGeometry.width(),
|
|
0.5f * (float)targetGeometry.height());
|
|
|
|
blitter->blit(texture->textureId(), m, QOpenGLTextureBlitter::OriginTopLeft);
|
|
}
|
|
|
|
void QWasmCompositor::drawWindowContent(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window)
|
|
{
|
|
QWasmBackingStore *backingStore = window->backingStore();
|
|
if (!backingStore)
|
|
return;
|
|
|
|
QOpenGLTexture const *texture = backingStore->getUpdatedTexture();
|
|
QPoint windowCanvasPosition = window->geometry().topLeft() - screen->geometry().topLeft();
|
|
QRect windowCanvasGeometry = QRect(windowCanvasPosition, window->geometry().size());
|
|
blit(blitter, screen, texture, windowCanvasGeometry);
|
|
}
|
|
|
|
QPalette QWasmCompositor::makeWindowPalette()
|
|
{
|
|
QPalette palette;
|
|
palette.setColor(QPalette::Active, QPalette::Highlight,
|
|
palette.color(QPalette::Active, QPalette::Highlight));
|
|
palette.setColor(QPalette::Active, QPalette::Base,
|
|
palette.color(QPalette::Active, QPalette::Highlight));
|
|
palette.setColor(QPalette::Inactive, QPalette::Highlight,
|
|
palette.color(QPalette::Inactive, QPalette::Dark));
|
|
palette.setColor(QPalette::Inactive, QPalette::Base,
|
|
palette.color(QPalette::Inactive, QPalette::Dark));
|
|
palette.setColor(QPalette::Inactive, QPalette::HighlightedText,
|
|
palette.color(QPalette::Inactive, QPalette::Window));
|
|
|
|
return palette;
|
|
}
|
|
|
|
QRect QWasmCompositor::titlebarRect(QWasmTitleBarOptions tb, QWasmCompositor::SubControls subcontrol)
|
|
{
|
|
QRect ret;
|
|
const int controlMargin = 2;
|
|
const int controlHeight = tb.rect.height() - controlMargin *2;
|
|
const int delta = controlHeight + controlMargin;
|
|
int offset = 0;
|
|
|
|
bool isMinimized = tb.state & Qt::WindowMinimized;
|
|
bool isMaximized = tb.state & Qt::WindowMaximized;
|
|
|
|
ret = tb.rect;
|
|
switch (subcontrol) {
|
|
case SC_TitleBarLabel:
|
|
if (tb.flags & Qt::WindowSystemMenuHint)
|
|
ret.adjust(delta, 0, -delta, 0);
|
|
break;
|
|
case SC_TitleBarCloseButton:
|
|
if (tb.flags & Qt::WindowSystemMenuHint) {
|
|
ret.adjust(0, 0, -delta, 0);
|
|
offset += delta;
|
|
}
|
|
break;
|
|
case SC_TitleBarMaxButton:
|
|
if (!isMaximized && tb.flags & Qt::WindowMaximizeButtonHint) {
|
|
ret.adjust(0, 0, -delta*2, 0);
|
|
offset += (delta +delta);
|
|
}
|
|
break;
|
|
case SC_TitleBarNormalButton:
|
|
if (isMinimized && (tb.flags & Qt::WindowMinimizeButtonHint)) {
|
|
offset += delta;
|
|
} else if (isMaximized && (tb.flags & Qt::WindowMaximizeButtonHint)) {
|
|
ret.adjust(0, 0, -delta*2, 0);
|
|
offset += (delta +delta);
|
|
}
|
|
break;
|
|
case SC_TitleBarSysMenu:
|
|
if (tb.flags & Qt::WindowSystemMenuHint) {
|
|
ret.setRect(tb.rect.left() + controlMargin, tb.rect.top() + controlMargin,
|
|
controlHeight, controlHeight);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
};
|
|
|
|
if (subcontrol != SC_TitleBarLabel && subcontrol != SC_TitleBarSysMenu) {
|
|
ret.setRect(tb.rect.right() - offset, tb.rect.top() + controlMargin,
|
|
controlHeight, controlHeight);
|
|
}
|
|
|
|
if (qApp->layoutDirection() == Qt::LeftToRight)
|
|
return ret;
|
|
|
|
QRect rect = ret;
|
|
rect.translate(2 * (tb.rect.right() - ret.right()) +
|
|
ret.width() - tb.rect.width(), 0);
|
|
|
|
return rect;
|
|
}
|
|
|
|
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 upate/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;
|
|
|
|
// Compose window content
|
|
frame();
|
|
}
|
|
|
|
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<QWindowSystemInterface::SynchronousDelivery>(
|
|
qwindow, QRect(QPoint(0, 0), qwindow->geometry().size()));
|
|
}
|
|
}
|
|
|
|
void QWasmCompositor::handleBackingStoreFlush()
|
|
{
|
|
// 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)
|
|
requestUpdate();
|
|
}
|
|
|
|
int dpiScaled(qreal value)
|
|
{
|
|
return value * (qreal(qt_defaultDpiX()) / 96.0);
|
|
}
|
|
|
|
QWasmCompositor::QWasmTitleBarOptions QWasmCompositor::makeTitleBarOptions(const QWasmWindow *window)
|
|
{
|
|
int width = window->windowFrameGeometry().width();
|
|
int border = window->borderWidth();
|
|
|
|
QWasmTitleBarOptions titleBarOptions;
|
|
|
|
titleBarOptions.rect = QRect(border, border, width - 2 * border, window->titleHeight());
|
|
titleBarOptions.flags = window->window()->flags();
|
|
titleBarOptions.state = window->window()->windowState();
|
|
|
|
bool isMaximized = titleBarOptions.state & Qt::WindowMaximized; // this gets reset when maximized
|
|
|
|
if (titleBarOptions.flags & (Qt::WindowTitleHint))
|
|
titleBarOptions.subControls |= SC_TitleBarLabel;
|
|
if (titleBarOptions.flags & Qt::WindowMaximizeButtonHint) {
|
|
if (isMaximized)
|
|
titleBarOptions.subControls |= SC_TitleBarNormalButton;
|
|
else
|
|
titleBarOptions.subControls |= SC_TitleBarMaxButton;
|
|
}
|
|
if (titleBarOptions.flags & Qt::WindowSystemMenuHint) {
|
|
titleBarOptions.subControls |= SC_TitleBarCloseButton;
|
|
titleBarOptions.subControls |= SC_TitleBarSysMenu;
|
|
}
|
|
|
|
|
|
titleBarOptions.palette = QWasmCompositor::makeWindowPalette();
|
|
|
|
if (window->window()->isActive())
|
|
titleBarOptions.palette.setCurrentColorGroup(QPalette::Active);
|
|
else
|
|
titleBarOptions.palette.setCurrentColorGroup(QPalette::Inactive);
|
|
|
|
if (window->activeSubControl() != QWasmCompositor::SC_None)
|
|
titleBarOptions.subControls = window->activeSubControl();
|
|
|
|
if (!window->window()->title().isEmpty())
|
|
titleBarOptions.titleBarOptionsString = window->window()->title();
|
|
|
|
titleBarOptions.windowIcon = window->window()->icon();
|
|
|
|
return titleBarOptions;
|
|
}
|
|
|
|
void QWasmCompositor::drawWindowDecorations(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window)
|
|
{
|
|
int width = window->windowFrameGeometry().width();
|
|
int height = window->windowFrameGeometry().height();
|
|
qreal dpr = window->devicePixelRatio();
|
|
|
|
QImage image(QSize(width * dpr, height * dpr), QImage::Format_RGB32);
|
|
image.setDevicePixelRatio(dpr);
|
|
QPainter painter(&image);
|
|
painter.fillRect(QRect(0, 0, width, height), painter.background());
|
|
|
|
QWasmTitleBarOptions titleBarOptions = makeTitleBarOptions(window);
|
|
|
|
drawTitlebarWindow(titleBarOptions, &painter);
|
|
|
|
QWasmFrameOptions frameOptions;
|
|
frameOptions.rect = QRect(0, 0, width, height);
|
|
frameOptions.lineWidth = dpiScaled(4.);
|
|
|
|
drawFrameWindow(frameOptions, &painter);
|
|
|
|
painter.end();
|
|
|
|
QOpenGLTexture texture(QOpenGLTexture::Target2D);
|
|
texture.setMinificationFilter(QOpenGLTexture::Nearest);
|
|
texture.setMagnificationFilter(QOpenGLTexture::Nearest);
|
|
texture.setWrapMode(QOpenGLTexture::ClampToEdge);
|
|
texture.setData(image, QOpenGLTexture::DontGenerateMipMaps);
|
|
texture.create();
|
|
texture.bind();
|
|
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image.width(), image.height(), GL_RGBA, GL_UNSIGNED_BYTE,
|
|
image.constScanLine(0));
|
|
|
|
blit(blitter, screen, &texture, QRect(window->windowFrameGeometry().topLeft(), QSize(width, height)));
|
|
}
|
|
|
|
void QWasmCompositor::drawFrameWindow(QWasmFrameOptions options, QPainter *painter)
|
|
{
|
|
int x = options.rect.x();
|
|
int y = options.rect.y();
|
|
int w = options.rect.width();
|
|
int h = options.rect.height();
|
|
const QColor &c1 = options.palette.light().color();
|
|
const QColor &c2 = options.palette.shadow().color();
|
|
const QColor &c3 = options.palette.midlight().color();
|
|
const QColor &c4 = options.palette.dark().color();
|
|
const QBrush *fill = nullptr;
|
|
|
|
const qreal devicePixelRatio = painter->device()->devicePixelRatio();
|
|
if (!qFuzzyCompare(devicePixelRatio, qreal(1))) {
|
|
const qreal inverseScale = qreal(1) / devicePixelRatio;
|
|
painter->scale(inverseScale, inverseScale);
|
|
x = qRound(devicePixelRatio * x);
|
|
y = qRound(devicePixelRatio * y);
|
|
w = qRound(devicePixelRatio * w);
|
|
h = qRound(devicePixelRatio * h);
|
|
}
|
|
|
|
QPen oldPen = painter->pen();
|
|
QPoint a[3] = { QPoint(x, y+h-2), QPoint(x, y), QPoint(x+w-2, y) };
|
|
painter->setPen(c1);
|
|
painter->drawPolyline(a, 3);
|
|
QPoint b[3] = { QPoint(x, y+h-1), QPoint(x+w-1, y+h-1), QPoint(x+w-1, y) };
|
|
painter->setPen(c2);
|
|
painter->drawPolyline(b, 3);
|
|
if (w > 4 && h > 4) {
|
|
QPoint c[3] = { QPoint(x+1, y+h-3), QPoint(x+1, y+1), QPoint(x+w-3, y+1) };
|
|
painter->setPen(c3);
|
|
painter->drawPolyline(c, 3);
|
|
QPoint d[3] = { QPoint(x+1, y+h-2), QPoint(x+w-2, y+h-2), QPoint(x+w-2, y+1) };
|
|
painter->setPen(c4);
|
|
painter->drawPolyline(d, 3);
|
|
if (fill)
|
|
painter->fillRect(QRect(x+2, y+2, w-4, h-4), *fill);
|
|
}
|
|
painter->setPen(oldPen);
|
|
}
|
|
|
|
//from commonstyle.cpp
|
|
static QPixmap cachedPixmapFromXPM(const char * const *xpm)
|
|
{
|
|
QPixmap result;
|
|
const QString tag = QString::asprintf("xpm:0x%p", static_cast<const void*>(xpm));
|
|
if (!QPixmapCache::find(tag, &result)) {
|
|
result = QPixmap(xpm);
|
|
QPixmapCache::insert(tag, result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void QWasmCompositor::drawItemPixmap(QPainter *painter, const QRect &rect, int alignment,
|
|
const QPixmap &pixmap) const
|
|
{
|
|
qreal scale = pixmap.devicePixelRatio();
|
|
QSize size = pixmap.size() / scale;
|
|
int x = rect.x();
|
|
int y = rect.y();
|
|
int w = size.width();
|
|
int h = size.height();
|
|
if ((alignment & Qt::AlignVCenter) == Qt::AlignVCenter)
|
|
y += rect.size().height()/2 - h/2;
|
|
else if ((alignment & Qt::AlignBottom) == Qt::AlignBottom)
|
|
y += rect.size().height() - h;
|
|
if ((alignment & Qt::AlignRight) == Qt::AlignRight)
|
|
x += rect.size().width() - w;
|
|
else if ((alignment & Qt::AlignHCenter) == Qt::AlignHCenter)
|
|
x += rect.size().width()/2 - w/2;
|
|
|
|
QRect aligned = QRect(x, y, w, h);
|
|
QRect inter = aligned.intersected(rect);
|
|
|
|
painter->drawPixmap(inter.x(), inter.y(), pixmap, inter.x() - aligned.x(), inter.y() - aligned.y(), inter.width() * scale, inter.height() *scale);
|
|
}
|
|
|
|
|
|
void QWasmCompositor::drawTitlebarWindow(QWasmTitleBarOptions tb, QPainter *painter)
|
|
{
|
|
QRect ir;
|
|
if (tb.subControls.testFlag(SC_TitleBarLabel)) {
|
|
QColor left = tb.palette.highlight().color();
|
|
QColor right = tb.palette.base().color();
|
|
|
|
QBrush fillBrush(left);
|
|
if (left != right) {
|
|
QPoint p1(tb.rect.x(), tb.rect.top() + tb.rect.height()/2);
|
|
QPoint p2(tb.rect.right(), tb.rect.top() + tb.rect.height()/2);
|
|
QLinearGradient lg(p1, p2);
|
|
lg.setColorAt(0, left);
|
|
lg.setColorAt(1, right);
|
|
fillBrush = lg;
|
|
}
|
|
|
|
painter->fillRect(tb.rect, fillBrush);
|
|
ir = titlebarRect(tb, SC_TitleBarLabel);
|
|
painter->setPen(tb.palette.highlightedText().color());
|
|
painter->drawText(ir.x() + 2, ir.y(), ir.width() - 2, ir.height(),
|
|
Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb.titleBarOptionsString);
|
|
} // SC_TitleBarLabel
|
|
|
|
QPixmap pixmap;
|
|
|
|
if (tb.subControls.testFlag(SC_TitleBarCloseButton)
|
|
&& tb.flags & Qt::WindowSystemMenuHint) {
|
|
ir = titlebarRect(tb, SC_TitleBarCloseButton);
|
|
pixmap = cachedPixmapFromXPM(qt_close_xpm).scaled(QSize(10, 10));
|
|
drawItemPixmap(painter, ir, Qt::AlignCenter, pixmap);
|
|
} //SC_TitleBarCloseButton
|
|
|
|
if (tb.subControls.testFlag(SC_TitleBarMaxButton)
|
|
&& tb.flags & Qt::WindowMaximizeButtonHint
|
|
&& !(tb.state & Qt::WindowMaximized)) {
|
|
ir = titlebarRect(tb, SC_TitleBarMaxButton);
|
|
pixmap = cachedPixmapFromXPM(qt_maximize_xpm).scaled(QSize(10, 10));
|
|
drawItemPixmap(painter, ir, Qt::AlignCenter, pixmap);
|
|
} //SC_TitleBarMaxButton
|
|
|
|
bool drawNormalButton = (tb.subControls & SC_TitleBarNormalButton)
|
|
&& (((tb.flags & Qt::WindowMinimizeButtonHint)
|
|
&& (tb.flags & Qt::WindowMinimized))
|
|
|| ((tb.flags & Qt::WindowMaximizeButtonHint)
|
|
&& (tb.flags & Qt::WindowMaximized)));
|
|
|
|
if (drawNormalButton) {
|
|
ir = titlebarRect(tb, SC_TitleBarNormalButton);
|
|
pixmap = cachedPixmapFromXPM(qt_normalizeup_xpm).scaled( QSize(10, 10));
|
|
|
|
drawItemPixmap(painter, ir, Qt::AlignCenter, pixmap);
|
|
} // SC_TitleBarNormalButton
|
|
|
|
if (tb.subControls & SC_TitleBarSysMenu && tb.flags & Qt::WindowSystemMenuHint) {
|
|
ir = titlebarRect(tb, SC_TitleBarSysMenu);
|
|
if (!tb.windowIcon.isNull()) {
|
|
tb.windowIcon.paint(painter, ir, Qt::AlignCenter);
|
|
} else {
|
|
pixmap = cachedPixmapFromXPM(qt_menu_xpm).scaled(QSize(10, 10));
|
|
drawItemPixmap(painter, ir, Qt::AlignCenter, pixmap);
|
|
}
|
|
}
|
|
}
|
|
|
|
void QWasmCompositor::drawShadePanel(QWasmTitleBarOptions options, QPainter *painter)
|
|
{
|
|
int lineWidth = 1;
|
|
QPalette palette = options.palette;
|
|
const QBrush *fill = &options.palette.brush(QPalette::Button);
|
|
|
|
int x = options.rect.x();
|
|
int y = options.rect.y();
|
|
int w = options.rect.width();
|
|
int h = options.rect.height();
|
|
|
|
const qreal devicePixelRatio = painter->device()->devicePixelRatio();
|
|
if (!qFuzzyCompare(devicePixelRatio, qreal(1))) {
|
|
const qreal inverseScale = qreal(1) / devicePixelRatio;
|
|
painter->scale(inverseScale, inverseScale);
|
|
|
|
x = qRound(devicePixelRatio * x);
|
|
y = qRound(devicePixelRatio * y);
|
|
w = qRound(devicePixelRatio * w);
|
|
h = qRound(devicePixelRatio * h);
|
|
lineWidth = qRound(devicePixelRatio * lineWidth);
|
|
}
|
|
|
|
QColor shade = palette.dark().color();
|
|
QColor light = palette.light().color();
|
|
|
|
if (fill) {
|
|
if (fill->color() == shade)
|
|
shade = palette.shadow().color();
|
|
if (fill->color() == light)
|
|
light = palette.midlight().color();
|
|
}
|
|
QPen oldPen = painter->pen();
|
|
QList<QLineF> lines;
|
|
lines.reserve(2*lineWidth);
|
|
|
|
painter->setPen(light);
|
|
int x1, y1, x2, y2;
|
|
int i;
|
|
x1 = x;
|
|
y1 = y2 = y;
|
|
x2 = x + w - 2;
|
|
for (i = 0; i < lineWidth; i++) // top shadow
|
|
lines << QLineF(x1, y1++, x2--, y2++);
|
|
|
|
x2 = x1;
|
|
y1 = y + h - 2;
|
|
for (i = 0; i < lineWidth; i++) // left shado
|
|
lines << QLineF(x1++, y1, x2++, y2--);
|
|
|
|
painter->drawLines(lines);
|
|
lines.clear();
|
|
painter->setPen(shade);
|
|
x1 = x;
|
|
y1 = y2 = y+h-1;
|
|
x2 = x+w-1;
|
|
for (i=0; i<lineWidth; i++) { // bottom shadow
|
|
lines << QLineF(x1++, y1--, x2, y2--);
|
|
}
|
|
x1 = x2;
|
|
y1 = y;
|
|
y2 = y + h - lineWidth - 1;
|
|
for (i = 0; i < lineWidth; i++) // right shadow
|
|
lines << QLineF(x1--, y1++, x2--, y2);
|
|
|
|
painter->drawLines(lines);
|
|
if (fill) // fill with fill color
|
|
painter->fillRect(x+lineWidth, y+lineWidth, w-lineWidth*2, h-lineWidth*2, *fill);
|
|
painter->setPen(oldPen); // restore pen
|
|
|
|
}
|
|
|
|
void QWasmCompositor::drawWindow(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window)
|
|
{
|
|
if (window->window()->type() != Qt::Popup && !(window->m_windowState & Qt::WindowFullScreen))
|
|
drawWindowDecorations(blitter, screen, window);
|
|
drawWindowContent(blitter, screen, window);
|
|
}
|
|
|
|
void QWasmCompositor::frame()
|
|
{
|
|
if (!m_isEnabled || m_windowStack.empty() || !screen())
|
|
return;
|
|
|
|
QWasmWindow *someWindow = nullptr;
|
|
|
|
for (QWasmWindow *window : qAsConst(m_windowStack)) {
|
|
if (window->window()->surfaceClass() == QSurface::Window
|
|
&& qt_window_private(static_cast<QWindow *>(window->window()))->receivedExpose) {
|
|
someWindow = window;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!someWindow)
|
|
return;
|
|
|
|
if (m_context.isNull()) {
|
|
m_context.reset(new QOpenGLContext());
|
|
m_context->setFormat(someWindow->window()->requestedFormat());
|
|
m_context->setScreen(screen()->screen());
|
|
m_context->create();
|
|
}
|
|
|
|
bool ok = m_context->makeCurrent(someWindow->window());
|
|
if (!ok)
|
|
return;
|
|
|
|
if (!m_blitter->isCreated())
|
|
m_blitter->create();
|
|
|
|
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);
|
|
|
|
m_blitter->bind();
|
|
m_blitter->setRedBlueSwizzle(true);
|
|
|
|
for (QWasmWindow *window : qAsConst(m_windowStack)) {
|
|
QWasmCompositedWindow &compositedWindow = m_compositedWindows[window];
|
|
|
|
if (!compositedWindow.visible)
|
|
continue;
|
|
|
|
drawWindow(m_blitter.data(), screen(), window);
|
|
}
|
|
|
|
m_blitter->release();
|
|
|
|
if (someWindow && someWindow->window()->surfaceType() == QSurface::OpenGLSurface)
|
|
m_context->swapBuffers(someWindow->window());
|
|
}
|
|
|
|
void QWasmCompositor::notifyTopWindowChanged(QWasmWindow *window)
|
|
{
|
|
QWindow *modalWindow;
|
|
bool blocked = QGuiApplicationPrivate::instance()->isWindowBlocked(window->window(), &modalWindow);
|
|
|
|
if (blocked) {
|
|
modalWindow->requestActivate();
|
|
raise(static_cast<QWasmWindow*>(modalWindow->handle()));
|
|
return;
|
|
}
|
|
|
|
requestUpdate();
|
|
}
|
|
|
|
QWasmScreen *QWasmCompositor::screen()
|
|
{
|
|
return static_cast<QWasmScreen *>(parent());
|
|
}
|
|
|
|
QOpenGLContext *QWasmCompositor::context()
|
|
{
|
|
return m_context.data();
|
|
}
|