Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qqnxwindowgrabber.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 Research In Motion
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include <QAbstractEventDispatcher>
7#include <QDebug>
8#include <QGuiApplication>
9#include <QImage>
10#include <QThread>
11#include <qpa/qplatformnativeinterface.h>
12
13#include <QOpenGLContext>
14#include <QOpenGLFunctions>
15
16#include <rhi/qrhi.h>
17
18#include <cstring>
19
20#include <EGL/egl.h>
21#include <errno.h>
22
24
25static PFNEGLCREATEIMAGEKHRPROC s_eglCreateImageKHR;
26static PFNEGLDESTROYIMAGEKHRPROC s_eglDestroyImageKHR;
27
29{
30public:
33
34 bool initialize(screen_context_t screenContext);
35
37 GLuint getTexture(screen_window_t window, const QSize &size);
38
39private:
40 bool grab(screen_window_t window);
41 bool resize(const QSize &size);
42
43 QSize m_size;
44 screen_pixmap_t m_pixmap;
45 screen_buffer_t m_pixmapBuffer;
46 EGLImageKHR m_eglImage;
47 GLuint m_glTexture;
48 unsigned char *m_bufferAddress;
49 int m_bufferStride;
50};
51
53 : QObject(parent),
54 m_windowParent(nullptr),
55 m_window(nullptr),
56 m_screenContext(nullptr),
57 m_rhi(nullptr),
59 m_eglImageSupported(false),
60 m_startPending(false)
61{
62 // grab the window frame with 60 frames per second
63 m_timer.setInterval(1000/60);
64
65 connect(&m_timer, &QTimer::timeout, this, &QQnxWindowGrabber::triggerUpdate);
66
67 QCoreApplication::eventDispatcher()->installNativeEventFilter(this);
68
69 // Use of EGL images can be disabled by setting QQNX_MM_DISABLE_EGLIMAGE_SUPPORT to something
70 // non-zero. This is probably useful only to test that this path still works since it results
71 // in a high CPU load.
72 if (!s_eglCreateImageKHR && qgetenv("QQNX_MM_DISABLE_EGLIMAGE_SUPPORT").toInt() == 0) {
73 s_eglCreateImageKHR = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImageKHR"));
74 s_eglDestroyImageKHR = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress("eglDestroyImageKHR"));
75 }
76
78 if (nativeInterface) {
79 m_screenContext = static_cast<screen_context_t>(
80 nativeInterface->nativeResourceForIntegration("screenContext"));
81 }
82
83 // Create a parent window for the window whose content will be grabbed. Since the
84 // window is only a buffer conduit, the characteristics of the parent window are
85 // irrelevant. The contents of the window can be grabbed so long as the window
86 // joins the parent window's group and the parent window is in this process.
87 // Using the window that displays this content isn't possible because there's no
88 // way to reliably retrieve it from this code or any calling code.
89 screen_create_window(&m_windowParent, m_screenContext);
90 screen_create_window_group(m_windowParent, nullptr);
91}
92
94{
95 screen_destroy_window(m_windowParent);
96 QCoreApplication::eventDispatcher()->removeNativeEventFilter(this);
97}
98
100{
101 m_timer.setInterval(1000/frameRate);
102}
103
105{
106 m_windowId = windowId;
107}
108
110{
111 m_rhi = rhi;
112
113 checkForEglImageExtension();
114}
115
117{
118 if (m_active)
119 return;
120
121 if (!m_window) {
122 m_startPending = true;
123 return;
124 }
125
126 m_startPending = false;
127
128 if (!m_screenContext)
129 screen_get_window_property_pv(m_window, SCREEN_PROPERTY_CONTEXT, reinterpret_cast<void**>(&m_screenContext));
130
131 m_timer.start();
132
133 m_active = true;
134}
135
137{
138 if (!m_active)
139 return;
140
141 resetBuffers();
142
143 m_timer.stop();
144
145 m_active = false;
146}
147
149{
150 m_timer.stop();
151}
152
154{
155 if (!m_active)
156 return;
157
158 m_timer.start();
159}
160
162{
163 if (!m_active)
164 return;
165
166 triggerUpdate();
167}
168
169bool QQnxWindowGrabber::handleScreenEvent(screen_event_t screen_event)
170{
171
172 int eventType;
173 if (screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_TYPE, &eventType) != 0) {
174 qWarning() << "QQnxWindowGrabber: Failed to query screen event type";
175 return false;
176 }
177
178 if (eventType != SCREEN_EVENT_CREATE)
179 return false;
180
181 screen_window_t window = 0;
182 if (screen_get_event_property_pv(screen_event, SCREEN_PROPERTY_WINDOW, (void**)&window) != 0) {
183 qWarning() << "QQnxWindowGrabber: Failed to query window property";
184 return false;
185 }
186
187 const int maxIdStrLength = 128;
188 char idString[maxIdStrLength];
189 if (screen_get_window_property_cv(window, SCREEN_PROPERTY_ID_STRING, maxIdStrLength, idString) != 0) {
190 qWarning() << "QQnxWindowGrabber: Failed to query window ID string";
191 return false;
192 }
193
194 // Grab windows that have a non-empty ID string and a matching window id to grab
195 if (idString[0] != '\0' && m_windowId == idString) {
196 m_window = window;
197
198 if (m_startPending)
199 start();
200 }
201
202 return false;
203}
204
206{
207 if (eventType == "screen_event_t") {
208 const screen_event_t event = static_cast<screen_event_t>(message);
209 return handleScreenEvent(event);
210 }
211
212 return false;
213}
214
216{
217 char groupName[256];
218 memset(groupName, 0, sizeof(groupName));
219 screen_get_window_property_cv(m_windowParent,
220 SCREEN_PROPERTY_GROUP,
221 sizeof(groupName) - 1,
222 groupName);
223 return QByteArray(groupName);
224}
225
227{
228 return m_eglImageSupported;
229}
230
231void QQnxWindowGrabber::checkForEglImageExtension()
232{
233 m_eglImageSupported = false;
234
235 if (!m_rhi || m_rhi->backend() != QRhi::OpenGLES2)
236 return;
237
238 const EGLDisplay defaultDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
239
240 const char *vendor = eglQueryString(defaultDisplay, EGL_VENDOR);
241
242 if (vendor && std::strstr(vendor, "VMWare"))
243 return;
244
245 const char *eglExtensions = eglQueryString(defaultDisplay, EGL_EXTENSIONS);
246
247 if (!eglExtensions)
248 return;
249
250 m_eglImageSupported = std::strstr(eglExtensions, "EGL_KHR_image")
253}
254
255void QQnxWindowGrabber::triggerUpdate()
256{
257 int size[2] = { 0, 0 };
258
259 const int result = screen_get_window_property_iv(m_window, SCREEN_PROPERTY_SOURCE_SIZE, size);
260
261 if (result != 0) {
262 resetBuffers();
263 qWarning() << "QQnxWindowGrabber: cannot get window size:" << strerror(errno);
264 return;
265 }
266
267 if (m_size.width() != size[0] || m_size.height() != size[1])
268 m_size = QSize(size[0], size[1]);
269
270 emit updateScene(m_size);
271}
272
273bool QQnxWindowGrabber::selectBuffer()
274{
275 // If we're using egl images we need to double buffer since the gpu may still be using the last
276 // video frame. If we're not, it doesn't matter since the data is immediately copied.
278 std::swap(m_frontBuffer, m_backBuffer);
279
280 if (m_frontBuffer)
281 return true;
282
283 auto frontBuffer = std::make_unique<QQnxWindowGrabberImage>();
284
285 if (!frontBuffer->initialize(m_screenContext))
286 return false;
287
288 m_frontBuffer = std::move(frontBuffer);
289
290 return true;
291}
292
294{
295 if (!selectBuffer())
296 return 0;
297
298 return m_frontBuffer->getTexture(m_window, m_size);
299}
300
302{
303 if (!selectBuffer())
304 return {};
305
306 return m_frontBuffer->getBuffer(m_window, m_size);
307}
308
309void QQnxWindowGrabber::resetBuffers()
310{
311 m_frontBuffer.reset();
312 m_backBuffer.reset();
313}
314
316 : m_pixmap(0),
317 m_pixmapBuffer(0),
318 m_eglImage(0),
319 m_glTexture(0),
320 m_bufferAddress(nullptr),
321 m_bufferStride(0)
322{
323}
324
326{
327 if (m_glTexture)
328 glDeleteTextures(1, &m_glTexture);
329 if (m_eglImage)
330 s_eglDestroyImageKHR(eglGetDisplay(EGL_DEFAULT_DISPLAY), m_eglImage);
331 if (m_pixmap)
332 screen_destroy_pixmap(m_pixmap);
333}
334
335bool QQnxWindowGrabberImage::initialize(screen_context_t screenContext)
336{
337 if (screen_create_pixmap(&m_pixmap, screenContext) != 0) {
338 qWarning() << "QQnxWindowGrabber: cannot create pixmap:" << strerror(errno);
339 return false;
340 }
341 const int usage = SCREEN_USAGE_WRITE | SCREEN_USAGE_READ | SCREEN_USAGE_NATIVE;
342 screen_set_pixmap_property_iv(m_pixmap, SCREEN_PROPERTY_USAGE, &usage);
343
344 // XXX as a matter of fact, the underlying buffer is BGRX8888 (according to
345 // QNX, screen formats can be loose on the ARGB ordering) - as there is no
346 // SCREEN_FORMAT_BGRX8888 constant, we use SCREEN_FORMAT_RGBX8888, which
347 // carries the same depth and allows us to use the buffer.
348 const int format = SCREEN_FORMAT_RGBX8888;
349 screen_set_pixmap_property_iv(m_pixmap, SCREEN_PROPERTY_FORMAT, &format);
350
351 return true;
352}
353
354bool QQnxWindowGrabberImage::resize(const QSize &newSize)
355{
356 if (m_pixmapBuffer) {
357 screen_destroy_pixmap_buffer(m_pixmap);
358 m_pixmapBuffer = 0;
359 m_bufferAddress = 0;
360 m_bufferStride = 0;
361 }
362
363 const int size[2] = { newSize.width(), newSize.height() };
364
365 screen_set_pixmap_property_iv(m_pixmap, SCREEN_PROPERTY_BUFFER_SIZE, size);
366
367 if (screen_create_pixmap_buffer(m_pixmap) == 0) {
368 screen_get_pixmap_property_pv(m_pixmap, SCREEN_PROPERTY_RENDER_BUFFERS,
369 reinterpret_cast<void**>(&m_pixmapBuffer));
370 screen_get_buffer_property_pv(m_pixmapBuffer, SCREEN_PROPERTY_POINTER,
371 reinterpret_cast<void**>(&m_bufferAddress));
372 screen_get_buffer_property_iv(m_pixmapBuffer, SCREEN_PROPERTY_STRIDE, &m_bufferStride);
373 m_size = newSize;
374
375 return true;
376 } else {
377 m_size = QSize();
378 return false;
379 }
380}
381
382bool QQnxWindowGrabberImage::grab(screen_window_t window)
383{
384 const int rect[] = { 0, 0, m_size.width(), m_size.height() };
385 return screen_read_window(window, m_pixmapBuffer, 1, rect, 0) == 0;
386}
387
389 screen_window_t window, const QSize &size)
390{
391 if (size != m_size && !resize(size))
392 return {};
393
394 if (!m_bufferAddress || !grab(window))
395 return {};
396
397 return {
398 .width = m_size.width(),
399 .height = m_size.height(),
400 .stride = m_bufferStride,
401 .data = m_bufferAddress
402 };
403}
404
406{
407 if (size != m_size) {
408 // create a brand new texture to be the KHR image sibling, as
409 // previously used textures cannot be reused with new KHR image
410 // sources - note that glDeleteTextures handles nullptr gracefully
411 glDeleteTextures(1, &m_glTexture);
412 glGenTextures(1, &m_glTexture);
413
414 glBindTexture(GL_TEXTURE_2D, m_glTexture);
415 if (m_eglImage) {
416 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, 0);
417 s_eglDestroyImageKHR(eglGetDisplay(EGL_DEFAULT_DISPLAY), m_eglImage);
418 m_eglImage = 0;
419 }
420 if (!resize(size))
421 return 0;
422 m_eglImage = s_eglCreateImageKHR(eglGetDisplay(EGL_DEFAULT_DISPLAY), EGL_NO_CONTEXT,
423 EGL_NATIVE_PIXMAP_KHR, m_pixmap, 0);
424 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage);
425 }
426
427 if (!m_pixmap || !grab(window))
428 return 0;
429
430 return m_glTexture;
431}
432
434
435#include "moc_qqnxwindowgrabber_p.cpp"
bool m_active
\inmodule QtCore
Definition qbytearray.h:57
static QAbstractEventDispatcher * eventDispatcher()
Returns a pointer to the event dispatcher object for the main thread.
static QPlatformNativeInterface * platformNativeInterface()
\inmodule QtCore
Definition qobject.h:103
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
The QPlatformNativeInterface class provides an abstraction for retrieving native resource handles.
virtual void * nativeResourceForIntegration(const QByteArray &resource)
bool initialize(screen_context_t screenContext)
GLuint getTexture(screen_window_t window, const QSize &size)
QQnxWindowGrabber::BufferView getBuffer(screen_window_t window, const QSize &size)
QQnxWindowGrabber(QObject *parent=0)
bool isEglImageSupported() const
bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override
This method is called for every native event.
void setWindowId(const QByteArray &windowId)
bool handleScreenEvent(screen_event_t event)
QByteArray windowGroupId() const
void updateScene(const QSize &size)
void setFrameRate(int frameRate)
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Definition qrhi.h:1804
Implementation backend() const
Definition qrhi.cpp:8651
@ OpenGLES2
Definition qrhi.h:1809
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:133
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:130
void start(int msec)
Starts or restarts the timer with a timeout interval of msec milliseconds.
Definition qtimer.cpp:241
void setInterval(int msec)
Definition qtimer.cpp:579
void stop()
Stops the timer.
Definition qtimer.cpp:267
void timeout(QPrivateSignal)
This signal is emitted when the timer times out.
rect
[4]
Combined button and popup list for selecting options.
static bool initialize()
Definition qctf.cpp:94
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
typedef EGLDisplay(EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYEXTPROC)(EGLenum platform
#define qWarning
Definition qlogging.h:166
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint GLsizei const GLchar * message
GLint GLsizei GLsizei GLenum format
struct _cl_event * event
GLuint64EXT * result
[6]
GLsizeiptr const void GLenum usage
Definition qopenglext.h:543
static PFNEGLDESTROYIMAGEKHRPROC s_eglDestroyImageKHR
static QT_BEGIN_NAMESPACE PFNEGLCREATEIMAGEKHRPROC s_eglCreateImageKHR
#define GLuint
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
#define emit
ptrdiff_t qintptr
Definition qtypes.h:166
static int toInt(const QChar &qc, int R)
QObject::connect nullptr
aWidget window() -> setWindowTitle("New Window Title")
[2]