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