1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module 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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://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 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qscreenqnx_qws.h"
43
44 #include <qapplication.h>
45 #include <qregexp.h>
46
47 #include <gf/gf.h>
48
49 QT_BEGIN_NAMESPACE
50
51 // This struct holds all the pointers to QNX's internals
52 struct QQnxScreenContext
53 {
QQnxScreenContextQQnxScreenContext54 inline QQnxScreenContext()
55 : device(0), display(0), layer(0), hwSurface(0), memSurface(0), context(0)
56 {}
~QQnxScreenContextQQnxScreenContext57 inline ~QQnxScreenContext()
58 { cleanup(); }
59
60 void cleanup();
61
62 gf_dev_t device;
63 gf_dev_info_t deviceInfo;
64 gf_display_t display;
65 gf_display_info_t displayInfo;
66 gf_layer_t layer;
67 gf_surface_t hwSurface;
68 gf_surface_t memSurface;
69 gf_surface_info_t memSurfaceInfo;
70 gf_context_t context;
71 };
72
cleanup()73 void QQnxScreenContext::cleanup()
74 {
75 if (context) {
76 gf_context_free(context);
77 context = 0;
78 }
79 if (memSurface) {
80 gf_surface_free(memSurface);
81 memSurface = 0;
82 }
83 if (hwSurface) {
84 gf_surface_free(hwSurface);
85 hwSurface = 0;
86 }
87 if (layer) {
88 gf_layer_detach(layer);
89 layer = 0;
90 }
91 if (display) {
92 gf_display_detach(display);
93 display = 0;
94 }
95 if (device) {
96 gf_dev_detach(device);
97 device = 0;
98 }
99 }
100
101
102 /*!
103 \class QQnxScreen
104 \preliminary
105 \ingroup qws
106 \since 4.6
107 \internal
108
109 \brief The QQnxScreen class implements a screen driver
110 for QNX io-display based devices.
111
112 Note - you never have to instanciate this class, the QScreenDriverFactory
113 does that for us based on the \c{QWS_DISPLAY} environment variable.
114
115 To activate this driver, set \c{QWS_DISPLAY} to \c{qnx}.
116
117 Example:
118 \c{QWS_DISPLAY=qnx; export QWS_DISPLAY}
119
120 By default, the main layer of the first display of the first device is used.
121 If you have multiple graphic cards, multiple displays or multiple layers and
122 don't want to connect to the default, you can override that with setting
123 the corresponding options \c{device}, \c{display} or \c{layer} in the \c{QWS_DISPLAY} variable:
124
125 \c{QWS_DISPLAY=qnx:device=3:display=4:layer=5}
126
127 In addition, it is suggested to set the physical width and height of the display.
128 QQnxScreen will use that information to compute the dots per inch (DPI) in order to render
129 fonts correctly. If this informaiton is omitted, QQnxScreen defaults to 72 dpi.
130
131 \c{QWS_DISPLAY=qnx:mmWidth=120:mmHeight=80}
132
133 \c{mmWidth} and \c{mmHeight} are the physical width/height of the screen in millimeters.
134
135 \sa QScreen, QScreenDriverPlugin, {Running Qt for Embedded Linux Applications}{Running Applications}
136 */
137
138 /*!
139 Constructs a QQnxScreen object. The \a display_id argument
140 identifies the Qt for Embedded Linux server to connect to.
141 */
QQnxScreen(int display_id)142 QQnxScreen::QQnxScreen(int display_id)
143 : QScreen(display_id), d(new QQnxScreenContext)
144 {
145 }
146
147 /*!
148 Destroys this QQnxScreen object.
149 */
~QQnxScreen()150 QQnxScreen::~QQnxScreen()
151 {
152 delete d;
153 }
154
155 /*!
156 \reimp
157 */
initDevice()158 bool QQnxScreen::initDevice()
159 {
160 #ifndef QT_NO_QWS_CURSOR
161 QScreenCursor::initSoftwareCursor();
162 #endif
163
164 return true;
165 }
166
167 /*!
168 \internal
169 Attaches to the named device \a name.
170 */
attachDevice(QQnxScreenContext * const d,const char * name)171 static inline bool attachDevice(QQnxScreenContext * const d, const char *name)
172 {
173 int ret = gf_dev_attach(&d->device, name, &d->deviceInfo);
174 if (ret != GF_ERR_OK) {
175 qWarning("QQnxScreen: gf_dev_attach(%s) failed with error code %d", name, ret);
176 return false;
177 }
178 return true;
179 }
180
181 /*!
182 \internal
183 Attaches to the display at index \a displayIndex.
184 */
attachDisplay(QQnxScreenContext * const d,int displayIndex)185 static inline bool attachDisplay(QQnxScreenContext * const d, int displayIndex)
186 {
187 int ret = gf_display_attach(&d->display, d->device, displayIndex, &d->displayInfo);
188 if (ret != GF_ERR_OK) {
189 qWarning("QQnxScreen: gf_display_attach(%d) failed with error code %d", displayIndex, ret);
190 return false;
191 }
192 return true;
193 }
194
195 /*!
196 \internal
197 Attaches to the layer \a layerIndex.
198 */
attachLayer(QQnxScreenContext * const d,int layerIndex)199 static inline bool attachLayer(QQnxScreenContext * const d, int layerIndex)
200 {
201 unsigned flags = QApplication::type() != QApplication::GuiServer ? GF_LAYER_ATTACH_PASSIVE : 0;
202 int ret = gf_layer_attach(&d->layer, d->display, layerIndex, flags);
203 if (ret != GF_ERR_OK) {
204 qWarning("QQnxScreen: gf_layer_attach(%d) failed with error code %d", layerIndex, ret);
205 return false;
206 }
207
208 return true;
209 }
210
211 /*!
212 \internal
213 Creates a new hardware surface (usually on the Gfx card memory) with the dimensions \a w * \a h.
214 */
createHwSurface(QQnxScreenContext * const d,int w,int h)215 static inline bool createHwSurface(QQnxScreenContext * const d, int w, int h)
216 {
217 int ret = gf_surface_create_layer(&d->hwSurface, &d->layer, 1, 0,
218 w, h, d->displayInfo.format, 0, 0);
219 if (ret != GF_ERR_OK) {
220 qWarning("QQnxScreen: gf_surface_create_layer(%dx%d) failed with error code %d", w, h, ret);
221 return false;
222 }
223
224 gf_layer_set_surfaces(d->layer, &d->hwSurface, 1);
225
226 gf_layer_enable(d->layer);
227
228 ret = gf_layer_update(d->layer, 0);
229 if (ret != GF_ERR_OK) {
230 qWarning("QQnxScreen: gf_layer_update() failed with error code %d\n", ret);
231 return false;
232 }
233
234 ret = gf_context_create(&d->context);
235 if (ret != GF_ERR_OK) {
236 qWarning("QQnxScreen: gf_context_create() failed with error code %d", ret);
237 return false;
238 }
239
240 ret = gf_context_set_surface(d->context, d->hwSurface);
241 if (ret != GF_ERR_OK) {
242 qWarning("QQnxScreen: gf_context_set_surface() failed with error code %d", ret);
243 return false;
244 }
245
246 return true;
247 }
248
249 /*!
250 \internal
251 Creates an in-memory, linear accessible surface of dimensions \a w * \a h.
252 This is the main surface that QWS blits to.
253 */
createMemSurface(QQnxScreenContext * const d,int w,int h)254 static inline bool createMemSurface(QQnxScreenContext * const d, int w, int h)
255 {
256 #ifndef QT_NO_QWS_MULTIPROCESS
257 if (QApplication::type() != QApplication::GuiServer) {
258 unsigned sidlist[64];
259 int n = gf_surface_sidlist(d->device, sidlist); // undocumented API
260 for (int i = 0; i < n; ++i) {
261 int ret = gf_surface_attach_by_sid(&d->memSurface, d->device, sidlist[i]);
262 if (ret == GF_ERR_OK) {
263 gf_surface_get_info(d->memSurface, &d->memSurfaceInfo);
264 if (d->memSurfaceInfo.sid != unsigned(GF_SID_INVALID)) {
265 // can we use the surface's vaddr?
266 unsigned flags = GF_SURFACE_CPU_LINEAR_READABLE | GF_SURFACE_CPU_LINEAR_WRITEABLE;
267 if ((d->memSurfaceInfo.flags & flags) == flags)
268 return true;
269 }
270
271 gf_surface_free(d->memSurface);
272 d->memSurface = 0;
273 }
274 }
275 qWarning("QQnxScreen: cannot attach to an usable surface; create a new one.");
276 }
277 #endif
278 int ret = gf_surface_create(&d->memSurface, d->device, w, h, d->displayInfo.format, 0,
279 GF_SURFACE_CREATE_CPU_FAST_ACCESS | GF_SURFACE_CREATE_CPU_LINEAR_ACCESSIBLE
280 | GF_SURFACE_CREATE_PHYS_CONTIG | GF_SURFACE_CREATE_SHAREABLE);
281 if (ret != GF_ERR_OK) {
282 qWarning("QQnxScreen: gf_surface_create(%dx%d) failed with error code %d",
283 w, h, ret);
284 return false;
285 }
286
287 gf_surface_get_info(d->memSurface, &d->memSurfaceInfo);
288
289 if (d->memSurfaceInfo.sid == unsigned(GF_SID_INVALID)) {
290 qWarning("QQnxScreen: gf_surface_get_info() failed.");
291 return false;
292 }
293
294 return true;
295 }
296
297 /*!
298 \reimp
299 Connects to QNX's io-display based device based on the \a displaySpec parameters
300 from the \c{QWS_DISPLAY} environment variable. See the QQnxScreen class documentation
301 for possible parameters.
302
303 \sa QQnxScreen
304 */
connect(const QString & displaySpec)305 bool QQnxScreen::connect(const QString &displaySpec)
306 {
307 const QStringList params = displaySpec.split(QLatin1Char(':'), QString::SkipEmptyParts);
308
309 // default to device 0
310 int deviceIndex = 0;
311 if (!params.isEmpty()) {
312 QRegExp deviceRegExp(QLatin1String("^device=(.+)$"));
313 if (params.indexOf(deviceRegExp) != -1)
314 deviceIndex = deviceRegExp.cap(1).toInt();
315 }
316
317 if (!attachDevice(d, GF_DEVICE_INDEX(deviceIndex)))
318 return false;
319
320 qDebug("QQnxScreen: Attached to Device, number of displays: %d", d->deviceInfo.ndisplays);
321
322 // default to display id passed to constructor
323 int displayIndex = displayId;
324 if (!params.isEmpty()) {
325 QRegExp displayRegexp(QLatin1String("^display=(\\d+)$"));
326 if (params.indexOf(displayRegexp) != -1)
327 displayIndex = displayRegexp.cap(1).toInt();
328 }
329
330 if (!attachDisplay(d, displayIndex))
331 return false;
332
333 qDebug("QQnxScreen: Attached to Display %d, resolution %dx%d, refresh %d Hz",
334 displayIndex, d->displayInfo.xres, d->displayInfo.yres, d->displayInfo.refresh);
335
336 // default to main_layer_index from the displayInfo struct
337 int layerIndex = d->displayInfo.main_layer_index;
338 if (!params.isEmpty()) {
339 QRegExp layerRegexp(QLatin1String("^layer=(\\d+)$"));
340 if (params.indexOf(layerRegexp) != -1)
341 layerIndex = layerRegexp.cap(1).toInt();
342 }
343
344 if (!attachLayer(d, layerIndex))
345 return false;
346
347 // determine the pixel format and the pixel type
348 switch (d->displayInfo.format) {
349 #if defined(QT_QWS_DEPTH_32) || defined(QT_QWS_DEPTH_GENERIC)
350 case GF_FORMAT_ARGB8888:
351 pixeltype = QScreen::BGRPixel;
352 // fall through
353 case GF_FORMAT_BGRA8888:
354 setPixelFormat(QImage::Format_ARGB32);
355 break;
356 #endif
357 #if defined(QT_QWS_DEPTH_24)
358 case GF_FORMAT_BGR888:
359 pixeltype = QScreen::BGRPixel;
360 setPixelFormat(QImage::Format_RGB888);
361 break;
362 #endif
363 #if defined(QT_QWS_DEPTH_16) || defined(QT_QWS_DEPTH_GENERIC)
364 case GF_FORMAT_PACK_RGB565:
365 case GF_FORMAT_PKLE_RGB565:
366 case GF_FORMAT_PKBE_RGB565:
367 #if Q_BYTE_ORDER == Q_BIG_ENDIAN
368 setFrameBufferLittleEndian((d->displayInfo.format & GF_FORMAT_PKLE) == GF_FORMAT_PKLE);
369 #endif
370 setPixelFormat(QImage::Format_RGB16);
371 break;
372 #endif
373 default:
374 return false;
375 }
376
377 // tell QWSDisplay the width and height of the display
378 w = dw = d->displayInfo.xres;
379 h = dh = d->displayInfo.yres;
380 QScreen::d = (d->displayInfo.format & GF_FORMAT_BPP); // colour depth
381
382 // assume 72 dpi as default, to calculate the physical dimensions if not specified
383 const int defaultDpi = 72;
384 // Handle display physical size
385 physWidth = qRound(dw * 25.4 / defaultDpi);
386 physHeight = qRound(dh * 25.4 / defaultDpi);
387 if (!params.isEmpty()) {
388 QRegExp mmWidthRegexp(QLatin1String("^mmWidth=(\\d+)$"));
389 if (params.indexOf(mmWidthRegexp) != -1)
390 physWidth = mmWidthRegexp.cap(1).toInt();
391
392 QRegExp mmHeightRegexp(QLatin1String("^mmHeight=(\\d+)$"));
393 if (params.indexOf(mmHeightRegexp) != -1)
394 physHeight = mmHeightRegexp.cap(1).toInt();
395 }
396
397 if (QApplication::type() == QApplication::GuiServer) {
398 // create a hardware surface with our dimensions. In the old days, it was possible
399 // to get a pointer directly to the hw surface, so we could blit directly. Now, we
400 // have to use one indirection more, because it's not guaranteed that the hw surface
401 // is mappable into our process.
402 if (!createHwSurface(d, w, h))
403 return false;
404 }
405
406 // create an in-memory linear surface that is used by QWS. QWS will blit directly in here.
407 if (!createMemSurface(d, w, h))
408 return false;
409
410 // set the address of the in-memory buffer that QWS is blitting to
411 data = d->memSurfaceInfo.vaddr;
412 // set the line stepping
413 lstep = d->memSurfaceInfo.stride;
414
415 // the overall size of the in-memory buffer is linestep * height
416 size = mapsize = lstep * h;
417
418 // done, the driver should be connected to the display now.
419 return true;
420 }
421
422 /*!
423 \reimp
424 */
disconnect()425 void QQnxScreen::disconnect()
426 {
427 d->cleanup();
428 }
429
430 /*!
431 \reimp
432 */
shutdownDevice()433 void QQnxScreen::shutdownDevice()
434 {
435 }
436
437 /*!
438 \reimp
439 QQnxScreen doesn't support setting the mode, use io-display instead.
440 */
setMode(int,int,int)441 void QQnxScreen::setMode(int,int,int)
442 {
443 qWarning("QQnxScreen: Unable to change mode, use io-display instead.");
444 }
445
446 /*!
447 \reimp
448 */
supportsDepth(int depth) const449 bool QQnxScreen::supportsDepth(int depth) const
450 {
451 gf_modeinfo_t displayMode;
452 for (int i = 0; gf_display_query_mode(d->display, i, &displayMode) == GF_ERR_OK; ++i) {
453 switch (displayMode.primary_format) {
454 #if defined(QT_QWS_DEPTH_32) || defined(QT_QWS_DEPTH_GENERIC)
455 case GF_FORMAT_ARGB8888:
456 case GF_FORMAT_BGRA8888:
457 if (depth == 32)
458 return true;
459 break;
460 #endif
461 #if defined(QT_QWS_DEPTH_24)
462 case GF_FORMAT_BGR888:
463 if (depth == 24)
464 return true;
465 break;
466 #endif
467 #if defined(QT_QWS_DEPTH_16) || defined(QT_QWS_DEPTH_GENERIC)
468 case GF_FORMAT_PACK_RGB565:
469 case GF_FORMAT_PKLE_RGB565:
470 case GF_FORMAT_PKBE_RGB565:
471 if (depth == 16)
472 return true;
473 break;
474 #endif
475 default:
476 break;
477 }
478 }
479
480 return false;
481 }
482
483 /*!
484 \reimp
485 */
blank(bool on)486 void QQnxScreen::blank(bool on)
487 {
488 int ret = gf_display_set_dpms(d->display, on ? GF_DPMS_OFF : GF_DPMS_ON);
489 if (ret != GF_ERR_OK)
490 qWarning("QQnxScreen: gf_display_set_dpms() failed with error code %d", ret);
491 }
492
493 /*!
494 \reimp
495 */
exposeRegion(QRegion r,int changing)496 void QQnxScreen::exposeRegion(QRegion r, int changing)
497 {
498 // here is where the actual magic happens. QWS will call exposeRegion whenever
499 // a region on the screen is dirty and needs to be updated on the actual screen.
500
501 // first, call the parent implementation. The parent implementation will update
502 // the region on our in-memory surface
503 QScreen::exposeRegion(r, changing);
504
505 #ifndef QT_NO_QWS_TRANSFORMED
506 if (qt_screen->isTransformed())
507 return;
508 #endif
509 // now our in-memory surface should be up to date with the latest changes.
510
511 if (!d->hwSurface)
512 return;
513
514 // the code below copies the region from the in-memory surface to the hardware.
515
516 // start drawing.
517 int ret = gf_draw_begin(d->context);
518 if (ret != GF_ERR_OK) {
519 qWarning("QQnxScreen: gf_draw_begin() failed with error code %d", ret);
520 return;
521 }
522 QVector<QRect> rects = r.rects();
523 Q_FOREACH (QRect rect, rects) {
524 if (!rect.isEmpty()) {
525 // blit the changed region from the memory surface to the hardware surface
526 ret = gf_draw_blit2(d->context, d->memSurface, d->hwSurface,
527 rect.x(), rect.y(), rect.right(), rect.bottom(), rect.x(), rect.y());
528 if (ret != GF_ERR_OK)
529 qWarning("QQnxScreen: gf_draw_blit2() failed with error code %d", ret);
530 }
531 }
532 // flush all drawing commands (in our case, a single blit)
533 ret = gf_draw_flush(d->context);
534 if (ret != GF_ERR_OK)
535 qWarning("QQnxScreen: gf_draw_flush() failed with error code %d", ret);
536
537 // tell QNX that we're done drawing.
538 gf_draw_end(d->context);
539 }
540
541 #ifndef QT_NO_QWS_TRANSFORMED
setDirty(const QRect & r)542 void QQnxScreen::setDirty(const QRect &r)
543 {
544 //This function is called only when the screen is transformed
545 if (!qt_screen->isTransformed())
546 return;
547
548 if (!d->hwSurface)
549 return;
550
551 int ret = gf_draw_begin(d->context);
552
553 if (ret != GF_ERR_OK) {
554 qWarning("QQnxScreen: gf_draw_begin() failed with error code %d in setDirty", ret);
555 return;
556 }
557
558 // blit the changed region from the memory surface to the hardware surface
559 ret = gf_draw_blit2(d->context, d->memSurface, d->hwSurface,
560 r.x(), r.y(), r.x()+ r.width(), r.y()+r.height(), r.x(), r.y());
561 if (ret != GF_ERR_OK)
562 qWarning("QQnxScreen: gf_draw_blit2() failed with error code %d in setDirty", ret);
563
564 ret = gf_draw_flush(d->context);
565 if (ret != GF_ERR_OK)
566 qWarning("QQnxScreen: gf_draw_flush() failed with error code %d in setDirty", ret);
567
568 // tell QNX that we're done drawing.
569 gf_draw_end(d->context);
570 }
571 #endif
572
573 QT_END_NAMESPACE
574