1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qwaylandeglwindow_p.h"
5
6#include <QtWaylandClient/private/qwaylandscreen_p.h>
7#include <QtWaylandClient/private/qwaylandsurface_p.h>
8#include "qwaylandglcontext_p.h"
9
10#include <QtGui/private/qeglconvenience_p.h>
11
12#include <QDebug>
13#include <QtGui/QWindow>
14#include <qpa/qwindowsysteminterface.h>
15#include <QOpenGLFramebufferObject>
16#include <QOpenGLContext>
17
18QT_BEGIN_NAMESPACE
19
20namespace QtWaylandClient {
21
22QWaylandEglWindow::QWaylandEglWindow(QWindow *window, QWaylandDisplay *display)
23 : QWaylandWindow(window, display)
24 , m_clientBufferIntegration(static_cast<QWaylandEglClientBufferIntegration *>(mDisplay->clientBufferIntegration()))
25 , m_format(window->requestedFormat())
26{
27 connect(display, &QWaylandDisplay::connected, this, [this] {
28 m_clientBufferIntegration = static_cast<QWaylandEglClientBufferIntegration *>(
29 mDisplay->clientBufferIntegration());
30 });
31 ensureSize();
32}
33
34QWaylandEglWindow::~QWaylandEglWindow()
35{
36 if (m_eglSurface) {
37 eglDestroySurface(dpy: m_clientBufferIntegration->eglDisplay(), surface: m_eglSurface);
38 m_eglSurface = 0;
39 }
40
41 if (m_waylandEglWindow)
42 wl_egl_window_destroy(egl_window: m_waylandEglWindow);
43
44 delete m_contentFBO;
45}
46
47QWaylandWindow::WindowType QWaylandEglWindow::windowType() const
48{
49 return QWaylandWindow::Egl;
50}
51
52void QWaylandEglWindow::ensureSize()
53{
54 // this is always called on the main thread
55 QRect rect = geometry();
56 QMargins margins = clientSideMargins();
57 QSize sizeWithMargins = (rect.size() + QSize(margins.left() + margins.right(), margins.top() + margins.bottom())) * scale();
58 {
59 QWriteLocker lock(&m_bufferSizeLock);
60 m_bufferSize = sizeWithMargins;
61 }
62
63 QMutexLocker lock (&m_eglSurfaceLock);
64 updateSurface(create: false);
65}
66
67void QWaylandEglWindow::setGeometry(const QRect &rect)
68{
69 QWaylandWindow::setGeometry(rect);
70 // If the surface was invalidated through invalidateSurface() and
71 // we're now getting a resize we don't want to create it again.
72 // Just resize the wl_egl_window, the EGLSurface will be created
73 // the next time makeCurrent is called.
74 ensureSize();
75}
76
77void QWaylandEglWindow::updateSurface(bool create)
78{
79 // eglSurfaceLock should be locked before calling this method
80
81 QSize sizeWithMargins;
82 {
83 QReadLocker lock(&m_bufferSizeLock);
84 sizeWithMargins = m_bufferSize;
85 }
86
87 // wl_egl_windows must have both width and height > 0
88 // mesa's egl returns NULL if we try to create a, invalid wl_egl_window, however not all EGL
89 // implementations may do that, so check the size ourself. Besides, we must deal with resizing
90 // a valid window to 0x0, which would make it invalid. Hence, destroy it.
91 if (sizeWithMargins.isEmpty()) {
92 if (m_eglSurface) {
93 eglDestroySurface(dpy: m_clientBufferIntegration->eglDisplay(), surface: m_eglSurface);
94 m_eglSurface = 0;
95 }
96 if (m_waylandEglWindow) {
97 wl_egl_window_destroy(egl_window: m_waylandEglWindow);
98 m_waylandEglWindow = 0;
99 }
100 mOffset = QPoint();
101 } else {
102 QReadLocker locker(&mSurfaceLock);
103 if (m_waylandEglWindow) {
104 int current_width = 0;
105 int current_height = 0;
106 static bool disableResizeCheck = qgetenv(varName: "QT_WAYLAND_DISABLE_RESIZECHECK").toInt();
107
108 if (!disableResizeCheck) {
109 wl_egl_window_get_attached_size(egl_window: m_waylandEglWindow, width: &current_width, height: &current_height);
110 }
111 if (disableResizeCheck || (current_width != sizeWithMargins.width() || current_height != sizeWithMargins.height()) || m_requestedSize != sizeWithMargins) {
112 wl_egl_window_resize(egl_window: m_waylandEglWindow, width: sizeWithMargins.width(), height: sizeWithMargins.height(), dx: mOffset.x(), dy: mOffset.y());
113 m_requestedSize = sizeWithMargins;
114 mOffset = QPoint();
115
116 m_resize = true;
117 }
118 } else if (create && mSurface) {
119 wl_egl_window *eglWindow = wl_egl_window_create(mSurface->object(), sizeWithMargins.width(), sizeWithMargins.height());
120 if (Q_UNLIKELY(!eglWindow)) {
121 qCWarning(lcQpaWayland, "Could not create wl_egl_window with size %dx%d\n", sizeWithMargins.width(), sizeWithMargins.height());
122 return;
123 }
124
125 QSurfaceFormat fmt = window()->requestedFormat();
126 if (mDisplay->supportsWindowDecoration())
127 fmt.setAlphaBufferSize(8);
128 EGLConfig eglConfig = q_configFromGLFormat(display: m_clientBufferIntegration->eglDisplay(), format: fmt);
129 m_format = q_glFormatFromConfig(display: m_clientBufferIntegration->eglDisplay(), config: eglConfig, referenceFormat: fmt);
130
131 EGLSurface eglSurface = eglCreateWindowSurface(dpy: m_clientBufferIntegration->eglDisplay(), config: eglConfig, win: (EGLNativeWindowType) eglWindow, attrib_list: 0);
132 if (Q_UNLIKELY(eglSurface == EGL_NO_SURFACE)) {
133 qCWarning(lcQpaWayland, "Could not create EGL surface (EGL error 0x%x)\n", eglGetError());
134 wl_egl_window_destroy(egl_window: eglWindow);
135 return;
136 }
137
138 m_waylandEglWindow = eglWindow;
139 m_eglSurface = eglSurface;
140 m_requestedSize = sizeWithMargins;
141 }
142 }
143}
144
145QRect QWaylandEglWindow::contentsRect() const
146{
147 QRect r = geometry();
148 QMargins m = clientSideMargins();
149 return QRect(m.left(), m.bottom(), r.width(), r.height());
150}
151
152QSurfaceFormat QWaylandEglWindow::format() const
153{
154 return m_format;
155}
156
157void QWaylandEglWindow::invalidateSurface()
158{
159 QMutexLocker lock (&m_eglSurfaceLock);
160
161 if (m_eglSurface) {
162 eglDestroySurface(dpy: m_clientBufferIntegration->eglDisplay(), surface: m_eglSurface);
163 m_eglSurface = 0;
164 }
165 if (m_waylandEglWindow) {
166 wl_egl_window_destroy(egl_window: m_waylandEglWindow);
167 m_waylandEglWindow = nullptr;
168 }
169 delete m_contentFBO;
170 m_contentFBO = nullptr;
171}
172
173EGLSurface QWaylandEglWindow::eglSurface() const
174{
175 return m_eglSurface;
176}
177
178QMutex* QWaylandEglWindow::eglSurfaceLock()
179{
180 return &m_eglSurfaceLock;
181}
182
183GLuint QWaylandEglWindow::contentFBO() const
184{
185 if (!decoration())
186 return 0;
187
188 if (m_resize || !m_contentFBO) {
189 QOpenGLFramebufferObject *old = m_contentFBO;
190 QSize fboSize = geometry().size() * scale();
191 m_contentFBO = new QOpenGLFramebufferObject(fboSize.width(), fboSize.height(), QOpenGLFramebufferObject::CombinedDepthStencil);
192
193 delete old;
194 m_resize = false;
195 }
196
197 return m_contentFBO->handle();
198}
199
200GLuint QWaylandEglWindow::contentTexture() const
201{
202 return m_contentFBO->texture();
203}
204
205void QWaylandEglWindow::bindContentFBO()
206{
207 if (decoration()) {
208 contentFBO();
209 m_contentFBO->bind();
210 }
211}
212
213}
214
215QT_END_NAMESPACE
216
217#include "moc_qwaylandeglwindow_p.cpp"
218

source code of qtwayland/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp