1 /****************************************************************************
2 **
3 ** Copyright (C) 2018 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 or (at your option) any later version
20 ** approved by the KDE Free Qt Foundation. The licenses are as published by
21 ** the Free Software Foundation and appearing in the file LICENSE.GPL3
22 ** included in the packaging of this file. Please review the following
23 ** information to ensure the GNU General Public License requirements will
24 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25 **
26 ** $QT_END_LICENSE$
27 **
28 ****************************************************************************/
29 
30 #include "qwasmcompositor.h"
31 #include "qwasmwindow.h"
32 #include "qwasmstylepixmaps_p.h"
33 
34 #include <QtGui/qopengltexture.h>
35 
36 #include <QtGui/private/qwindow_p.h>
37 #include <QtGui/qopenglcontext.h>
38 #include <QtGui/qopenglfunctions.h>
39 #include <QtGui/qopengltextureblitter.h>
40 #include <QtGui/qoffscreensurface.h>
41 #include <QtGui/qpainter.h>
42 #include <private/qpixmapcache_p.h>
43 
44 #include <private/qguiapplication_p.h>
45 
46 #include <qpa/qwindowsysteminterface.h>
47 #include <QtCore/qcoreapplication.h>
48 #include <QtGui/qguiapplication.h>
49 
50 Q_GUI_EXPORT int qt_defaultDpiX();
51 
QWasmCompositedWindow()52 QWasmCompositedWindow::QWasmCompositedWindow()
53     : window(nullptr)
54     , parentWindow(nullptr)
55     , flushPending(false)
56     , visible(false)
57 {
58 }
59 
QWasmCompositor(QWasmScreen * screen)60 QWasmCompositor::QWasmCompositor(QWasmScreen *screen)
61     :QObject(screen)
62     , m_blitter(new QOpenGLTextureBlitter)
63     , m_needComposit(false)
64     , m_inFlush(false)
65     , m_inResize(false)
66     , m_isEnabled(true)
67     , m_targetDevicePixelRatio(1)
68 {
69 }
70 
~QWasmCompositor()71 QWasmCompositor::~QWasmCompositor()
72 {
73     destroy();
74 }
75 
destroy()76 void QWasmCompositor::destroy()
77 {
78     // Destroy OpenGL resources. This is done here in a separate function
79     // which can be called while screen() still returns a valid screen
80     // (which it might not, during destruction). A valid QScreen is
81     // a requirement for QOffscreenSurface on Wasm since the native
82     // context is tied to a single canvas.
83     if (m_context) {
84         QOffscreenSurface offScreenSurface(screen()->screen());
85         offScreenSurface.setFormat(m_context->format());
86         offScreenSurface.create();
87         m_context->makeCurrent(&offScreenSurface);
88         for (QWasmWindow *window : m_windowStack)
89             window->destroy();
90         m_blitter.reset(nullptr);
91         m_context.reset(nullptr);
92     }
93 
94     m_isEnabled = false; // prevent frame() from creating a new m_context
95 }
96 
setEnabled(bool enabled)97 void QWasmCompositor::setEnabled(bool enabled)
98 {
99     m_isEnabled = enabled;
100 }
101 
addWindow(QWasmWindow * window,QWasmWindow * parentWindow)102 void QWasmCompositor::addWindow(QWasmWindow *window, QWasmWindow *parentWindow)
103 {
104     QWasmCompositedWindow compositedWindow;
105     compositedWindow.window = window;
106     compositedWindow.parentWindow = parentWindow;
107     m_compositedWindows.insert(window, compositedWindow);
108 
109     if (parentWindow == 0)
110         m_windowStack.append(window);
111     else
112         m_compositedWindows[parentWindow].childWindows.append(window);
113 
114     notifyTopWindowChanged(window);
115 }
116 
removeWindow(QWasmWindow * window)117 void QWasmCompositor::removeWindow(QWasmWindow *window)
118 {
119     QWasmWindow *platformWindow = m_compositedWindows[window].parentWindow;
120 
121     if (platformWindow) {
122         QWasmWindow *parentWindow = window;
123         m_compositedWindows[parentWindow].childWindows.removeAll(window);
124     }
125 
126     m_windowStack.removeAll(window);
127     m_compositedWindows.remove(window);
128 
129     notifyTopWindowChanged(window);
130 }
131 
setVisible(QWasmWindow * window,bool visible)132 void QWasmCompositor::setVisible(QWasmWindow *window, bool visible)
133 {
134     QWasmCompositedWindow &compositedWindow = m_compositedWindows[window];
135     if (compositedWindow.visible == visible)
136         return;
137 
138     compositedWindow.visible = visible;
139     compositedWindow.flushPending = true;
140     if (visible)
141         compositedWindow.damage = compositedWindow.window->geometry();
142     else
143         m_globalDamage = compositedWindow.window->geometry(); // repaint previosly covered area.
144 
145     requestRedraw();
146 }
147 
raise(QWasmWindow * window)148 void QWasmCompositor::raise(QWasmWindow *window)
149 {
150     if (m_compositedWindows.size() <= 1)
151         return;
152 
153     QWasmCompositedWindow &compositedWindow = m_compositedWindows[window];
154     compositedWindow.damage = compositedWindow.window->geometry();
155     m_windowStack.removeAll(window);
156     m_windowStack.append(window);
157 
158     notifyTopWindowChanged(window);
159 }
160 
lower(QWasmWindow * window)161 void QWasmCompositor::lower(QWasmWindow *window)
162 {
163     if (m_compositedWindows.size() <= 1)
164         return;
165 
166     m_windowStack.removeAll(window);
167     m_windowStack.prepend(window);
168     QWasmCompositedWindow &compositedWindow = m_compositedWindows[window];
169     m_globalDamage = compositedWindow.window->geometry(); // repaint previosly covered area.
170 
171     notifyTopWindowChanged(window);
172 }
173 
setParent(QWasmWindow * window,QWasmWindow * parent)174 void QWasmCompositor::setParent(QWasmWindow *window, QWasmWindow *parent)
175 {
176     m_compositedWindows[window].parentWindow = parent;
177 
178     requestRedraw();
179 }
180 
flush(QWasmWindow * window,const QRegion & region)181 void QWasmCompositor::flush(QWasmWindow *window, const QRegion &region)
182 {
183     QWasmCompositedWindow &compositedWindow = m_compositedWindows[window];
184     compositedWindow.flushPending = true;
185     compositedWindow.damage = region;
186 
187     requestRedraw();
188 }
189 
windowCount() const190 int QWasmCompositor::windowCount() const
191 {
192     return m_windowStack.count();
193 }
194 
195 
redrawWindowContent()196 void QWasmCompositor::redrawWindowContent()
197 {
198     // Redraw window content by sending expose events. This redraw
199     // will cause a backing store flush, which will call requestRedraw()
200     // to composit.
201     for (QWasmWindow *platformWindow : m_windowStack) {
202         QWindow *window = platformWindow->window();
203         QWindowSystemInterface::handleExposeEvent<QWindowSystemInterface::SynchronousDelivery>(
204             window, QRect(QPoint(0, 0), window->geometry().size()));
205     }
206 }
207 
requestRedraw()208 void QWasmCompositor::requestRedraw()
209 {
210     if (m_needComposit)
211         return;
212 
213     m_needComposit = true;
214     QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
215 }
216 
windowAt(QPoint globalPoint,int padding) const217 QWindow *QWasmCompositor::windowAt(QPoint globalPoint, int padding) const
218 {
219     int index = m_windowStack.count() - 1;
220     // qDebug() << "window at" << "point" << p << "window count" << index;
221 
222     while (index >= 0) {
223         const QWasmCompositedWindow &compositedWindow = m_compositedWindows[m_windowStack.at(index)];
224         //qDebug() << "windwAt testing" << compositedWindow.window <<
225 
226         QRect geometry = compositedWindow.window->windowFrameGeometry()
227                          .adjusted(-padding, -padding, padding, padding);
228 
229         if (compositedWindow.visible && geometry.contains(globalPoint))
230             return m_windowStack.at(index)->window();
231         --index;
232     }
233 
234     return 0;
235 }
236 
keyWindow() const237 QWindow *QWasmCompositor::keyWindow() const
238 {
239     return m_windowStack.at(m_windowStack.count() - 1)->window();
240 }
241 
event(QEvent * ev)242 bool QWasmCompositor::event(QEvent *ev)
243 {
244     if (ev->type() == QEvent::UpdateRequest) {
245         if (m_isEnabled)
246             frame();
247         return true;
248     }
249 
250     return QObject::event(ev);
251 }
252 
blit(QOpenGLTextureBlitter * blitter,QWasmScreen * screen,const QOpenGLTexture * texture,QRect targetGeometry)253 void QWasmCompositor::blit(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, const QOpenGLTexture *texture, QRect targetGeometry)
254 {
255     QMatrix4x4 m;
256     m.translate(-1.0f, -1.0f);
257 
258     m.scale(2.0f / (float)screen->geometry().width(),
259             2.0f / (float)screen->geometry().height());
260 
261     m.translate((float)targetGeometry.width() / 2.0f,
262                 (float)-targetGeometry.height() / 2.0f);
263 
264     m.translate(targetGeometry.x(), screen->geometry().height() - targetGeometry.y());
265 
266     m.scale(0.5f * (float)targetGeometry.width(),
267             0.5f * (float)targetGeometry.height());
268 
269     blitter->blit(texture->textureId(), m, QOpenGLTextureBlitter::OriginTopLeft);
270 }
271 
drawWindowContent(QOpenGLTextureBlitter * blitter,QWasmScreen * screen,QWasmWindow * window)272 void QWasmCompositor::drawWindowContent(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window)
273 {
274     QWasmBackingStore *backingStore = window->backingStore();
275     if (!backingStore)
276         return;
277 
278     QOpenGLTexture const *texture = backingStore->getUpdatedTexture();
279     QPoint windowCanvasPosition = window->geometry().topLeft() - screen->geometry().topLeft();
280     QRect windowCanvasGeometry = QRect(windowCanvasPosition, window->geometry().size());
281     blit(blitter, screen, texture, windowCanvasGeometry);
282 }
283 
makeWindowPalette()284 QPalette QWasmCompositor::makeWindowPalette()
285 {
286     QPalette palette;
287     palette.setColor(QPalette::Active, QPalette::Highlight,
288                      palette.color(QPalette::Active, QPalette::Highlight));
289     palette.setColor(QPalette::Active, QPalette::Base,
290                      palette.color(QPalette::Active, QPalette::Highlight));
291     palette.setColor(QPalette::Inactive, QPalette::Highlight,
292                      palette.color(QPalette::Inactive, QPalette::Dark));
293     palette.setColor(QPalette::Inactive, QPalette::Base,
294                      palette.color(QPalette::Inactive, QPalette::Dark));
295     palette.setColor(QPalette::Inactive, QPalette::HighlightedText,
296                      palette.color(QPalette::Inactive, QPalette::Window));
297 
298     return palette;
299 }
300 
titlebarRect(QWasmTitleBarOptions tb,QWasmCompositor::SubControls subcontrol)301 QRect QWasmCompositor::titlebarRect(QWasmTitleBarOptions tb, QWasmCompositor::SubControls subcontrol)
302 {
303     QRect ret;
304     const int controlMargin = 2;
305     const int controlHeight = tb.rect.height() - controlMargin *2;
306     const int delta = controlHeight + controlMargin;
307     int offset = 0;
308 
309     bool isMinimized = tb.state & Qt::WindowMinimized;
310     bool isMaximized = tb.state & Qt::WindowMaximized;
311 
312     ret = tb.rect;
313     switch (subcontrol) {
314     case SC_TitleBarLabel:
315         if (tb.flags & Qt::WindowSystemMenuHint)
316             ret.adjust(delta, 0, -delta, 0);
317         break;
318     case SC_TitleBarCloseButton:
319         if (tb.flags & Qt::WindowSystemMenuHint) {
320             ret.adjust(0, 0, -delta, 0);
321             offset += delta;
322         }
323         break;
324     case SC_TitleBarMaxButton:
325         if (!isMaximized && tb.flags & Qt::WindowMaximizeButtonHint) {
326             ret.adjust(0, 0, -delta*2, 0);
327             offset += (delta +delta);
328         }
329         break;
330     case SC_TitleBarNormalButton:
331         if (isMinimized && (tb.flags & Qt::WindowMinimizeButtonHint)) {
332             offset += delta;
333         } else if (isMaximized && (tb.flags & Qt::WindowMaximizeButtonHint)) {
334             ret.adjust(0, 0, -delta*2, 0);
335             offset += (delta +delta);
336         }
337         break;
338     case SC_TitleBarSysMenu:
339         if (tb.flags & Qt::WindowSystemMenuHint) {
340             ret.setRect(tb.rect.left() + controlMargin, tb.rect.top() + controlMargin,
341                         controlHeight, controlHeight);
342         }
343         break;
344     default:
345         break;
346     };
347 
348     if (subcontrol != SC_TitleBarLabel && subcontrol != SC_TitleBarSysMenu) {
349         ret.setRect(tb.rect.right() - offset, tb.rect.top() + controlMargin,
350                     controlHeight, controlHeight);
351     }
352 
353     if (qApp->layoutDirection() == Qt::LeftToRight)
354         return ret;
355 
356     QRect rect = ret;
357     rect.translate(2 * (tb.rect.right() - ret.right()) +
358                    ret.width() - tb.rect.width(), 0);
359 
360     return rect;
361 }
362 
dpiScaled(qreal value)363 int dpiScaled(qreal value)
364 {
365     return value * (qreal(qt_defaultDpiX()) / 96.0);
366 }
367 
makeTitleBarOptions(const QWasmWindow * window)368 QWasmCompositor::QWasmTitleBarOptions QWasmCompositor::makeTitleBarOptions(const QWasmWindow *window)
369 {
370     int width = window->windowFrameGeometry().width();
371     int border = window->borderWidth();
372 
373     QWasmTitleBarOptions titleBarOptions;
374 
375     titleBarOptions.rect = QRect(border, border, width - 2 * border, window->titleHeight());
376     titleBarOptions.flags = window->window()->flags();
377     titleBarOptions.state = window->window()->windowState();
378 
379     bool isMaximized = titleBarOptions.state & Qt::WindowMaximized; // this gets reset when maximized
380 
381     if (titleBarOptions.flags & (Qt::WindowTitleHint))
382         titleBarOptions.subControls |= SC_TitleBarLabel;
383     if (titleBarOptions.flags & Qt::WindowMaximizeButtonHint) {
384         if (isMaximized)
385             titleBarOptions.subControls |= SC_TitleBarNormalButton;
386         else
387             titleBarOptions.subControls |= SC_TitleBarMaxButton;
388     }
389     if (titleBarOptions.flags & Qt::WindowSystemMenuHint) {
390         titleBarOptions.subControls |= SC_TitleBarCloseButton;
391         titleBarOptions.subControls |= SC_TitleBarSysMenu;
392     }
393 
394 
395     titleBarOptions.palette = QWasmCompositor::makeWindowPalette();
396 
397     if (window->window()->isActive())
398         titleBarOptions.palette.setCurrentColorGroup(QPalette::Active);
399     else
400         titleBarOptions.palette.setCurrentColorGroup(QPalette::Inactive);
401 
402     if (window->activeSubControl() != QWasmCompositor::SC_None)
403         titleBarOptions.subControls = window->activeSubControl();
404 
405     if (!window->window()->title().isEmpty())
406         titleBarOptions.titleBarOptionsString = window->window()->title();
407 
408     return titleBarOptions;
409 }
410 
drawWindowDecorations(QOpenGLTextureBlitter * blitter,QWasmScreen * screen,QWasmWindow * window)411 void QWasmCompositor::drawWindowDecorations(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window)
412 {
413     int width = window->windowFrameGeometry().width();
414     int height = window->windowFrameGeometry().height();
415     qreal dpr = window->devicePixelRatio();
416 
417     QImage image(QSize(width * dpr, height * dpr), QImage::Format_RGB32);
418     image.setDevicePixelRatio(dpr);
419     QPainter painter(&image);
420     painter.fillRect(QRect(0, 0, width, height), painter.background());
421 
422     QWasmTitleBarOptions titleBarOptions = makeTitleBarOptions(window);
423 
424     drawTitlebarWindow(titleBarOptions, &painter);
425 
426     QWasmFrameOptions frameOptions;
427     frameOptions.rect = QRect(0, 0, width, height);
428     frameOptions.lineWidth = dpiScaled(4.);
429 
430     drawFrameWindow(frameOptions, &painter);
431 
432     painter.end();
433 
434     QOpenGLTexture texture(QOpenGLTexture::Target2D);
435     texture.setMinificationFilter(QOpenGLTexture::Nearest);
436     texture.setMagnificationFilter(QOpenGLTexture::Nearest);
437     texture.setWrapMode(QOpenGLTexture::ClampToEdge);
438     texture.setData(image, QOpenGLTexture::DontGenerateMipMaps);
439     texture.create();
440     texture.bind();
441 
442     glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image.width(), image.height(), GL_RGBA, GL_UNSIGNED_BYTE,
443                     image.constScanLine(0));
444 
445     blit(blitter, screen, &texture, QRect(window->windowFrameGeometry().topLeft(), QSize(width, height)));
446 }
447 
drawFrameWindow(QWasmFrameOptions options,QPainter * painter)448 void QWasmCompositor::drawFrameWindow(QWasmFrameOptions options, QPainter *painter)
449 {
450     int x = options.rect.x();
451     int y = options.rect.y();
452     int w = options.rect.width();
453     int h = options.rect.height();
454     const QColor &c1 = options.palette.light().color();
455     const QColor &c2 = options.palette.shadow().color();
456     const QColor &c3 = options.palette.midlight().color();
457     const QColor &c4 = options.palette.dark().color();
458     const QBrush *fill = 0;
459 
460     const qreal devicePixelRatio = painter->device()->devicePixelRatioF();
461     if (!qFuzzyCompare(devicePixelRatio, qreal(1))) {
462         const qreal inverseScale = qreal(1) / devicePixelRatio;
463         painter->scale(inverseScale, inverseScale);
464         x = qRound(devicePixelRatio * x);
465         y = qRound(devicePixelRatio * y);
466         w = qRound(devicePixelRatio * w);
467         h = qRound(devicePixelRatio * h);
468     }
469 
470     QPen oldPen = painter->pen();
471     QPoint a[3] = { QPoint(x, y+h-2), QPoint(x, y), QPoint(x+w-2, y) };
472     painter->setPen(c1);
473     painter->drawPolyline(a, 3);
474     QPoint b[3] = { QPoint(x, y+h-1), QPoint(x+w-1, y+h-1), QPoint(x+w-1, y) };
475     painter->setPen(c2);
476     painter->drawPolyline(b, 3);
477     if (w > 4 && h > 4) {
478         QPoint c[3] = { QPoint(x+1, y+h-3), QPoint(x+1, y+1), QPoint(x+w-3, y+1) };
479         painter->setPen(c3);
480         painter->drawPolyline(c, 3);
481         QPoint d[3] = { QPoint(x+1, y+h-2), QPoint(x+w-2, y+h-2), QPoint(x+w-2, y+1) };
482         painter->setPen(c4);
483         painter->drawPolyline(d, 3);
484         if (fill)
485             painter->fillRect(QRect(x+2, y+2, w-4, h-4), *fill);
486     }
487     painter->setPen(oldPen);
488 }
489 
490 //from commonstyle.cpp
cachedPixmapFromXPM(const char * const * xpm)491 static QPixmap cachedPixmapFromXPM(const char * const *xpm)
492 {
493     QPixmap result;
494     const QString tag = QString::asprintf("xpm:0x%p", static_cast<const void*>(xpm));
495     if (!QPixmapCache::find(tag, &result)) {
496         result = QPixmap(xpm);
497         QPixmapCache::insert(tag, result);
498     }
499     return result;
500 }
501 
drawItemPixmap(QPainter * painter,const QRect & rect,int alignment,const QPixmap & pixmap) const502 void QWasmCompositor::drawItemPixmap(QPainter *painter, const QRect &rect, int alignment,
503                                       const QPixmap &pixmap) const
504 {
505     qreal scale = pixmap.devicePixelRatio();
506     QSize size =  pixmap.size() / scale;
507     int x = rect.x();
508     int y = rect.y();
509     int w = size.width();
510     int h = size.height();
511     if ((alignment & Qt::AlignVCenter) == Qt::AlignVCenter)
512         y += rect.size().height()/2 - h/2;
513     else if ((alignment & Qt::AlignBottom) == Qt::AlignBottom)
514         y += rect.size().height() - h;
515     if ((alignment & Qt::AlignRight) == Qt::AlignRight)
516         x += rect.size().width() - w;
517     else if ((alignment & Qt::AlignHCenter) == Qt::AlignHCenter)
518         x += rect.size().width()/2 - w/2;
519 
520     QRect aligned = QRect(x, y, w, h);
521     QRect inter = aligned.intersected(rect);
522 
523     painter->drawPixmap(inter.x(), inter.y(), pixmap, inter.x() - aligned.x(), inter.y() - aligned.y(), inter.width() * scale, inter.height() *scale);
524 }
525 
526 
drawTitlebarWindow(QWasmTitleBarOptions tb,QPainter * painter)527 void QWasmCompositor::drawTitlebarWindow(QWasmTitleBarOptions tb, QPainter *painter)
528 {
529     QRect ir;
530     if (tb.subControls.testFlag(SC_TitleBarLabel)) {
531         QColor left = tb.palette.highlight().color();
532         QColor right = tb.palette.base().color();
533 
534         QBrush fillBrush(left);
535         if (left != right) {
536             QPoint p1(tb.rect.x(), tb.rect.top() + tb.rect.height()/2);
537             QPoint p2(tb.rect.right(), tb.rect.top() + tb.rect.height()/2);
538             QLinearGradient lg(p1, p2);
539             lg.setColorAt(0, left);
540             lg.setColorAt(1, right);
541             fillBrush = lg;
542         }
543 
544         painter->fillRect(tb.rect, fillBrush);
545         ir = titlebarRect(tb, SC_TitleBarLabel);
546         painter->setPen(tb.palette.highlightedText().color());
547         painter->drawText(ir.x() + 2, ir.y(), ir.width() - 2, ir.height(),
548                           Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb.titleBarOptionsString);
549     } // SC_TitleBarLabel
550 
551     bool down = false;
552     QPixmap pixmap;
553 
554     if (tb.subControls.testFlag(SC_TitleBarCloseButton)
555             && tb.flags & Qt::WindowSystemMenuHint) {
556         ir = titlebarRect(tb, SC_TitleBarCloseButton);
557         down = tb.subControls & SC_TitleBarCloseButton && (tb.state & State_Sunken);
558         pixmap = cachedPixmapFromXPM(qt_close_xpm).scaled(QSize(10, 10));
559         drawItemPixmap(painter, ir, Qt::AlignCenter, pixmap);
560     } //SC_TitleBarCloseButton
561 
562     if (tb.subControls.testFlag(SC_TitleBarMaxButton)
563             && tb.flags & Qt::WindowMaximizeButtonHint
564             && !(tb.state & Qt::WindowMaximized)) {
565         ir = titlebarRect(tb, SC_TitleBarMaxButton);
566         down = tb.subControls & SC_TitleBarMaxButton && (tb.state & State_Sunken);
567         pixmap = cachedPixmapFromXPM(qt_maximize_xpm).scaled(QSize(10, 10));
568         drawItemPixmap(painter, ir, Qt::AlignCenter, pixmap);
569     } //SC_TitleBarMaxButton
570 
571     bool drawNormalButton = (tb.subControls & SC_TitleBarNormalButton)
572             && (((tb.flags & Qt::WindowMinimizeButtonHint)
573                  && (tb.flags & Qt::WindowMinimized))
574                 || ((tb.flags & Qt::WindowMaximizeButtonHint)
575                     && (tb.flags & Qt::WindowMaximized)));
576 
577     if (drawNormalButton) {
578         ir = titlebarRect(tb, SC_TitleBarNormalButton);
579         down = tb.subControls & SC_TitleBarNormalButton && (tb.state & State_Sunken);
580         pixmap = cachedPixmapFromXPM(qt_normalizeup_xpm).scaled( QSize(10, 10));
581 
582         drawItemPixmap(painter, ir, Qt::AlignCenter, pixmap);
583     } // SC_TitleBarNormalButton
584 
585     if (tb.subControls & SC_TitleBarSysMenu && tb.flags & Qt::WindowSystemMenuHint) {
586         ir = titlebarRect(tb, SC_TitleBarSysMenu);
587         pixmap = cachedPixmapFromXPM(qt_menu_xpm).scaled(QSize(10, 10));
588         drawItemPixmap(painter, ir, Qt::AlignCenter, pixmap);
589     }
590 }
591 
drawShadePanel(QWasmTitleBarOptions options,QPainter * painter)592 void QWasmCompositor::drawShadePanel(QWasmTitleBarOptions options, QPainter *painter)
593 {
594     int lineWidth = 1;
595     QPalette palette = options.palette;
596     const QBrush *fill = &options.palette.brush(QPalette::Button);
597 
598     int x = options.rect.x();
599     int y = options.rect.y();
600     int w = options.rect.width();
601     int h = options.rect.height();
602 
603     const qreal devicePixelRatio = painter->device()->devicePixelRatioF();
604     if (!qFuzzyCompare(devicePixelRatio, qreal(1))) {
605         const qreal inverseScale = qreal(1) / devicePixelRatio;
606         painter->scale(inverseScale, inverseScale);
607 
608         x = qRound(devicePixelRatio * x);
609         y = qRound(devicePixelRatio * y);
610         w = qRound(devicePixelRatio * w);
611         h = qRound(devicePixelRatio * h);
612         lineWidth = qRound(devicePixelRatio * lineWidth);
613     }
614 
615     QColor shade = palette.dark().color();
616     QColor light = palette.light().color();
617 
618     if (fill) {
619         if (fill->color() == shade)
620             shade = palette.shadow().color();
621         if (fill->color() == light)
622             light = palette.midlight().color();
623     }
624     QPen oldPen = painter->pen();
625     QVector<QLineF> lines;
626     lines.reserve(2*lineWidth);
627 
628     painter->setPen(light);
629     int x1, y1, x2, y2;
630     int i;
631     x1 = x;
632     y1 = y2 = y;
633     x2 = x + w - 2;
634     for (i = 0; i < lineWidth; i++)                // top shadow
635         lines << QLineF(x1, y1++, x2--, y2++);
636 
637     x2 = x1;
638     y1 = y + h - 2;
639     for (i = 0; i < lineWidth; i++)               // left shado
640         lines << QLineF(x1++, y1, x2++, y2--);
641 
642     painter->drawLines(lines);
643     lines.clear();
644     painter->setPen(shade);
645     x1 = x;
646     y1 = y2 = y+h-1;
647     x2 = x+w-1;
648     for (i=0; i<lineWidth; i++) {                // bottom shadow
649         lines << QLineF(x1++, y1--, x2, y2--);
650     }
651     x1 = x2;
652     y1 = y;
653     y2 = y + h - lineWidth - 1;
654     for (i = 0; i < lineWidth; i++)                // right shadow
655         lines << QLineF(x1--, y1++, x2--, y2);
656 
657     painter->drawLines(lines);
658     if (fill)                                // fill with fill color
659         painter->fillRect(x+lineWidth, y+lineWidth, w-lineWidth*2, h-lineWidth*2, *fill);
660     painter->setPen(oldPen);                        // restore pen
661 
662 }
663 
drawWindow(QOpenGLTextureBlitter * blitter,QWasmScreen * screen,QWasmWindow * window)664 void QWasmCompositor::drawWindow(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window)
665 {
666     if (window->window()->type() != Qt::Popup && !(window->m_windowState & Qt::WindowFullScreen))
667         drawWindowDecorations(blitter, screen, window);
668     drawWindowContent(blitter, screen, window);
669 }
670 
frame()671 void QWasmCompositor::frame()
672 {
673     if (!m_needComposit)
674         return;
675 
676     m_needComposit = false;
677 
678     if (!m_isEnabled || m_windowStack.empty() || !screen())
679         return;
680 
681     QWasmWindow *someWindow = nullptr;
682 
683     for (QWasmWindow *window : qAsConst(m_windowStack)) {
684         if (window->window()->surfaceClass() == QSurface::Window
685                 && qt_window_private(static_cast<QWindow *>(window->window()))->receivedExpose) {
686             someWindow = window;
687             break;
688         }
689     }
690 
691     if (!someWindow)
692         return;
693 
694     if (m_context.isNull()) {
695         m_context.reset(new QOpenGLContext());
696         m_context->setFormat(someWindow->window()->requestedFormat());
697         m_context->setScreen(screen()->screen());
698         m_context->create();
699     }
700 
701     bool ok = m_context->makeCurrent(someWindow->window());
702     if (!ok)
703         return;
704 
705     if (!m_blitter->isCreated())
706         m_blitter->create();
707 
708     qreal dpr = screen()->devicePixelRatio();
709     glViewport(0, 0, screen()->geometry().width() * dpr, screen()->geometry().height() * dpr);
710 
711     m_context->functions()->glClearColor(0.2, 0.2, 0.2, 1.0);
712     m_context->functions()->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
713 
714     m_blitter->bind();
715     m_blitter->setRedBlueSwizzle(true);
716 
717     for (QWasmWindow *window : qAsConst(m_windowStack)) {
718         QWasmCompositedWindow &compositedWindow = m_compositedWindows[window];
719 
720         if (!compositedWindow.visible)
721             continue;
722 
723         drawWindow(m_blitter.data(), screen(), window);
724     }
725 
726     m_blitter->release();
727 
728     if (someWindow && someWindow->window()->surfaceType() == QSurface::OpenGLSurface)
729         m_context->swapBuffers(someWindow->window());
730 }
731 
notifyTopWindowChanged(QWasmWindow * window)732 void QWasmCompositor::notifyTopWindowChanged(QWasmWindow *window)
733 {
734     QWindow *modalWindow;
735     bool blocked = QGuiApplicationPrivate::instance()->isWindowBlocked(window->window(), &modalWindow);
736 
737     if (blocked) {
738         raise(static_cast<QWasmWindow*>(modalWindow->handle()));
739         return;
740     }
741 
742     requestRedraw();
743     QWindowSystemInterface::handleWindowActivated(window->window());
744 }
745 
screen()746 QWasmScreen *QWasmCompositor::screen()
747 {
748     return static_cast<QWasmScreen *>(parent());
749 }
750 
context()751 QOpenGLContext *QWasmCompositor::context()
752 {
753     return m_context.data();
754 }
755