Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
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 <QtMultimedia/private/qeglimagefunctions_p.h>
19#include <QtMultimedia/private/qmultimedia_gl_support_p.h>
20
21#include <cstring>
22
23#include <EGL/egl.h>
24#include <errno.h>
25
26QT_BEGIN_NAMESPACE
27
28using QtMultimediaPrivate::EGLImageHandle;
29using QtMultimediaPrivate::GlTextureDeleter;
30using QtMultimediaPrivate::GlTextureHandle;
31using QtMultimediaPrivate::QEglImageFunctions;
32
34{
35public:
38
39 bool initialize(screen_context_t screenContext);
40
41 QQnxWindowGrabber::BufferView getBuffer(screen_window_t window, const QSize &size);
42 GLuint getTexture(screen_window_t window, const QSize &size);
43
44private:
45 bool grab(screen_window_t window);
46 bool resize(const QSize &size);
47
48 QSize m_size;
49 screen_pixmap_t m_pixmap;
50 screen_buffer_t m_pixmapBuffer;
51 EGLImageHandle m_eglImage;
52 GlTextureHandle m_glTexture;
53 unsigned char *m_bufferAddress;
54 int m_bufferStride;
55};
56
58 : QObject(parent),
59 m_windowParent(nullptr),
60 m_window(nullptr),
61 m_screenContext(nullptr),
62 m_rhi(nullptr),
63 m_active(false),
64 m_eglImageSupported(false),
65 m_startPending(false)
66{
67 // grab the window frame with 60 frames per second
68 m_timer.setInterval(1000/60);
69
70 connect(&m_timer, &QTimer::timeout, this, &QQnxWindowGrabber::triggerUpdate);
71
72 QCoreApplication::eventDispatcher()->installNativeEventFilter(this);
73
74 QPlatformNativeInterface *const nativeInterface = QGuiApplication::platformNativeInterface();
75 if (nativeInterface) {
76 m_screenContext = static_cast<screen_context_t>(
77 nativeInterface->nativeResourceForIntegration("screenContext"));
78 }
79
80 // Create a parent window for the window whose content will be grabbed. Since the
81 // window is only a buffer conduit, the characteristics of the parent window are
82 // irrelevant. The contents of the window can be grabbed so long as the window
83 // joins the parent window's group and the parent window is in this process.
84 // Using the window that displays this content isn't possible because there's no
85 // way to reliably retrieve it from this code or any calling code.
86 screen_create_window(&m_windowParent, m_screenContext);
87 screen_create_window_group(m_windowParent, nullptr);
88}
89
91{
92 screen_destroy_window(m_windowParent);
93 QCoreApplication::eventDispatcher()->removeNativeEventFilter(this);
94}
95
96void QQnxWindowGrabber::setFrameRate(int frameRate)
97{
98 m_timer.setInterval(1000/frameRate);
99}
100
101void QQnxWindowGrabber::setWindowId(const QByteArray &windowId)
102{
103 m_windowId = windowId;
104}
105
106void QQnxWindowGrabber::setRhi(QRhi *rhi)
107{
108 m_rhi = rhi;
109
110 checkForEglImageExtension();
111}
112
114{
115 if (m_active)
116 return;
117
118 if (!m_window) {
119 m_startPending = true;
120 return;
121 }
122
123 m_startPending = false;
124
125 if (!m_screenContext)
126 screen_get_window_property_pv(m_window, SCREEN_PROPERTY_CONTEXT, reinterpret_cast<void**>(&m_screenContext));
127
128 m_timer.start();
129
130 m_active = true;
131}
132
134{
135 if (!m_active)
136 return;
137
138 resetBuffers();
139
140 m_timer.stop();
141
142 m_active = false;
143}
144
146{
147 m_timer.stop();
148}
149
151{
152 if (!m_active)
153 return;
154
155 m_timer.start();
156}
157
159{
160 if (!m_active)
161 return;
162
163 triggerUpdate();
164}
165
166bool QQnxWindowGrabber::handleScreenEvent(screen_event_t screen_event)
167{
168
169 int eventType;
170 if (screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_TYPE, &eventType) != 0) {
171 qWarning() << "QQnxWindowGrabber: Failed to query screen event type";
172 return false;
173 }
174
175 if (eventType != SCREEN_EVENT_CREATE)
176 return false;
177
178 screen_window_t window = 0;
179 if (screen_get_event_property_pv(screen_event, SCREEN_PROPERTY_WINDOW, (void**)&window) != 0) {
180 qWarning() << "QQnxWindowGrabber: Failed to query window property";
181 return false;
182 }
183
184 const int maxIdStrLength = 128;
185 char idString[maxIdStrLength];
186 if (screen_get_window_property_cv(window, SCREEN_PROPERTY_ID_STRING, maxIdStrLength, idString) != 0) {
187 qWarning() << "QQnxWindowGrabber: Failed to query window ID string";
188 return false;
189 }
190
191 // Grab windows that have a non-empty ID string and a matching window id to grab
192 if (idString[0] != '\0' && m_windowId == idString) {
193 m_window = window;
194
195 if (m_startPending)
196 start();
197 }
198
199 return false;
200}
201
202bool QQnxWindowGrabber::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *)
203{
204 if (eventType == "screen_event_t") {
205 const screen_event_t event = static_cast<screen_event_t>(message);
206 return handleScreenEvent(event);
207 }
208
209 return false;
210}
211
213{
214 char groupName[256];
215 memset(groupName, 0, sizeof(groupName));
216 screen_get_window_property_cv(m_windowParent,
217 SCREEN_PROPERTY_GROUP,
218 sizeof(groupName) - 1,
219 groupName);
220 return QByteArray(groupName);
221}
222
224{
225 return m_eglImageSupported;
226}
227
228void QQnxWindowGrabber::checkForEglImageExtension()
229{
230 m_eglImageSupported = false;
231
232 if (!m_rhi || m_rhi->backend() != QRhi::OpenGLES2)
233 return;
234
235 // Use of EGL images can be disabled by setting QQNX_MM_DISABLE_EGLIMAGE_SUPPORT to something
236 // non-zero. This is probably useful only to test that this path still works since it results
237 // in a high CPU load.
238 if (qgetenv("QQNX_MM_DISABLE_EGLIMAGE_SUPPORT").toInt() != 0)
239 return;
240
241 const EGLDisplay defaultDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
242
243 const char *vendor = eglQueryString(defaultDisplay, EGL_VENDOR);
244
245 if (vendor && std::strstr(vendor, "VMWare"))
246 return;
247
248 m_eglImageSupported = QEglImageFunctions::instance().isValid();
249}
250
251void QQnxWindowGrabber::triggerUpdate()
252{
253 int size[2] = { 0, 0 };
254
255 const int result = screen_get_window_property_iv(m_window, SCREEN_PROPERTY_SOURCE_SIZE, size);
256
257 if (result != 0) {
258 resetBuffers();
259 qWarning() << "QQnxWindowGrabber: cannot get window size:" << strerror(errno);
260 return;
261 }
262
263 if (m_size.width() != size[0] || m_size.height() != size[1])
264 m_size = QSize(size[0], size[1]);
265
266 emit updateScene(m_size);
267}
268
269bool QQnxWindowGrabber::selectBuffer()
270{
271 // If we're using egl images we need to double buffer since the gpu may still be using the last
272 // video frame. If we're not, it doesn't matter since the data is immediately copied.
274 std::swap(m_frontBuffer, m_backBuffer);
275
276 if (m_frontBuffer)
277 return true;
278
279 auto frontBuffer = std::make_unique<QQnxWindowGrabberImage>();
280
281 if (!frontBuffer->initialize(m_screenContext))
282 return false;
283
284 m_frontBuffer = std::move(frontBuffer);
285
286 return true;
287}
288
290{
291 if (!selectBuffer())
292 return 0;
293
294 return m_frontBuffer->getTexture(m_window, m_size);
295}
296
298{
299 if (!selectBuffer())
300 return {};
301
302 return m_frontBuffer->getBuffer(m_window, m_size);
303}
304
305void QQnxWindowGrabber::resetBuffers()
306{
307 m_frontBuffer.reset();
308 m_backBuffer.reset();
309}
310
312 : m_pixmap(0),
314 m_bufferAddress(nullptr),
315 m_bufferStride(0)
316{
317}
318
320{
321 if (m_pixmap)
322 screen_destroy_pixmap(m_pixmap);
323}
324
325bool QQnxWindowGrabberImage::initialize(screen_context_t screenContext)
326{
327 if (screen_create_pixmap(&m_pixmap, screenContext) != 0) {
328 qWarning() << "QQnxWindowGrabber: cannot create pixmap:" << strerror(errno);
329 return false;
330 }
331 const int usage = SCREEN_USAGE_WRITE | SCREEN_USAGE_READ | SCREEN_USAGE_NATIVE;
332 screen_set_pixmap_property_iv(m_pixmap, SCREEN_PROPERTY_USAGE, &usage);
333
334 // XXX as a matter of fact, the underlying buffer is BGRX8888 (according to
335 // QNX, screen formats can be loose on the ARGB ordering) - as there is no
336 // SCREEN_FORMAT_BGRX8888 constant, we use SCREEN_FORMAT_RGBX8888, which
337 // carries the same depth and allows us to use the buffer.
338 const int format = SCREEN_FORMAT_RGBX8888;
339 screen_set_pixmap_property_iv(m_pixmap, SCREEN_PROPERTY_FORMAT, &format);
340
341 return true;
342}
343
344bool QQnxWindowGrabberImage::resize(const QSize &newSize)
345{
346 if (m_pixmapBuffer) {
347 screen_destroy_pixmap_buffer(m_pixmap);
348 m_pixmapBuffer = 0;
349 m_bufferAddress = 0;
350 m_bufferStride = 0;
351 }
352
353 const int size[2] = { newSize.width(), newSize.height() };
354
355 screen_set_pixmap_property_iv(m_pixmap, SCREEN_PROPERTY_BUFFER_SIZE, size);
356
357 if (screen_create_pixmap_buffer(m_pixmap) == 0) {
358 screen_get_pixmap_property_pv(m_pixmap, SCREEN_PROPERTY_RENDER_BUFFERS,
359 reinterpret_cast<void**>(&m_pixmapBuffer));
360 screen_get_buffer_property_pv(m_pixmapBuffer, SCREEN_PROPERTY_POINTER,
361 reinterpret_cast<void**>(&m_bufferAddress));
362 screen_get_buffer_property_iv(m_pixmapBuffer, SCREEN_PROPERTY_STRIDE, &m_bufferStride);
363 m_size = newSize;
364
365 return true;
366 } else {
367 m_size = QSize();
368 return false;
369 }
370}
371
372bool QQnxWindowGrabberImage::grab(screen_window_t window)
373{
374 const int rect[] = { 0, 0, m_size.width(), m_size.height() };
375 return screen_read_window(window, m_pixmapBuffer, 1, rect, 0) == 0;
376}
377
379 screen_window_t window, const QSize &size)
380{
381 if (size != m_size && !resize(size))
382 return {};
383
384 if (!m_bufferAddress || !grab(window))
385 return {};
386
387 return {
388 .width = m_size.width(),
389 .height = m_size.height(),
390 .stride = m_bufferStride,
391 .data = m_bufferAddress
392 };
393}
394
395GLuint QQnxWindowGrabberImage::getTexture(screen_window_t window, const QSize &size)
396{
397 if (size != m_size) {
398 // create a brand new texture to be the KHR image sibling, as
399 // previously used textures cannot be reused with new KHR image
400 // sources
401 GLuint glTexture = 0;
402 glGenTextures(1, &glTexture);
403 m_glTexture = GlTextureHandle(glTexture, GlTextureDeleter(QOpenGLContext::currentContext()));
404
405 const auto &eglImageFunctions = QEglImageFunctions::instance();
406
407 glBindTexture(GL_TEXTURE_2D, m_glTexture.get());
408 if (m_eglImage) {
409 eglImageFunctions.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, 0);
410 m_eglImage.reset();
411 }
412 if (!resize(size))
413 return 0;
414 m_eglImage = eglImageFunctions.eglCreateImage(eglGetDisplay(EGL_DEFAULT_DISPLAY),
415 EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR,
416 m_pixmap);
417 eglImageFunctions.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage.get());
418 }
419
420 if (!m_pixmap || !grab(window))
421 return 0;
422
423 return m_glTexture.get();
424}
425
426QT_END_NAMESPACE
427
428#include "moc_qqnxwindowgrabber_p.cpp"
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 setFrameRate(int frameRate)