Skip to content

Commit dde7e14

Browse files
committed
core/window: allow explicit surface format selection
1 parent dc3a796 commit dde7e14

10 files changed

+131
-5
lines changed

src/wayland/wlr_layershell.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ WaylandPanelInterface::WaylandPanelInterface(QObject* parent)
196196
QObject::connect(this->layer, &ProxyWindowBase::windowTransformChanged, this, &WaylandPanelInterface::windowTransformChanged);
197197
QObject::connect(this->layer, &ProxyWindowBase::colorChanged, this, &WaylandPanelInterface::colorChanged);
198198
QObject::connect(this->layer, &ProxyWindowBase::maskChanged, this, &WaylandPanelInterface::maskChanged);
199+
QObject::connect(this->layer, &ProxyWindowBase::surfaceFormatChanged, this, &WaylandPanelInterface::surfaceFormatChanged);
199200

200201
// panel specific
201202
QObject::connect(this->layer, &WlrLayershell::anchorsChanged, this, &WaylandPanelInterface::anchorsChanged);
@@ -232,6 +233,7 @@ proxyPair(qint32, height, setHeight);
232233
proxyPair(QuickshellScreenInfo*, screen, setScreen);
233234
proxyPair(QColor, color, setColor);
234235
proxyPair(PendingRegion*, mask, setMask);
236+
proxyPair(QsSurfaceFormat, surfaceFormat, setSurfaceFormat);
235237

236238
// panel specific
237239
proxyPair(Anchors, anchors, setAnchors);

src/wayland/wlr_layershell.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ class WaylandPanelInterface: public PanelWindowInterface {
155155
[[nodiscard]] PendingRegion* mask() const override;
156156
void setMask(PendingRegion* mask) override;
157157

158+
[[nodiscard]] QsSurfaceFormat surfaceFormat() const override;
159+
void setSurfaceFormat(QsSurfaceFormat mask) override;
160+
158161
[[nodiscard]] QQmlListProperty<QObject> data() override;
159162

160163
// panel specific

src/window/floatingwindow.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ FloatingWindowInterface::FloatingWindowInterface(QObject* parent)
3636
QObject::connect(this->window, &ProxyWindowBase::windowTransformChanged, this, &FloatingWindowInterface::windowTransformChanged);
3737
QObject::connect(this->window, &ProxyWindowBase::colorChanged, this, &FloatingWindowInterface::colorChanged);
3838
QObject::connect(this->window, &ProxyWindowBase::maskChanged, this, &FloatingWindowInterface::maskChanged);
39+
QObject::connect(this->window, &ProxyWindowBase::surfaceFormatChanged, this, &FloatingWindowInterface::surfaceFormatChanged);
3940
// clang-format on
4041
}
4142

@@ -64,6 +65,7 @@ proxyPair(qint32, height, setHeight);
6465
proxyPair(QuickshellScreenInfo*, screen, setScreen);
6566
proxyPair(QColor, color, setColor);
6667
proxyPair(PendingRegion*, mask, setMask);
68+
proxyPair(QsSurfaceFormat, surfaceFormat, setSurfaceFormat);
6769

6870
#undef proxyPair
6971
// NOLINTEND

src/window/floatingwindow.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <qtmetamacros.h>
55

66
#include "proxywindow.hpp"
7+
#include "windowinterface.hpp"
78

89
class ProxyFloatingWindow: public ProxyWindowBase {
910
Q_OBJECT;
@@ -50,6 +51,9 @@ class FloatingWindowInterface: public WindowInterface {
5051
[[nodiscard]] PendingRegion* mask() const override;
5152
void setMask(PendingRegion* mask) override;
5253

54+
[[nodiscard]] QsSurfaceFormat surfaceFormat() const override;
55+
void setSurfaceFormat(QsSurfaceFormat mask) override;
56+
5357
[[nodiscard]] QQmlListProperty<QObject> data() override;
5458
// NOLINTEND
5559

src/window/panelinterface.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ class Anchors {
1313
Q_PROPERTY(bool right MEMBER mRight);
1414
Q_PROPERTY(bool top MEMBER mTop);
1515
Q_PROPERTY(bool bottom MEMBER mBottom);
16-
QML_VALUE_TYPE(anchors);
16+
QML_VALUE_TYPE(panelAnchors);
17+
QML_STRUCTURED_VALUE;
1718

1819
public:
1920
[[nodiscard]] bool horizontalConstraint() const noexcept { return this->mLeft && this->mRight; }
@@ -40,7 +41,8 @@ class Margins {
4041
Q_PROPERTY(qint32 right MEMBER mRight);
4142
Q_PROPERTY(qint32 top MEMBER mTop);
4243
Q_PROPERTY(qint32 bottom MEMBER mBottom);
43-
QML_VALUE_TYPE(margins);
44+
QML_VALUE_TYPE(panelMargins);
45+
QML_STRUCTURED_VALUE;
4446

4547
public:
4648
[[nodiscard]] bool operator==(const Margins& other) const noexcept {

src/window/proxywindow.cpp

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@
66
#include <qobject.h>
77
#include <qqmlcontext.h>
88
#include <qqmlengine.h>
9+
#include <qqmlinfo.h>
910
#include <qqmllist.h>
1011
#include <qquickitem.h>
1112
#include <qquickwindow.h>
1213
#include <qregion.h>
14+
#include <qsurfaceformat.h>
15+
#include <qtenvironmentvariables.h>
1316
#include <qtmetamacros.h>
1417
#include <qtypes.h>
1518
#include <qvariant.h>
@@ -50,7 +53,7 @@ ProxyWindowBase::~ProxyWindowBase() { this->deleteWindow(true); }
5053
void ProxyWindowBase::onReload(QObject* oldInstance) {
5154
this->window = this->retrieveWindow(oldInstance);
5255
auto wasVisible = this->window != nullptr && this->window->isVisible();
53-
if (this->window == nullptr) this->window = this->createQQuickWindow();
56+
this->ensureQWindow();
5457

5558
// The qml engine will leave the WindowInterface as owner of everything
5659
// nested in an item, so we have to make sure the interface's children
@@ -85,10 +88,55 @@ void ProxyWindowBase::postCompleteWindow() { this->setVisible(this->mVisible); }
8588

8689
ProxiedWindow* ProxyWindowBase::createQQuickWindow() { return new ProxiedWindow(this); }
8790

88-
void ProxyWindowBase::createWindow() {
89-
if (this->window != nullptr) return;
91+
void ProxyWindowBase::ensureQWindow() {
92+
auto format = QSurfaceFormat::defaultFormat();
93+
94+
{
95+
// match QtQuick's default format, including env var controls
96+
static const auto useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER");
97+
static const auto useStencil = qEnvironmentVariableIsEmpty("QSG_NO_STENCIL_BUFFER");
98+
static const auto enableDebug = qEnvironmentVariableIsSet("QSG_OPENGL_DEBUG");
99+
static const auto disableVSync = qEnvironmentVariableIsSet("QSG_NO_VSYNC");
100+
101+
if (useDepth && format.depthBufferSize() == -1) format.setDepthBufferSize(24);
102+
else if (!useDepth) format.setDepthBufferSize(0);
103+
104+
if (useStencil && format.stencilBufferSize() == -1) format.setStencilBufferSize(8);
105+
else if (!useStencil) format.setStencilBufferSize(0);
106+
107+
auto opaque = this->qsSurfaceFormat.opaqueModified ? this->qsSurfaceFormat.opaque
108+
: this->mColor.alpha() >= 255;
109+
110+
if (opaque) format.setAlphaBufferSize(0);
111+
else format.setAlphaBufferSize(8);
112+
113+
if (enableDebug) format.setOption(QSurfaceFormat::DebugContext);
114+
if (disableVSync) format.setSwapInterval(0);
115+
116+
format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
117+
format.setRedBufferSize(8);
118+
format.setGreenBufferSize(8);
119+
format.setBlueBufferSize(8);
120+
}
121+
122+
this->mSurfaceFormat = format;
123+
124+
auto useOldWindow = this->window != nullptr;
125+
126+
if (useOldWindow) {
127+
if (this->window->requestedFormat() != format) {
128+
useOldWindow = false;
129+
}
130+
}
131+
132+
if (useOldWindow) return;
133+
delete this->window;
90134
this->window = this->createQQuickWindow();
135+
this->window->setFormat(format);
136+
}
91137

138+
void ProxyWindowBase::createWindow() {
139+
this->ensureQWindow();
92140
this->connectWindow();
93141
this->completeWindow();
94142
emit this->windowConnected();
@@ -320,6 +368,8 @@ void ProxyWindowBase::setColor(QColor color) {
320368
);
321369

322370
this->window->setColor(premultiplied);
371+
// setColor also modifies the alpha buffer size of the surface format
372+
this->window->setFormat(this->mSurfaceFormat);
323373
}
324374
}
325375

@@ -343,6 +393,17 @@ void ProxyWindowBase::setMask(PendingRegion* mask) {
343393
emit this->maskChanged();
344394
}
345395

396+
void ProxyWindowBase::setSurfaceFormat(QsSurfaceFormat format) {
397+
if (format == this->qsSurfaceFormat) return;
398+
if (this->window != nullptr) {
399+
qmlWarning(this) << "Cannot set window surface format.";
400+
return;
401+
}
402+
403+
this->qsSurfaceFormat = format;
404+
emit this->surfaceFormatChanged();
405+
}
406+
346407
void ProxyWindowBase::onMaskChanged() {
347408
if (this->window != nullptr) this->updateMask();
348409
}

src/window/proxywindow.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <qqmlparserstatus.h>
1010
#include <qquickitem.h>
1111
#include <qquickwindow.h>
12+
#include <qsurfaceformat.h>
1213
#include <qtmetamacros.h>
1314
#include <qtypes.h>
1415
#include <qwindow.h>
@@ -46,6 +47,7 @@ class ProxyWindowBase: public Reloadable {
4647
Q_PROPERTY(PendingRegion* mask READ mask WRITE setMask NOTIFY maskChanged);
4748
Q_PROPERTY(QObject* windowTransform READ windowTransform NOTIFY windowTransformChanged);
4849
Q_PROPERTY(bool backingWindowVisible READ isVisibleDirect NOTIFY backerVisibilityChanged);
50+
Q_PROPERTY(QsSurfaceFormat surfaceFormat WRITE setSurfaceFormat NOTIFY surfaceFormatChanged);
4951
Q_PROPERTY(QQmlListProperty<QObject> data READ data);
5052
Q_CLASSINFO("DefaultProperty", "data");
5153

@@ -59,6 +61,7 @@ class ProxyWindowBase: public Reloadable {
5961
void operator=(ProxyWindowBase&&) = delete;
6062

6163
void onReload(QObject* oldInstance) override;
64+
void ensureQWindow();
6265
void createWindow();
6366
void deleteWindow(bool keepItemOwnership = false);
6467

@@ -98,6 +101,9 @@ class ProxyWindowBase: public Reloadable {
98101
[[nodiscard]] PendingRegion* mask() const;
99102
virtual void setMask(PendingRegion* mask);
100103

104+
[[nodiscard]] QsSurfaceFormat surfaceFormat() const { return this->qsSurfaceFormat; }
105+
void setSurfaceFormat(QsSurfaceFormat format);
106+
101107
[[nodiscard]] QObject* windowTransform() const { return nullptr; } // NOLINT
102108

103109
[[nodiscard]] QQmlListProperty<QObject> data();
@@ -115,6 +121,7 @@ class ProxyWindowBase: public Reloadable {
115121
void screenChanged();
116122
void colorChanged();
117123
void maskChanged();
124+
void surfaceFormatChanged();
118125

119126
protected slots:
120127
virtual void onWidthChanged();
@@ -135,6 +142,8 @@ protected slots:
135142
QQuickItem* mContentItem = nullptr;
136143
bool reloadComplete = false;
137144
bool ranLints = false;
145+
QsSurfaceFormat qsSurfaceFormat;
146+
QSurfaceFormat mSurfaceFormat;
138147

139148
private:
140149
void polishItems();

src/window/windowinterface.hpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,26 @@
1515
class ProxyWindowBase;
1616
class QsWindowAttached;
1717

18+
class QsSurfaceFormat {
19+
Q_GADGET;
20+
QML_VALUE_TYPE(surfaceFormat);
21+
QML_STRUCTURED_VALUE;
22+
Q_PROPERTY(bool opaque MEMBER opaque WRITE setOpaque);
23+
24+
public:
25+
bool opaque = false;
26+
bool opaqueModified = false;
27+
28+
void setOpaque(bool opaque) {
29+
this->opaque = opaque;
30+
this->opaqueModified = true;
31+
}
32+
33+
[[nodiscard]] bool operator==(const QsSurfaceFormat& other) const {
34+
return other.opaqueModified == this->opaqueModified && other.opaque == this->opaque;
35+
}
36+
};
37+
1838
///! Base class of Quickshell windows
1939
/// Base class of Quickshell windows
2040
/// ### Attached properties
@@ -46,6 +66,10 @@ class WindowInterface: public Reloadable {
4666
/// along with map[To|From]Item (which is not reactive).
4767
Q_PROPERTY(QObject* windowTransform READ windowTransform NOTIFY windowTransformChanged);
4868
/// The background color of the window. Defaults to white.
69+
///
70+
/// > [!WARNING] If the window color is opaque before it is made visible,
71+
/// > it will not be able to become transparent later unless @@surfaceFormat$.opaque
72+
/// > is false.
4973
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged);
5074
/// The clickthrough mask. Defaults to null.
5175
///
@@ -90,6 +114,16 @@ class WindowInterface: public Reloadable {
90114
/// }
91115
/// ```
92116
Q_PROPERTY(PendingRegion* mask READ mask WRITE setMask NOTIFY maskChanged);
117+
/// Set the surface format to request from the system.
118+
///
119+
/// - `opaque` - If the requested surface should be opaque. Opaque windows allow
120+
/// the operating system to avoid drawing things behind them, or blending the window
121+
/// with those behind it, saving power and GPU load. If unset, this property defaults to
122+
/// true if @@color is opaque, or false if not. *You should not need to modify this
123+
/// property unless you create a surface that starts opaque and later becomes transparent.*
124+
///
125+
/// > [!NOTE] The surface format cannot be changed after the window is created.
126+
Q_PROPERTY(QsSurfaceFormat surfaceFormat WRITE setSurfaceFormat NOTIFY surfaceFormatChanged);
93127
Q_PROPERTY(QQmlListProperty<QObject> data READ data);
94128
// clang-format on
95129
Q_CLASSINFO("DefaultProperty", "data");
@@ -124,6 +158,9 @@ class WindowInterface: public Reloadable {
124158
[[nodiscard]] virtual PendingRegion* mask() const = 0;
125159
virtual void setMask(PendingRegion* mask) = 0;
126160

161+
[[nodiscard]] virtual QsSurfaceFormat surfaceFormat() const = 0;
162+
virtual void setSurfaceFormat(QsSurfaceFormat format) = 0;
163+
127164
[[nodiscard]] virtual QQmlListProperty<QObject> data() = 0;
128165

129166
static QsWindowAttached* qmlAttachedProperties(QObject* object);
@@ -138,6 +175,7 @@ class WindowInterface: public Reloadable {
138175
void windowTransformChanged();
139176
void colorChanged();
140177
void maskChanged();
178+
void surfaceFormatChanged();
141179
};
142180

143181
class QsWindowAttached: public QObject {

src/x11/panel_window.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,7 @@ XPanelInterface::XPanelInterface(QObject* parent)
482482
QObject::connect(this->panel, &ProxyWindowBase::windowTransformChanged, this, &XPanelInterface::windowTransformChanged);
483483
QObject::connect(this->panel, &ProxyWindowBase::colorChanged, this, &XPanelInterface::colorChanged);
484484
QObject::connect(this->panel, &ProxyWindowBase::maskChanged, this, &XPanelInterface::maskChanged);
485+
QObject::connect(this->panel, &ProxyWindowBase::surfaceFormatChanged, this, &XPanelInterface::surfaceFormatChanged);
485486

486487
// panel specific
487488
QObject::connect(this->panel, &XPanelWindow::anchorsChanged, this, &XPanelInterface::anchorsChanged);
@@ -516,6 +517,7 @@ proxyPair(qint32, height, setHeight);
516517
proxyPair(QuickshellScreenInfo*, screen, setScreen);
517518
proxyPair(QColor, color, setColor);
518519
proxyPair(PendingRegion*, mask, setMask);
520+
proxyPair(QsSurfaceFormat, surfaceFormat, setSurfaceFormat);
519521

520522
// panel specific
521523
proxyPair(Anchors, anchors, setAnchors);

src/x11/panel_window.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,9 @@ class XPanelInterface: public PanelWindowInterface {
136136
[[nodiscard]] PendingRegion* mask() const override;
137137
void setMask(PendingRegion* mask) override;
138138

139+
[[nodiscard]] QsSurfaceFormat surfaceFormat() const override;
140+
void setSurfaceFormat(QsSurfaceFormat mask) override;
141+
139142
[[nodiscard]] QQmlListProperty<QObject> data() override;
140143

141144
// panel specific

0 commit comments

Comments
 (0)