1/****************************************************************************
2**
3** Copyright (C) 2016 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:LGPL$
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 Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include <QtCore/qtextstream.h>
41#include <qpa/qwindowsysteminterface.h>
42#include <qpa/qplatformintegration.h>
43#include <private/qguiapplication_p.h>
44#include <private/qwindow_p.h>
45#ifndef QT_NO_OPENGL
46# include <QtGui/private/qopenglcontext_p.h>
47# include <QtGui/QOpenGLContext>
48# include <QtPlatformCompositorSupport/private/qopenglcompositorbackingstore_p.h>
49#endif
50#include <QtEglSupport/private/qeglconvenience_p.h>
51
52#include "qeglfswindow_p.h"
53#ifndef QT_NO_OPENGL
54# include "qeglfscursor_p.h"
55#endif
56#include "qeglfshooks_p.h"
57#include "qeglfsdeviceintegration_p.h"
58
59QT_BEGIN_NAMESPACE
60
61QEglFSWindow::QEglFSWindow(QWindow *w)
62 : QPlatformWindow(w),
63#ifndef QT_NO_OPENGL
64 m_backingStore(0),
65#endif
66 m_raster(false),
67 m_winId(0),
68 m_surface(EGL_NO_SURFACE),
69 m_window(0),
70 m_flags(0)
71{
72}
73
74QEglFSWindow::~QEglFSWindow()
75{
76 destroy();
77}
78
79static WId newWId()
80{
81 static WId id = 0;
82
83 if (id == std::numeric_limits<WId>::max())
84 qWarning("QEGLPlatformWindow: Out of window IDs");
85
86 return ++id;
87}
88
89void QEglFSWindow::create()
90{
91 if (m_flags.testFlag(Created))
92 return;
93
94 m_winId = newWId();
95
96 // Save the original surface type before changing to OpenGLSurface.
97 m_raster = (window()->surfaceType() == QSurface::RasterSurface);
98 if (m_raster) // change to OpenGL, but not for RasterGLSurface
99 window()->setSurfaceType(QSurface::OpenGLSurface);
100
101 if (window()->type() == Qt::Desktop) {
102 QRect fullscreenRect(QPoint(), screen()->availableGeometry().size());
103 QWindowSystemInterface::handleGeometryChange(window(), fullscreenRect);
104 return;
105 }
106
107 m_flags = Created;
108
109 if (window()->type() == Qt::Desktop)
110 return;
111
112 // Stop if there is already a window backed by a native window and surface. Additional
113 // raster windows will not have their own native window, surface and context. Instead,
114 // they will be composited onto the root window's surface.
115 QEglFSScreen *screen = this->screen();
116#ifndef QT_NO_OPENGL
117 QOpenGLCompositor *compositor = QOpenGLCompositor::instance();
118 if (screen->primarySurface() != EGL_NO_SURFACE) {
119 if (Q_UNLIKELY(!isRaster() || !compositor->targetWindow())) {
120#if !defined(Q_OS_ANDROID) || defined(Q_OS_ANDROID_EMBEDDED)
121 // We can have either a single OpenGL window or multiple raster windows.
122 // Other combinations cannot work.
123 qFatal("EGLFS: OpenGL windows cannot be mixed with others.");
124#endif
125 return;
126 }
127 m_format = compositor->targetWindow()->format();
128 return;
129 }
130#endif // QT_NO_OPENGL
131
132 m_flags |= HasNativeWindow;
133 setGeometry(QRect()); // will become fullscreen
134
135 resetSurface();
136
137 if (Q_UNLIKELY(m_surface == EGL_NO_SURFACE)) {
138 EGLint error = eglGetError();
139 eglTerminate(screen->display());
140 qFatal("EGL Error : Could not create the egl surface: error = 0x%x\n", error);
141 }
142
143 screen->setPrimarySurface(m_surface);
144
145#ifndef QT_NO_OPENGL
146 if (isRaster()) {
147 QOpenGLContext *context = new QOpenGLContext(QGuiApplication::instance());
148 context->setShareContext(qt_gl_global_share_context());
149 context->setFormat(m_format);
150 context->setScreen(window()->screen());
151 if (Q_UNLIKELY(!context->create()))
152 qFatal("EGLFS: Failed to create compositing context");
153 compositor->setTarget(context, window(), screen->rawGeometry());
154 compositor->setRotation(qEnvironmentVariableIntValue("QT_QPA_EGLFS_ROTATION"));
155 // If there is a "root" window into which raster and QOpenGLWidget content is
156 // composited, all other contexts must share with its context.
157 if (!qt_gl_global_share_context()) {
158 qt_gl_set_global_share_context(context);
159 // What we set up here is in effect equivalent to the application setting
160 // AA_ShareOpenGLContexts. Set the attribute to be fully consistent.
161 QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
162 }
163 }
164#endif // QT_NO_OPENGL
165}
166
167void QEglFSWindow::destroy()
168{
169 QEglFSScreen *screen = this->screen();
170 if (m_flags.testFlag(HasNativeWindow)) {
171#ifndef QT_NO_OPENGL
172 QEglFSCursor *cursor = qobject_cast<QEglFSCursor *>(screen->cursor());
173 if (cursor)
174 cursor->resetResources();
175#endif
176 if (screen->primarySurface() == m_surface)
177 screen->setPrimarySurface(EGL_NO_SURFACE);
178
179 invalidateSurface();
180 }
181
182 m_flags = 0;
183#ifndef QT_NO_OPENGL
184 QOpenGLCompositor::instance()->removeWindow(this);
185#endif
186}
187
188void QEglFSWindow::invalidateSurface()
189{
190 if (m_surface != EGL_NO_SURFACE) {
191 eglDestroySurface(screen()->display(), m_surface);
192 m_surface = EGL_NO_SURFACE;
193 }
194 qt_egl_device_integration()->destroyNativeWindow(m_window);
195 m_window = 0;
196}
197
198void QEglFSWindow::resetSurface()
199{
200 EGLDisplay display = screen()->display();
201 QSurfaceFormat platformFormat = qt_egl_device_integration()->surfaceFormatFor(window()->requestedFormat());
202
203 m_config = QEglFSDeviceIntegration::chooseConfig(display, platformFormat);
204 m_format = q_glFormatFromConfig(display, m_config, platformFormat);
205 const QSize surfaceSize = screen()->rawGeometry().size();
206 m_window = qt_egl_device_integration()->createNativeWindow(this, surfaceSize, m_format);
207 m_surface = eglCreateWindowSurface(display, m_config, m_window, NULL);
208}
209
210void QEglFSWindow::setVisible(bool visible)
211{
212#ifndef QT_NO_OPENGL
213 QOpenGLCompositor *compositor = QOpenGLCompositor::instance();
214 QList<QOpenGLCompositorWindow *> windows = compositor->windows();
215 QWindow *wnd = window();
216
217 if (wnd->type() != Qt::Desktop) {
218 if (visible) {
219 compositor->addWindow(this);
220 } else {
221 compositor->removeWindow(this);
222 windows = compositor->windows();
223 if (windows.size())
224 windows.last()->sourceWindow()->requestActivate();
225 }
226 }
227#else
228 QWindow *wnd = window();
229#endif
230 QWindowSystemInterface::handleExposeEvent(wnd, QRect(QPoint(0, 0), wnd->geometry().size()));
231
232 if (visible)
233 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
234}
235
236void QEglFSWindow::setGeometry(const QRect &r)
237{
238 QRect rect = r;
239 if (m_flags.testFlag(HasNativeWindow))
240 rect = screen()->availableGeometry();
241
242 QPlatformWindow::setGeometry(rect);
243
244 QWindowSystemInterface::handleGeometryChange(window(), rect);
245
246 const QRect lastReportedGeometry = qt_window_private(window())->geometry;
247 if (rect != lastReportedGeometry)
248 QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), rect.size()));
249}
250
251QRect QEglFSWindow::geometry() const
252{
253 // For yet-to-become-fullscreen windows report the geometry covering the entire
254 // screen. This is particularly important for Quick where the root object may get
255 // sized to some geometry queried before calling create().
256 if (!m_flags.testFlag(Created) && screen()->primarySurface() == EGL_NO_SURFACE)
257 return screen()->availableGeometry();
258
259 return QPlatformWindow::geometry();
260}
261
262void QEglFSWindow::requestActivateWindow()
263{
264#ifndef QT_NO_OPENGL
265 if (window()->type() != Qt::Desktop)
266 QOpenGLCompositor::instance()->moveToTop(this);
267#endif
268 QWindow *wnd = window();
269 QWindowSystemInterface::handleWindowActivated(wnd);
270 QWindowSystemInterface::handleExposeEvent(wnd, QRect(QPoint(0, 0), wnd->geometry().size()));
271}
272
273void QEglFSWindow::raise()
274{
275 QWindow *wnd = window();
276 if (wnd->type() != Qt::Desktop) {
277#ifndef QT_NO_OPENGL
278 QOpenGLCompositor::instance()->moveToTop(this);
279#endif
280 QWindowSystemInterface::handleExposeEvent(wnd, QRect(QPoint(0, 0), wnd->geometry().size()));
281 }
282}
283
284void QEglFSWindow::lower()
285{
286#ifndef QT_NO_OPENGL
287 QOpenGLCompositor *compositor = QOpenGLCompositor::instance();
288 QList<QOpenGLCompositorWindow *> windows = compositor->windows();
289 if (window()->type() != Qt::Desktop && windows.count() > 1) {
290 int idx = windows.indexOf(this);
291 if (idx > 0) {
292 compositor->changeWindowIndex(this, idx - 1);
293 QWindowSystemInterface::handleExposeEvent(windows.last()->sourceWindow(),
294 QRect(QPoint(0, 0), windows.last()->sourceWindow()->geometry().size()));
295 }
296 }
297#endif
298}
299
300EGLSurface QEglFSWindow::surface() const
301{
302 return m_surface != EGL_NO_SURFACE ? m_surface : screen()->primarySurface();
303}
304
305QSurfaceFormat QEglFSWindow::format() const
306{
307 return m_format;
308}
309
310EGLNativeWindowType QEglFSWindow::eglWindow() const
311{
312 return m_window;
313}
314
315QEglFSScreen *QEglFSWindow::screen() const
316{
317 return static_cast<QEglFSScreen *>(QPlatformWindow::screen());
318}
319
320bool QEglFSWindow::isRaster() const
321{
322 return m_raster || window()->surfaceType() == QSurface::RasterGLSurface;
323}
324
325#ifndef QT_NO_OPENGL
326QWindow *QEglFSWindow::sourceWindow() const
327{
328 return window();
329}
330
331const QPlatformTextureList *QEglFSWindow::textures() const
332{
333 if (m_backingStore)
334 return m_backingStore->textures();
335
336 return 0;
337}
338
339void QEglFSWindow::endCompositing()
340{
341 if (m_backingStore)
342 m_backingStore->notifyComposited();
343}
344#endif
345
346WId QEglFSWindow::winId() const
347{
348 return m_winId;
349}
350
351void QEglFSWindow::setOpacity(qreal)
352{
353 if (!isRaster())
354 qWarning("QEglFSWindow: Cannot set opacity for non-raster windows");
355
356 // Nothing to do here. The opacity is stored in the QWindow.
357}
358
359QT_END_NAMESPACE
360