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 <QtCore/qtextstream.h> |
5 | #include <QtGui/qwindow.h> |
6 | #include <QtGui/private/qguiapplication_p.h> |
7 | #include <qpa/qwindowsysteminterface.h> |
8 | #include <qpa/qplatformcursor.h> |
9 | #ifndef QT_NO_OPENGL |
10 | # include <QtOpenGL/private/qopenglcompositor_p.h> |
11 | #endif |
12 | |
13 | #include "qeglfsscreen_p.h" |
14 | #include "qeglfswindow_p.h" |
15 | #include "qeglfshooks_p.h" |
16 | |
17 | QT_BEGIN_NAMESPACE |
18 | |
19 | QEglFSScreen::QEglFSScreen(EGLDisplay dpy) |
20 | : m_dpy(dpy), |
21 | m_surface(EGL_NO_SURFACE), |
22 | m_cursor(nullptr) |
23 | { |
24 | m_cursor = qt_egl_device_integration()->createCursor(screen: this); |
25 | } |
26 | |
27 | QEglFSScreen::~QEglFSScreen() |
28 | { |
29 | delete m_cursor; |
30 | } |
31 | |
32 | QRect QEglFSScreen::geometry() const |
33 | { |
34 | QRect r = rawGeometry(); |
35 | |
36 | static int rotation = qEnvironmentVariableIntValue(varName: "QT_QPA_EGLFS_ROTATION" ); |
37 | switch (rotation) { |
38 | case 0: |
39 | case 180: |
40 | case -180: |
41 | break; |
42 | case 90: |
43 | case -90: { |
44 | int h = r.height(); |
45 | r.setHeight(r.width()); |
46 | r.setWidth(h); |
47 | break; |
48 | } |
49 | default: |
50 | qWarning(msg: "Invalid rotation %d specified in QT_QPA_EGLFS_ROTATION" , rotation); |
51 | break; |
52 | } |
53 | |
54 | return r; |
55 | } |
56 | |
57 | QRect QEglFSScreen::rawGeometry() const |
58 | { |
59 | return QRect(QPoint(0, 0), qt_egl_device_integration()->screenSize()); |
60 | } |
61 | |
62 | int QEglFSScreen::depth() const |
63 | { |
64 | return qt_egl_device_integration()->screenDepth(); |
65 | } |
66 | |
67 | QImage::Format QEglFSScreen::format() const |
68 | { |
69 | return qt_egl_device_integration()->screenFormat(); |
70 | } |
71 | |
72 | QSizeF QEglFSScreen::physicalSize() const |
73 | { |
74 | return qt_egl_device_integration()->physicalScreenSize(); |
75 | } |
76 | |
77 | QDpi QEglFSScreen::logicalDpi() const |
78 | { |
79 | return qt_egl_device_integration()->logicalDpi(); |
80 | } |
81 | |
82 | QDpi QEglFSScreen::logicalBaseDpi() const |
83 | { |
84 | return qt_egl_device_integration()->logicalBaseDpi(); |
85 | } |
86 | |
87 | Qt::ScreenOrientation QEglFSScreen::nativeOrientation() const |
88 | { |
89 | return qt_egl_device_integration()->nativeOrientation(); |
90 | } |
91 | |
92 | Qt::ScreenOrientation QEglFSScreen::orientation() const |
93 | { |
94 | return qt_egl_device_integration()->orientation(); |
95 | } |
96 | |
97 | QPlatformCursor *QEglFSScreen::cursor() const |
98 | { |
99 | return m_cursor; |
100 | } |
101 | |
102 | qreal QEglFSScreen::refreshRate() const |
103 | { |
104 | return qt_egl_device_integration()->refreshRate(); |
105 | } |
106 | |
107 | void QEglFSScreen::setPrimarySurface(EGLSurface surface) |
108 | { |
109 | m_surface = surface; |
110 | } |
111 | |
112 | void QEglFSScreen::handleCursorMove(const QPoint &pos) |
113 | { |
114 | #ifndef QT_NO_OPENGL |
115 | const QOpenGLCompositor *compositor = QOpenGLCompositor::instance(); |
116 | const QList<QOpenGLCompositorWindow *> windows = compositor->windows(); |
117 | QEglFSIntegration *platformIntegration = static_cast<QEglFSIntegration *>(QGuiApplicationPrivate::platformIntegration()); |
118 | |
119 | // Generate enter and leave events like a real windowing system would do. |
120 | if (windows.isEmpty()) |
121 | return; |
122 | |
123 | // First window is always fullscreen. |
124 | if (windows.size() == 1) { |
125 | QWindow *window = windows[0]->sourceWindow(); |
126 | if (platformIntegration->pointerWindow() != window) { |
127 | platformIntegration->setPointerWindow(window); |
128 | QWindowSystemInterface::handleEnterEvent(window, local: window->mapFromGlobal(pos), global: pos); |
129 | } |
130 | return; |
131 | } |
132 | |
133 | QWindow *enter = nullptr, *leave = nullptr; |
134 | for (int i = windows.size() - 1; i >= 0; --i) { |
135 | QWindow *window = windows[i]->sourceWindow(); |
136 | const QRect geom = window->geometry(); |
137 | if (geom.contains(p: pos)) { |
138 | if (platformIntegration->pointerWindow() != window) { |
139 | leave = platformIntegration->pointerWindow(); |
140 | platformIntegration->setPointerWindow(window); |
141 | enter = window; |
142 | } |
143 | break; |
144 | } |
145 | } |
146 | |
147 | if (enter && leave) { |
148 | QWindowSystemInterface::handleEnterLeaveEvent(enter, leave, local: enter->mapFromGlobal(pos), global: pos); |
149 | } else if (enter) { |
150 | QWindowSystemInterface::handleEnterEvent(window: enter, local: enter->mapFromGlobal(pos), global: pos); |
151 | } else if (leave) { |
152 | QWindowSystemInterface::handleLeaveEvent(window: leave); |
153 | } |
154 | #else |
155 | Q_UNUSED(pos); |
156 | #endif |
157 | } |
158 | |
159 | QPixmap QEglFSScreen::grabWindow(WId wid, int x, int y, int width, int height) const |
160 | { |
161 | #ifndef QT_NO_OPENGL |
162 | QOpenGLCompositor *compositor = QOpenGLCompositor::instance(); |
163 | const QList<QOpenGLCompositorWindow *> windows = compositor->windows(); |
164 | Q_ASSERT(!windows.isEmpty()); |
165 | |
166 | QImage img; |
167 | |
168 | QEglFSWindow *primaryWin = static_cast<QEglFSWindow *>(windows.first()->sourceWindow()->handle()); |
169 | if (primaryWin->isRaster() || primaryWin->backingStore()) { |
170 | // Request the compositor to render everything into an FBO and read it back. This |
171 | // is of course slow, but it's safe and reliable. It will not include the mouse |
172 | // cursor, which is a plus. |
173 | img = compositor->grab(); |
174 | } else { |
175 | // Just a single OpenGL window without compositing. Do not support this case for now. Doing |
176 | // glReadPixels is not an option since it would read from the back buffer which may have |
177 | // undefined content when calling right after a swapBuffers (unless preserved swap is |
178 | // available and enabled, but we have no support for that). |
179 | qWarning(msg: "grabWindow: Not supported for non-composited OpenGL content. Use QQuickWindow::grabWindow() instead." ); |
180 | return QPixmap(); |
181 | } |
182 | |
183 | if (!wid) { |
184 | const QSize screenSize = geometry().size(); |
185 | if (width < 0) |
186 | width = screenSize.width() - x; |
187 | if (height < 0) |
188 | height = screenSize.height() - y; |
189 | return QPixmap::fromImage(image: img).copy(ax: x, ay: y, awidth: width, aheight: height); |
190 | } |
191 | |
192 | for (QOpenGLCompositorWindow *w : windows) { |
193 | const QWindow *window = w->sourceWindow(); |
194 | if (window->winId() == wid) { |
195 | const QRect geom = window->geometry(); |
196 | if (width < 0) |
197 | width = geom.width() - x; |
198 | if (height < 0) |
199 | height = geom.height() - y; |
200 | QRect rect(geom.topLeft() + QPoint(x, y), QSize(width, height)); |
201 | rect &= window->geometry(); |
202 | return QPixmap::fromImage(image: img).copy(rect); |
203 | } |
204 | } |
205 | #else // QT_NO_OPENGL |
206 | Q_UNUSED(wid); |
207 | Q_UNUSED(x); |
208 | Q_UNUSED(y); |
209 | Q_UNUSED(width); |
210 | Q_UNUSED(height); |
211 | #endif |
212 | return QPixmap(); |
213 | } |
214 | |
215 | QWindow *QEglFSScreen::topLevelAt(const QPoint &point) const |
216 | { |
217 | #ifndef QT_NO_OPENGL |
218 | QOpenGLCompositor *compositor = QOpenGLCompositor::instance(); |
219 | const QList<QOpenGLCompositorWindow *> windows = compositor->windows(); |
220 | const int windowCount = windows.size(); |
221 | |
222 | // Higher z-order is at the end of the list |
223 | for (int i = windowCount - 1; i >= 0; i--) { |
224 | QWindow *window = windows[i]->sourceWindow(); |
225 | if (window->isVisible() && window->geometry().contains(p: point)) |
226 | return window; |
227 | } |
228 | #endif |
229 | |
230 | return QPlatformScreen::topLevelAt(point); |
231 | } |
232 | |
233 | QT_END_NAMESPACE |
234 | |