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 #ifndef QT_NO_QWS_QVFB
43 
44 #define QTOPIA_QVFB_BRIGHTNESS
45 
46 #include <stdlib.h>
47 #include <sys/types.h>
48 #include <sys/ipc.h>
49 #include <sys/shm.h>
50 #include <sys/stat.h>
51 #include <unistd.h>
52 #include <fcntl.h>
53 #include <errno.h>
54 
55 #include <qvfbhdr.h>
56 #include <qscreenvfb_qws.h>
57 #include <qkbdvfb_qws.h>
58 #include <qmousevfb_qws.h>
59 #include <qwindowsystem_qws.h>
60 #include <qsocketnotifier.h>
61 #include <qapplication.h>
62 #include <qscreen_qws.h>
63 #include <qmousedriverfactory_qws.h>
64 #include <qkbddriverfactory_qws.h>
65 #include <qdebug.h>
66 
67 QT_BEGIN_NAMESPACE
68 
69 class QVFbScreenPrivate
70 {
71 public:
72     QVFbScreenPrivate();
73     ~QVFbScreenPrivate();
74 
75     bool success;
76     unsigned char *shmrgn;
77     int brightness;
78     bool blank;
79     QVFbHeader *hdr;
80     QWSMouseHandler *mouse;
81 #ifndef QT_NO_QWS_KEYBOARD
82     QWSKeyboardHandler *keyboard;
83 #endif
84 };
85 
QVFbScreenPrivate()86 QVFbScreenPrivate::QVFbScreenPrivate()
87     : mouse(0)
88 
89 {
90 #ifndef QT_NO_QWS_KEYBOARD
91     keyboard = 0;
92 #endif
93     brightness = 255;
94     blank = false;
95 }
96 
~QVFbScreenPrivate()97 QVFbScreenPrivate::~QVFbScreenPrivate()
98 {
99     delete mouse;
100 #ifndef QT_NO_QWS_KEYBOARD
101     delete keyboard;
102 #endif
103 }
104 
105 /*!
106     \internal
107 
108     \class QVFbScreen
109     \ingroup qws
110 
111     \brief The QVFbScreen class implements a screen driver for the
112     virtual framebuffer.
113 
114     Note that this class is only available in \l{Qt for Embedded Linux}.
115     Custom screen drivers can be added by subclassing the
116     QScreenDriverPlugin class, using the QScreenDriverFactory class to
117     dynamically load the driver into the application, but there should
118     only be one screen object per application.
119 
120     The Qt for Embedded Linux platform provides a \l{The Virtual
121     Framebuffer}{virtual framebuffer} for development and debugging;
122     the virtual framebuffer allows Qt for Embedded Linux applications to be
123     developed on a desktop machine, without switching between consoles
124     and X11.
125 
126     \sa QScreen, QScreenDriverPlugin, {Running Applications}
127 */
128 
129 /*!
130     \fn bool QVFbScreen::connect(const QString & displaySpec)
131     \reimp
132 */
133 
134 /*!
135     \fn void QVFbScreen::disconnect()
136     \reimp
137 */
138 
139 /*!
140     \fn bool QVFbScreen::initDevice()
141     \reimp
142 */
143 
144 /*!
145     \fn void QVFbScreen::restore()
146     \reimp
147 */
148 
149 /*!
150     \fn void QVFbScreen::save()
151     \reimp
152 */
153 
154 /*!
155     \fn void QVFbScreen::setDirty(const QRect & r)
156     \reimp
157 */
158 
159 /*!
160     \fn void QVFbScreen::setMode(int nw, int nh, int nd)
161     \reimp
162 */
163 
164 /*!
165     \fn void QVFbScreen::shutdownDevice()
166     \reimp
167 */
168 
169 /*!
170     \fn QVFbScreen::QVFbScreen(int displayId)
171 
172     Constructs a QVNCScreen object. The \a displayId argument
173     identifies the Qt for Embedded Linux server to connect to.
174 */
QVFbScreen(int display_id)175 QVFbScreen::QVFbScreen(int display_id)
176     : QScreen(display_id, VFbClass), d_ptr(new QVFbScreenPrivate)
177 {
178     d_ptr->shmrgn = 0;
179     d_ptr->hdr = 0;
180     data = 0;
181 }
182 
183 /*!
184     Destroys this QVFbScreen object.
185 */
~QVFbScreen()186 QVFbScreen::~QVFbScreen()
187 {
188     delete d_ptr;
189 }
190 
191 static QVFbScreen *connected = 0;
192 
connect(const QString & displaySpec)193 bool QVFbScreen::connect(const QString &displaySpec)
194 {
195     QStringList displayArgs = displaySpec.split(QLatin1Char(':'));
196     if (displayArgs.contains(QLatin1String("Gray")))
197         grayscale = true;
198 
199     key_t key = ftok(QT_VFB_MOUSE_PIPE(displayId).toLocal8Bit(), 'b');
200 
201     if (key == -1)
202         return false;
203 
204 #if Q_BYTE_ORDER == Q_BIG_ENDIAN
205 #ifndef QT_QWS_FRAMEBUFFER_LITTLE_ENDIAN
206     if (displayArgs.contains(QLatin1String("littleendian")))
207 #endif
208         QScreen::setFrameBufferLittleEndian(true);
209 #endif
210 
211     int shmId = shmget(key, 0, 0);
212     if (shmId != -1)
213         d_ptr->shmrgn = (unsigned char *)shmat(shmId, 0, 0);
214     else
215         return false;
216 
217     if ((long)d_ptr->shmrgn == -1 || d_ptr->shmrgn == 0) {
218         qDebug("No shmrgn %ld", (long)d_ptr->shmrgn);
219         return false;
220     }
221 
222     d_ptr->hdr = (QVFbHeader *)d_ptr->shmrgn;
223     data = d_ptr->shmrgn + d_ptr->hdr->dataoffset;
224 
225     dw = w = d_ptr->hdr->width;
226     dh = h = d_ptr->hdr->height;
227     d = d_ptr->hdr->depth;
228 
229     switch (d) {
230     case 1:
231         setPixelFormat(QImage::Format_Mono);
232         break;
233     case 8:
234         setPixelFormat(QImage::Format_Indexed8);
235         break;
236     case 12:
237         setPixelFormat(QImage::Format_RGB444);
238         break;
239     case 15:
240         setPixelFormat(QImage::Format_RGB555);
241         break;
242     case 16:
243         setPixelFormat(QImage::Format_RGB16);
244         break;
245     case 18:
246         setPixelFormat(QImage::Format_RGB666);
247         break;
248     case 24:
249         setPixelFormat(QImage::Format_RGB888);
250         break;
251     case 32:
252         setPixelFormat(QImage::Format_ARGB32_Premultiplied);
253         break;
254     }
255 
256     lstep = d_ptr->hdr->linestep;
257 
258     // Handle display physical size spec.
259     int dimIdxW = -1;
260     int dimIdxH = -1;
261     for (int i = 0; i < displayArgs.size(); ++i) {
262         if (displayArgs.at(i).startsWith(QLatin1String("mmWidth"))) {
263             dimIdxW = i;
264             break;
265         }
266     }
267     for (int i = 0; i < displayArgs.size(); ++i) {
268         if (displayArgs.at(i).startsWith(QLatin1String("mmHeight"))) {
269             dimIdxH = i;
270             break;
271         }
272     }
273     if (dimIdxW >= 0) {
274         bool ok;
275         int pos = 7;
276         if (displayArgs.at(dimIdxW).at(pos) == QLatin1Char('='))
277             ++pos;
278         int pw = displayArgs.at(dimIdxW).mid(pos).toInt(&ok);
279         if (ok) {
280             physWidth = pw;
281             if (dimIdxH < 0)
282                 physHeight = dh*physWidth/dw;
283         }
284     }
285     if (dimIdxH >= 0) {
286         bool ok;
287         int pos = 8;
288         if (displayArgs.at(dimIdxH).at(pos) == QLatin1Char('='))
289             ++pos;
290         int ph = displayArgs.at(dimIdxH).mid(pos).toInt(&ok);
291         if (ok) {
292             physHeight = ph;
293             if (dimIdxW < 0)
294                 physWidth = dw*physHeight/dh;
295         }
296     }
297     if (dimIdxW < 0 && dimIdxH < 0) {
298         const int dpi = 72;
299         physWidth = qRound(dw * 25.4 / dpi);
300         physHeight = qRound(dh * 25.4 / dpi);
301     }
302 
303     qDebug("Connected to VFB server %s: %d x %d x %d %dx%dmm (%dx%ddpi)", displaySpec.toLatin1().data(),
304         w, h, d, physWidth, physHeight, qRound(dw*25.4/physWidth), qRound(dh*25.4/physHeight) );
305 
306     size = lstep * h;
307     mapsize = size;
308     screencols = d_ptr->hdr->numcols;
309     memcpy(screenclut, d_ptr->hdr->clut, sizeof(QRgb) * screencols);
310 
311     connected = this;
312 
313     if (qgetenv("QT_QVFB_BGR").toInt())
314         pixeltype = BGRPixel;
315 
316     return true;
317 }
318 
disconnect()319 void QVFbScreen::disconnect()
320 {
321     connected = 0;
322     if ((long)d_ptr->shmrgn != -1 && d_ptr->shmrgn) {
323         if (qApp->type() == QApplication::GuiServer && d_ptr->hdr->dataoffset >= (int)sizeof(QVFbHeader)) {
324             d_ptr->hdr->serverVersion = 0;
325         }
326         shmdt((char*)d_ptr->shmrgn);
327     }
328 }
329 
initDevice()330 bool QVFbScreen::initDevice()
331 {
332 #ifndef QT_NO_QWS_MOUSE_QVFB
333     const QString mouseDev = QT_VFB_MOUSE_PIPE(displayId);
334     d_ptr->mouse = new QVFbMouseHandler(QLatin1String("QVFbMouse"), mouseDev);
335     qwsServer->setDefaultMouse("None");
336     if (d_ptr->mouse)
337         d_ptr->mouse->setScreen(this);
338 #endif
339 
340 #if !defined(QT_NO_QWS_KBD_QVFB) && !defined(QT_NO_QWS_KEYBOARD)
341     const QString keyboardDev = QT_VFB_KEYBOARD_PIPE(displayId);
342     d_ptr->keyboard = new QVFbKeyboardHandler(keyboardDev);
343     qwsServer->setDefaultKeyboard("None");
344 #endif
345 
346     if (d_ptr->hdr->dataoffset >= (int)sizeof(QVFbHeader))
347         d_ptr->hdr->serverVersion = QT_VERSION;
348 
349     if(d==8) {
350         screencols=256;
351         if (grayscale) {
352             // Build grayscale palette
353             for(int loopc=0;loopc<256;loopc++) {
354                 screenclut[loopc]=qRgb(loopc,loopc,loopc);
355             }
356         } else {
357             // 6x6x6 216 color cube
358             int idx = 0;
359             for(int ir = 0x0; ir <= 0xff; ir+=0x33) {
360                 for(int ig = 0x0; ig <= 0xff; ig+=0x33) {
361                     for(int ib = 0x0; ib <= 0xff; ib+=0x33) {
362                         screenclut[idx]=qRgb(ir, ig, ib);
363                         idx++;
364                     }
365                 }
366             }
367             screencols=idx;
368         }
369         memcpy(d_ptr->hdr->clut, screenclut, sizeof(QRgb) * screencols);
370         d_ptr->hdr->numcols = screencols;
371     } else if (d == 4) {
372         int val = 0;
373         for (int idx = 0; idx < 16; idx++, val += 17) {
374             screenclut[idx] = qRgb(val, val, val);
375         }
376         screencols = 16;
377         memcpy(d_ptr->hdr->clut, screenclut, sizeof(QRgb) * screencols);
378         d_ptr->hdr->numcols = screencols;
379     } else if (d == 1) {
380         screencols = 2;
381         screenclut[1] = qRgb(0xff, 0xff, 0xff);
382         screenclut[0] = qRgb(0, 0, 0);
383         memcpy(d_ptr->hdr->clut, screenclut, sizeof(QRgb) * screencols);
384         d_ptr->hdr->numcols = screencols;
385     }
386 
387 #ifndef QT_NO_QWS_CURSOR
388     QScreenCursor::initSoftwareCursor();
389 #endif
390     return true;
391 }
392 
shutdownDevice()393 void QVFbScreen::shutdownDevice()
394 {
395 }
396 
setMode(int,int,int)397 void QVFbScreen::setMode(int ,int ,int)
398 {
399 }
400 
401 // save the state of the graphics card
402 // This is needed so that e.g. we can restore the palette when switching
403 // between linux virtual consoles.
save()404 void QVFbScreen::save()
405 {
406     // nothing to do.
407 }
408 
409 // restore the state of the graphics card.
restore()410 void QVFbScreen::restore()
411 {
412 }
setDirty(const QRect & rect)413 void QVFbScreen::setDirty(const QRect& rect)
414 {
415     const QRect r = rect.translated(-offset());
416     d_ptr->hdr->dirty = true;
417     d_ptr->hdr->update = d_ptr->hdr->update.united(r);
418 }
419 
setBrightness(int b)420 void QVFbScreen::setBrightness(int b)
421 {
422     if (connected) {
423         connected->d_ptr->brightness = b;
424 
425         QVFbHeader *hdr = connected->d_ptr->hdr;
426         if (hdr->viewerVersion < 0x040400) // brightness not supported
427             return;
428 
429         const int br = connected->d_ptr->blank ? 0 : b;
430         if (hdr->brightness != br) {
431             hdr->brightness = br;
432             connected->setDirty(connected->region().boundingRect());
433         }
434     }
435 }
436 
blank(bool on)437 void QVFbScreen::blank(bool on)
438 {
439     d_ptr->blank = on;
440     setBrightness(connected->d_ptr->brightness);
441 }
442 
443 #endif // QT_NO_QWS_QVFB
444 
445 QT_END_NAMESPACE
446