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 plugins 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 "qlinuxfbintegration.h"
43 #include "../fb_base/fb_base.h"
44 #include "qgenericunixfontdatabase.h"
45 #include <QtGui/private/qpixmap_raster_p.h>
46 #include <private/qcore_unix_p.h> // overrides QT_OPEN
47 #include <qimage.h>
48 #include <qdebug.h>
49 
50 #include <unistd.h>
51 #include <stdlib.h>
52 #include <sys/ioctl.h>
53 #include <sys/types.h>
54 #include <sys/stat.h>
55 #include <sys/mman.h>
56 #include <sys/kd.h>
57 #include <fcntl.h>
58 #include <errno.h>
59 #include <stdio.h>
60 #include <limits.h>
61 #include <signal.h>
62 
63 #if !defined(Q_OS_DARWIN) && !defined(Q_OS_FREEBSD)
64 #include <linux/fb.h>
65 
66 #ifdef __i386__
67 #include <asm/mtrr.h>
68 #endif
69 #endif
70 
71 QT_BEGIN_NAMESPACE
72 
73 class QLinuxFbIntegrationPrivate
74 {
75 public:
76     QLinuxFbIntegrationPrivate();
77     ~QLinuxFbIntegrationPrivate();
78 
79     void openTty();
80     void closeTty();
81 
82     int fd;
83     int startupw;
84     int startuph;
85     int startupd;
86     bool blank;
87 
88     bool doGraphicsMode;
89 #ifdef QT_QWS_DEPTH_GENERIC
90     bool doGenericColors;
91 #endif
92     int ttyfd;
93     long oldKdMode;
94     QString ttyDevice;
95     QString displaySpec;
96 };
97 
QLinuxFbIntegrationPrivate()98 QLinuxFbIntegrationPrivate::QLinuxFbIntegrationPrivate()
99     : fd(-1), blank(true), doGraphicsMode(true),
100 #ifdef QT_QWS_DEPTH_GENERIC
101       doGenericColors(false),
102 #endif
103       ttyfd(-1), oldKdMode(KD_TEXT)
104 {
105 }
106 
~QLinuxFbIntegrationPrivate()107 QLinuxFbIntegrationPrivate::~QLinuxFbIntegrationPrivate()
108 {
109     closeTty();
110 }
111 
openTty()112 void QLinuxFbIntegrationPrivate::openTty()
113 {
114     const char *const devs[] = {"/dev/tty0", "/dev/tty", "/dev/console", 0};
115 
116     if (ttyDevice.isEmpty()) {
117         for (const char * const *dev = devs; *dev; ++dev) {
118             ttyfd = QT_OPEN(*dev, O_RDWR);
119             if (ttyfd != -1)
120                 break;
121         }
122     } else {
123         ttyfd = QT_OPEN(ttyDevice.toAscii().constData(), O_RDWR);
124     }
125 
126     if (ttyfd == -1)
127         return;
128 
129     if (doGraphicsMode) {
130         ioctl(ttyfd, KDGETMODE, &oldKdMode);
131         if (oldKdMode != KD_GRAPHICS) {
132             int ret = ioctl(ttyfd, KDSETMODE, KD_GRAPHICS);
133             if (ret == -1)
134                 doGraphicsMode = false;
135         }
136     }
137 
138     // No blankin' screen, no blinkin' cursor!, no cursor!
139     const char termctl[] = "\033[9;0]\033[?33l\033[?25l\033[?1c";
140     QT_WRITE(ttyfd, termctl, sizeof(termctl));
141 }
142 
closeTty()143 void QLinuxFbIntegrationPrivate::closeTty()
144 {
145     if (ttyfd == -1)
146         return;
147 
148     if (doGraphicsMode)
149         ioctl(ttyfd, KDSETMODE, oldKdMode);
150 
151     // Blankin' screen, blinkin' cursor!
152     const char termctl[] = "\033[9;15]\033[?33h\033[?25h\033[?0c";
153     QT_WRITE(ttyfd, termctl, sizeof(termctl));
154 
155     QT_CLOSE(ttyfd);
156     ttyfd = -1;
157 }
158 
QLinuxFbIntegration()159 QLinuxFbIntegration::QLinuxFbIntegration()
160     :fontDb(new QGenericUnixFontDatabase())
161 {
162     d_ptr = new QLinuxFbIntegrationPrivate();
163 
164     // XXX
165     QString displaySpec = QString::fromLatin1(qgetenv("QWS_DISPLAY"));
166 
167     if (!connect(displaySpec))
168         qFatal("QLinuxFbIntegration: could not initialize screen");
169     initDevice();
170 
171     // Create a QImage directly on the screen's framebuffer.
172     // This is the blit target for copying windows to the screen.
173     mPrimaryScreen = new QLinuxFbScreen(data, w, h, lstep,
174                                                       screenFormat);
175     mPrimaryScreen->setPhysicalSize(QSize(physWidth, physHeight));
176     mScreens.append(mPrimaryScreen);
177 }
178 
~QLinuxFbIntegration()179 QLinuxFbIntegration::~QLinuxFbIntegration()
180 {
181     delete mPrimaryScreen;
182     delete d_ptr;
183 }
184 
connect(const QString & displaySpec)185 bool QLinuxFbIntegration::connect(const QString &displaySpec)
186 {
187     const QStringList args = displaySpec.split(QLatin1Char(':'));
188 
189     if (args.contains(QLatin1String("nographicsmodeswitch")))
190         d_ptr->doGraphicsMode = false;
191 
192 #ifdef QT_QWS_DEPTH_GENERIC
193     if (args.contains(QLatin1String("genericcolors")))
194         d_ptr->doGenericColors = true;
195 #endif
196 
197     QRegExp ttyRegExp(QLatin1String("tty=(.*)"));
198     if (args.indexOf(ttyRegExp) != -1)
199         d_ptr->ttyDevice = ttyRegExp.cap(1);
200 
201 #if 0
202 #if Q_BYTE_ORDER == Q_BIG_ENDIAN
203 #ifndef QT_QWS_FRAMEBUFFER_LITTLE_ENDIAN
204     if (args.contains(QLatin1String("littleendian")))
205 #endif
206         QScreen::setFrameBufferLittleEndian(true);
207 #endif
208 #endif
209 
210     // Check for explicitly specified device
211     const int len = 8; // "/dev/fbx"
212     int m = displaySpec.indexOf(QLatin1String("/dev/fb"));
213 
214     QString dev;
215     if (m > 0)
216         dev = displaySpec.mid(m, len);
217     else
218         dev = QLatin1String("/dev/fb0");
219 
220     if (access(dev.toLatin1().constData(), R_OK|W_OK) == 0)
221         d_ptr->fd = QT_OPEN(dev.toLatin1().constData(), O_RDWR);
222     if (d_ptr->fd == -1) {
223         if (access(dev.toLatin1().constData(), R_OK) == 0)
224             d_ptr->fd = QT_OPEN(dev.toLatin1().constData(), O_RDONLY);
225         if (d_ptr->fd == 1) {
226             qWarning("Error opening framebuffer device %s", qPrintable(dev));
227             return false;
228         }
229     }
230 
231     fb_fix_screeninfo finfo;
232     fb_var_screeninfo vinfo;
233     //#######################
234     // Shut up Valgrind
235     memset(&vinfo, 0, sizeof(vinfo));
236     memset(&finfo, 0, sizeof(finfo));
237     //#######################
238 
239     /* Get fixed screen information */
240     if (d_ptr->fd != -1 && ioctl(d_ptr->fd, FBIOGET_FSCREENINFO, &finfo)) {
241         perror("QLinuxFbIntegration::connect");
242         qWarning("Error reading fixed information");
243         return false;
244     }
245 
246     if (finfo.type == FB_TYPE_VGA_PLANES) {
247         qWarning("VGA16 video mode not supported");
248         return false;
249     }
250 
251     /* Get variable screen information */
252     if (d_ptr->fd != -1 && ioctl(d_ptr->fd, FBIOGET_VSCREENINFO, &vinfo)) {
253         perror("QLinuxFbIntegration::connect");
254         qWarning("Error reading variable information");
255         return false;
256     }
257 
258     grayscale = vinfo.grayscale;
259     d = vinfo.bits_per_pixel;
260     if (d == 24) {
261         d = vinfo.red.length + vinfo.green.length + vinfo.blue.length;
262         if (d <= 0)
263             d = 24; // reset if color component lengths are not reported
264     } else if (d == 16) {
265         d = vinfo.red.length + vinfo.green.length + vinfo.blue.length;
266         if (d <= 0)
267             d = 16;
268     }
269     lstep = finfo.line_length;
270 
271     int xoff = vinfo.xoffset;
272     int yoff = vinfo.yoffset;
273     const char* qwssize;
274     if((qwssize=::getenv("QWS_SIZE")) && sscanf(qwssize,"%dx%d",&w,&h)==2) {
275         if (d_ptr->fd != -1) {
276             if ((uint)w > vinfo.xres) w = vinfo.xres;
277             if ((uint)h > vinfo.yres) h = vinfo.yres;
278         }
279         dw=w;
280         dh=h;
281         int xxoff, yyoff;
282         if (sscanf(qwssize, "%*dx%*d+%d+%d", &xxoff, &yyoff) == 2) {
283             if (xxoff < 0 || xxoff + w > (int)(vinfo.xres))
284                 xxoff = vinfo.xres - w;
285             if (yyoff < 0 || yyoff + h > (int)(vinfo.yres))
286                 yyoff = vinfo.yres - h;
287             xoff += xxoff;
288             yoff += yyoff;
289         } else {
290             xoff += (vinfo.xres - w)/2;
291             yoff += (vinfo.yres - h)/2;
292         }
293     } else {
294         dw=w=vinfo.xres;
295         dh=h=vinfo.yres;
296     }
297 
298     if (w == 0 || h == 0) {
299         qWarning("QLinuxFbIntegration::connect(): Unable to find screen geometry, "
300                  "will use 320x240.");
301         dw = w = 320;
302         dh = h = 240;
303     }
304 
305     setPixelFormat(vinfo);
306 
307     // Handle display physical size spec.
308     QStringList displayArgs = displaySpec.split(QLatin1Char(':'));
309     QRegExp mmWidthRx(QLatin1String("mmWidth=?(\\d+)"));
310     int dimIdxW = displayArgs.indexOf(mmWidthRx);
311     QRegExp mmHeightRx(QLatin1String("mmHeight=?(\\d+)"));
312     int dimIdxH = displayArgs.indexOf(mmHeightRx);
313     if (dimIdxW >= 0) {
314         mmWidthRx.exactMatch(displayArgs.at(dimIdxW));
315         physWidth = mmWidthRx.cap(1).toInt();
316         if (dimIdxH < 0)
317             physHeight = dh*physWidth/dw;
318     }
319     if (dimIdxH >= 0) {
320         mmHeightRx.exactMatch(displayArgs.at(dimIdxH));
321         physHeight = mmHeightRx.cap(1).toInt();
322         if (dimIdxW < 0)
323             physWidth = dw*physHeight/dh;
324     }
325     if (dimIdxW < 0 && dimIdxH < 0) {
326         if (vinfo.width != 0 && vinfo.height != 0
327             && vinfo.width != UINT_MAX && vinfo.height != UINT_MAX) {
328             physWidth = vinfo.width;
329             physHeight = vinfo.height;
330         } else {
331             const int dpi = 72;
332             physWidth = qRound(dw * 25.4 / dpi);
333             physHeight = qRound(dh * 25.4 / dpi);
334         }
335     }
336 
337     dataoffset = yoff * lstep + xoff * d / 8;
338     //qDebug("Using %dx%dx%d screen",w,h,d);
339 
340     /* Figure out the size of the screen in bytes */
341     size = h * lstep;
342 
343     mapsize = finfo.smem_len;
344 
345     data = (unsigned char *)-1;
346     if (d_ptr->fd != -1)
347         data = (unsigned char *)mmap(0, mapsize, PROT_READ | PROT_WRITE,
348                                      MAP_SHARED, d_ptr->fd, 0);
349 
350     if ((long)data == -1) {
351         perror("QLinuxFbIntegration::connect");
352         qWarning("Error: failed to map framebuffer device to memory.");
353         return false;
354     } else {
355         data += dataoffset;
356     }
357 
358 #if 0
359     canaccel = useOffscreen();
360     if(canaccel)
361         setupOffScreen();
362 #endif
363     canaccel = false;
364 
365     // Now read in palette
366     if((vinfo.bits_per_pixel==8) || (vinfo.bits_per_pixel==4)) {
367         screencols= (vinfo.bits_per_pixel==8) ? 256 : 16;
368         int loopc;
369         fb_cmap startcmap;
370         startcmap.start=0;
371         startcmap.len=screencols;
372         startcmap.red=(unsigned short int *)
373                  malloc(sizeof(unsigned short int)*screencols);
374         startcmap.green=(unsigned short int *)
375                    malloc(sizeof(unsigned short int)*screencols);
376         startcmap.blue=(unsigned short int *)
377                   malloc(sizeof(unsigned short int)*screencols);
378         startcmap.transp=(unsigned short int *)
379                     malloc(sizeof(unsigned short int)*screencols);
380         if (d_ptr->fd == -1 || ioctl(d_ptr->fd, FBIOGETCMAP, &startcmap)) {
381             perror("QLinuxFbIntegration::connect");
382             qWarning("Error reading palette from framebuffer, using default palette");
383             createPalette(startcmap, vinfo, finfo);
384         }
385         int bits_used = 0;
386         for(loopc=0;loopc<screencols;loopc++) {
387             screenclut[loopc]=qRgb(startcmap.red[loopc] >> 8,
388                                    startcmap.green[loopc] >> 8,
389                                    startcmap.blue[loopc] >> 8);
390             bits_used |= startcmap.red[loopc]
391                          | startcmap.green[loopc]
392                          | startcmap.blue[loopc];
393         }
394         // WORKAROUND: Some framebuffer drivers only return 8 bit
395         // color values, so we need to not bit shift them..
396         if ((bits_used & 0x00ff) && !(bits_used & 0xff00)) {
397             for(loopc=0;loopc<screencols;loopc++) {
398                 screenclut[loopc] = qRgb(startcmap.red[loopc],
399                                          startcmap.green[loopc],
400                                          startcmap.blue[loopc]);
401             }
402             qWarning("8 bits cmap returned due to faulty FB driver, colors corrected");
403         }
404         free(startcmap.red);
405         free(startcmap.green);
406         free(startcmap.blue);
407         free(startcmap.transp);
408     } else {
409         screencols=0;
410     }
411 
412     return true;
413 }
414 
initDevice()415 bool QLinuxFbIntegration::initDevice()
416 {
417     d_ptr->openTty();
418 
419     // Grab current mode so we can reset it
420     fb_var_screeninfo vinfo;
421     fb_fix_screeninfo finfo;
422     //#######################
423     // Shut up Valgrind
424     memset(&vinfo, 0, sizeof(vinfo));
425     memset(&finfo, 0, sizeof(finfo));
426     //#######################
427 
428     if (ioctl(d_ptr->fd, FBIOGET_VSCREENINFO, &vinfo)) {
429         perror("QLinuxFbScreen::initDevice");
430         qFatal("Error reading variable information in card init");
431         return false;
432     }
433 
434 #ifdef DEBUG_VINFO
435     qDebug("Greyscale %d",vinfo.grayscale);
436     qDebug("Nonstd %d",vinfo.nonstd);
437     qDebug("Red %d %d %d",vinfo.red.offset,vinfo.red.length,
438            vinfo.red.msb_right);
439     qDebug("Green %d %d %d",vinfo.green.offset,vinfo.green.length,
440            vinfo.green.msb_right);
441     qDebug("Blue %d %d %d",vinfo.blue.offset,vinfo.blue.length,
442            vinfo.blue.msb_right);
443     qDebug("Transparent %d %d %d",vinfo.transp.offset,vinfo.transp.length,
444            vinfo.transp.msb_right);
445 #endif
446 
447     d_ptr->startupw=vinfo.xres;
448     d_ptr->startuph=vinfo.yres;
449     d_ptr->startupd=vinfo.bits_per_pixel;
450     grayscale = vinfo.grayscale;
451 
452     if (ioctl(d_ptr->fd, FBIOGET_FSCREENINFO, &finfo)) {
453         perror("QLinuxFbScreen::initDevice");
454         qCritical("Error reading fixed information in card init");
455         // It's not an /error/ as such, though definitely a bad sign
456         // so we return true
457         return true;
458     }
459 
460 #ifdef __i386__
461     // Now init mtrr
462     if(!::getenv("QWS_NOMTRR")) {
463         int mfd=QT_OPEN("/proc/mtrr",O_WRONLY,0);
464         // MTRR entry goes away when file is closed - i.e.
465         // hopefully when QWS is killed
466         if(mfd != -1) {
467             mtrr_sentry sentry;
468             sentry.base=(unsigned long int)finfo.smem_start;
469             //qDebug("Physical framebuffer address %p",(void*)finfo.smem_start);
470             // Size needs to be in 4k chunks, but that's not always
471             // what we get thanks to graphics card registers. Write combining
472             // these is Not Good, so we write combine what we can
473             // (which is not much - 4 megs on an 8 meg card, it seems)
474             unsigned int size=finfo.smem_len;
475             size=size >> 22;
476             size=size << 22;
477             sentry.size=size;
478             sentry.type=MTRR_TYPE_WRCOMB;
479             if(ioctl(mfd,MTRRIOC_ADD_ENTRY,&sentry)==-1) {
480                 //printf("Couldn't add mtrr entry for %lx %lx, %s\n",
481                 //sentry.base,sentry.size,strerror(errno));
482             }
483         }
484 
485         // Should we close mfd here?
486         //QT_CLOSE(mfd);
487     }
488 #endif
489     if ((vinfo.bits_per_pixel==8) || (vinfo.bits_per_pixel==4) || (finfo.visual==FB_VISUAL_DIRECTCOLOR))
490     {
491         fb_cmap cmap;
492         createPalette(cmap, vinfo, finfo);
493         if (ioctl(d_ptr->fd, FBIOPUTCMAP, &cmap)) {
494             perror("QLinuxFbScreen::initDevice");
495             qWarning("Error writing palette to framebuffer");
496         }
497         free(cmap.red);
498         free(cmap.green);
499         free(cmap.blue);
500         free(cmap.transp);
501     }
502 
503 #if 0
504     if (canaccel) {
505         *entryp=0;
506         *lowest = mapsize;
507         insert_entry(*entryp, *lowest, *lowest);  // dummy entry to mark start
508     }
509 
510     shared->fifocount = 0;
511     shared->buffer_offset = 0xffffffff;  // 0 would be a sensible offset (screen)
512     shared->linestep = 0;
513     shared->cliptop = 0xffffffff;
514     shared->clipleft = 0xffffffff;
515     shared->clipright = 0xffffffff;
516     shared->clipbottom = 0xffffffff;
517     shared->rop = 0xffffffff;
518 #endif
519 
520 #ifdef QT_QWS_DEPTH_GENERIC
521     if (pixelFormat() == QImage::Format_Invalid && screencols == 0
522         && d_ptr->doGenericColors)
523     {
524         qt_set_generic_blit(this, vinfo.bits_per_pixel,
525                             vinfo.red.length, vinfo.green.length,
526                             vinfo.blue.length, vinfo.transp.length,
527                             vinfo.red.offset, vinfo.green.offset,
528                             vinfo.blue.offset, vinfo.transp.offset);
529     }
530 #endif
531 
532 #if 0
533 #ifndef QT_NO_QWS_CURSOR
534     QScreenCursor::initSoftwareCursor();
535 #endif
536 #endif
537     blank(false);
538 
539     return true;
540 }
541 
setPixelFormat(struct fb_var_screeninfo info)542 void QLinuxFbIntegration::setPixelFormat(struct fb_var_screeninfo info)
543 {
544     const fb_bitfield rgba[4] = { info.red, info.green,
545                                   info.blue, info.transp };
546 
547     QImage::Format format = QImage::Format_Invalid;
548 
549     switch (d) {
550     case 32: {
551         const fb_bitfield argb8888[4] = {{16, 8, 0}, {8, 8, 0},
552                                          {0, 8, 0}, {24, 8, 0}};
553         const fb_bitfield abgr8888[4] = {{0, 8, 0}, {8, 8, 0},
554                                          {16, 8, 0}, {24, 8, 0}};
555         if (memcmp(rgba, argb8888, 4 * sizeof(fb_bitfield)) == 0) {
556             format = QImage::Format_ARGB32;
557         } else if (memcmp(rgba, argb8888, 3 * sizeof(fb_bitfield)) == 0) {
558             format = QImage::Format_RGB32;
559         } else if (memcmp(rgba, abgr8888, 3 * sizeof(fb_bitfield)) == 0) {
560             format = QImage::Format_RGB32;
561             pixeltype = BGRPixel;
562         }
563         break;
564     }
565     case 24: {
566         const fb_bitfield rgb888[4] = {{16, 8, 0}, {8, 8, 0},
567                                        {0, 8, 0}, {0, 0, 0}};
568         const fb_bitfield bgr888[4] = {{0, 8, 0}, {8, 8, 0},
569                                        {16, 8, 0}, {0, 0, 0}};
570         if (memcmp(rgba, rgb888, 3 * sizeof(fb_bitfield)) == 0) {
571             format = QImage::Format_RGB888;
572         } else if (memcmp(rgba, bgr888, 3 * sizeof(fb_bitfield)) == 0) {
573             format = QImage::Format_RGB888;
574             pixeltype = BGRPixel;
575         }
576         break;
577     }
578     case 18: {
579         const fb_bitfield rgb666[4] = {{12, 6, 0}, {6, 6, 0},
580                                        {0, 6, 0}, {0, 0, 0}};
581         if (memcmp(rgba, rgb666, 3 * sizeof(fb_bitfield)) == 0)
582             format = QImage::Format_RGB666;
583         break;
584     }
585     case 16: {
586         const fb_bitfield rgb565[4] = {{11, 5, 0}, {5, 6, 0},
587                                        {0, 5, 0}, {0, 0, 0}};
588         const fb_bitfield bgr565[4] = {{0, 5, 0}, {5, 6, 0},
589                                        {11, 5, 0}, {0, 0, 0}};
590         if (memcmp(rgba, rgb565, 3 * sizeof(fb_bitfield)) == 0) {
591             format = QImage::Format_RGB16;
592         } else if (memcmp(rgba, bgr565, 3 * sizeof(fb_bitfield)) == 0) {
593             format = QImage::Format_RGB16;
594             pixeltype = BGRPixel;
595         }
596         break;
597     }
598     case 15: {
599         const fb_bitfield rgb1555[4] = {{10, 5, 0}, {5, 5, 0},
600                                         {0, 5, 0}, {15, 1, 0}};
601         const fb_bitfield bgr1555[4] = {{0, 5, 0}, {5, 5, 0},
602                                         {10, 5, 0}, {15, 1, 0}};
603         if (memcmp(rgba, rgb1555, 3 * sizeof(fb_bitfield)) == 0) {
604             format = QImage::Format_RGB555;
605         } else if (memcmp(rgba, bgr1555, 3 * sizeof(fb_bitfield)) == 0) {
606             format = QImage::Format_RGB555;
607             pixeltype = BGRPixel;
608         }
609         break;
610     }
611     case 12: {
612         const fb_bitfield rgb444[4] = {{8, 4, 0}, {4, 4, 0},
613                                        {0, 4, 0}, {0, 0, 0}};
614         if (memcmp(rgba, rgb444, 3 * sizeof(fb_bitfield)) == 0)
615             format = QImage::Format_RGB444;
616         break;
617     }
618     case 8:
619         break;
620     case 1:
621         format = QImage::Format_Mono; //###: LSB???
622         break;
623     default:
624         break;
625     }
626 
627     screenFormat = format;
628 }
629 
createPalette(fb_cmap & cmap,fb_var_screeninfo & vinfo,fb_fix_screeninfo & finfo)630 void QLinuxFbIntegration::createPalette(fb_cmap &cmap, fb_var_screeninfo &vinfo, fb_fix_screeninfo &finfo)
631 {
632     if((vinfo.bits_per_pixel==8) || (vinfo.bits_per_pixel==4)) {
633         screencols= (vinfo.bits_per_pixel==8) ? 256 : 16;
634         cmap.start=0;
635         cmap.len=screencols;
636         cmap.red=(unsigned short int *)
637                  malloc(sizeof(unsigned short int)*screencols);
638         cmap.green=(unsigned short int *)
639                    malloc(sizeof(unsigned short int)*screencols);
640         cmap.blue=(unsigned short int *)
641                   malloc(sizeof(unsigned short int)*screencols);
642         cmap.transp=(unsigned short int *)
643                     malloc(sizeof(unsigned short int)*screencols);
644 
645         if (screencols==16) {
646             if (finfo.type == FB_TYPE_PACKED_PIXELS) {
647                 // We'll setup a grayscale cmap for 4bpp linear
648                 int val = 0;
649                 for (int idx = 0; idx < 16; ++idx, val += 17) {
650                     cmap.red[idx] = (val<<8)|val;
651                     cmap.green[idx] = (val<<8)|val;
652                     cmap.blue[idx] = (val<<8)|val;
653                     screenclut[idx]=qRgb(val, val, val);
654                 }
655             } else {
656                 // Default 16 colour palette
657                 // Green is now trolltech green so certain images look nicer
658                 //                             black  d_gray l_gray white  red  green  blue cyan magenta yellow
659                 unsigned char reds[16]   = { 0x00, 0x7F, 0xBF, 0xFF, 0xFF, 0xA2, 0x00, 0xFF, 0xFF, 0x00, 0x7F, 0x7F, 0x00, 0x00, 0x00, 0x82 };
660                 unsigned char greens[16] = { 0x00, 0x7F, 0xBF, 0xFF, 0x00, 0xC5, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x7F, 0x7F, 0x7F };
661                 unsigned char blues[16]  = { 0x00, 0x7F, 0xBF, 0xFF, 0x00, 0x11, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x7F, 0x7F, 0x7F, 0x00, 0x00 };
662 
663                 for (int idx = 0; idx < 16; ++idx) {
664                     cmap.red[idx] = ((reds[idx]) << 8)|reds[idx];
665                     cmap.green[idx] = ((greens[idx]) << 8)|greens[idx];
666                     cmap.blue[idx] = ((blues[idx]) << 8)|blues[idx];
667                     cmap.transp[idx] = 0;
668                     screenclut[idx]=qRgb(reds[idx], greens[idx], blues[idx]);
669                 }
670             }
671         } else {
672             if (grayscale) {
673                 // Build grayscale palette
674                 int i;
675                 for(i=0;i<screencols;++i) {
676                     int bval = screencols == 256 ? i : (i << 4);
677                     ushort val = (bval << 8) | bval;
678                     cmap.red[i] = val;
679                     cmap.green[i] = val;
680                     cmap.blue[i] = val;
681                     cmap.transp[i] = 0;
682                     screenclut[i] = qRgb(bval,bval,bval);
683                 }
684             } else {
685                 // 6x6x6 216 color cube
686                 int idx = 0;
687                 for(int ir = 0x0; ir <= 0xff; ir+=0x33) {
688                     for(int ig = 0x0; ig <= 0xff; ig+=0x33) {
689                         for(int ib = 0x0; ib <= 0xff; ib+=0x33) {
690                             cmap.red[idx] = (ir << 8)|ir;
691                             cmap.green[idx] = (ig << 8)|ig;
692                             cmap.blue[idx] = (ib << 8)|ib;
693                             cmap.transp[idx] = 0;
694                             screenclut[idx]=qRgb(ir, ig, ib);
695                             ++idx;
696                         }
697                     }
698                 }
699                 // Fill in rest with 0
700                 for (int loopc=0; loopc<40; ++loopc) {
701                     screenclut[idx]=0;
702                     ++idx;
703                 }
704                 screencols=idx;
705             }
706         }
707     } else if(finfo.visual==FB_VISUAL_DIRECTCOLOR) {
708         cmap.start=0;
709         int rbits=0,gbits=0,bbits=0;
710         switch (vinfo.bits_per_pixel) {
711         case 8:
712             rbits=vinfo.red.length;
713             gbits=vinfo.green.length;
714             bbits=vinfo.blue.length;
715             if(rbits==0 && gbits==0 && bbits==0) {
716                 // cyber2000 driver bug hack
717                 rbits=3;
718                 gbits=3;
719                 bbits=2;
720             }
721             break;
722         case 15:
723             rbits=5;
724             gbits=5;
725             bbits=5;
726             break;
727         case 16:
728             rbits=5;
729             gbits=6;
730             bbits=5;
731             break;
732         case 18:
733         case 19:
734             rbits=6;
735             gbits=6;
736             bbits=6;
737             break;
738         case 24: case 32:
739             rbits=gbits=bbits=8;
740             break;
741         }
742         screencols=cmap.len=1<<qMax(rbits,qMax(gbits,bbits));
743         cmap.red=(unsigned short int *)
744                  malloc(sizeof(unsigned short int)*256);
745         cmap.green=(unsigned short int *)
746                    malloc(sizeof(unsigned short int)*256);
747         cmap.blue=(unsigned short int *)
748                   malloc(sizeof(unsigned short int)*256);
749         cmap.transp=(unsigned short int *)
750                     malloc(sizeof(unsigned short int)*256);
751         for(unsigned int i = 0x0; i < cmap.len; i++) {
752             cmap.red[i] = i*65535/((1<<rbits)-1);
753             cmap.green[i] = i*65535/((1<<gbits)-1);
754             cmap.blue[i] = i*65535/((1<<bbits)-1);
755             cmap.transp[i] = 0;
756         }
757     }
758 }
759 
blank(bool on)760 void QLinuxFbIntegration::blank(bool on)
761 {
762     if (d_ptr->blank == on)
763         return;
764 
765 #if defined(QT_QWS_IPAQ)
766     if (on)
767         system("apm -suspend");
768 #else
769     if (d_ptr->fd == -1)
770         return;
771 // Some old kernel versions don't have this.  These defines should go
772 // away eventually
773 #if defined(FBIOBLANK)
774 #if defined(VESA_POWERDOWN) && defined(VESA_NO_BLANKING)
775     ioctl(d_ptr->fd, FBIOBLANK, on ? VESA_POWERDOWN : VESA_NO_BLANKING);
776 #else
777     ioctl(d_ptr->fd, FBIOBLANK, on ? 1 : 0);
778 #endif
779 #endif
780 #endif
781 
782     d_ptr->blank = on;
783 }
784 
hasCapability(QPlatformIntegration::Capability cap) const785 bool QLinuxFbIntegration::hasCapability(QPlatformIntegration::Capability cap) const
786 {
787     switch (cap) {
788     case ThreadedPixmaps: return true;
789     default: return QPlatformIntegration::hasCapability(cap);
790     }
791 }
792 
793 
createPixmapData(QPixmapData::PixelType type) const794 QPixmapData *QLinuxFbIntegration::createPixmapData(QPixmapData::PixelType type) const
795 {
796     return new QRasterPixmapData(type);
797 }
798 
createWindowSurface(QWidget * widget,WId) const799 QWindowSurface *QLinuxFbIntegration::createWindowSurface(QWidget *widget, WId) const
800 {
801     QFbWindowSurface * surface =
802         new QFbWindowSurface(mPrimaryScreen, widget);
803     return surface;
804 }
805 
createPlatformWindow(QWidget * widget,WId) const806 QPlatformWindow *QLinuxFbIntegration::createPlatformWindow(QWidget *widget, WId /*winId*/) const
807 {
808     QFbWindow *w = new QFbWindow(widget);
809     mPrimaryScreen->addWindow(w);
810     return w;
811 }
812 
fontDatabase() const813 QPlatformFontDatabase *QLinuxFbIntegration::fontDatabase() const
814 {
815     return fontDb;
816 }
817 
QLinuxFbScreen(uchar * d,int w,int h,int lstep,QImage::Format screenFormat)818 QLinuxFbScreen::QLinuxFbScreen(uchar * d, int w,
819     int h, int lstep, QImage::Format screenFormat) : compositePainter(0)
820 {
821     data = d;
822     mGeometry = QRect(0,0,w,h);
823     bytesPerLine = lstep;
824     mFormat = screenFormat;
825     mDepth = 16;
826     mScreenImage = new QImage(mGeometry.width(), mGeometry.height(),
827                               mFormat);
828     mFbScreenImage = new QImage(data, mGeometry.width(), mGeometry.height(),
829                               bytesPerLine, mFormat);
830 #ifndef QT_NO_CURSOR
831     cursor = new QPlatformSoftwareCursor(this);
832 #endif
833 }
834 
setGeometry(QRect rect)835 void QLinuxFbScreen::setGeometry(QRect rect)
836 {
837     mGeometry = rect;
838     delete mFbScreenImage;
839     mFbScreenImage = new QImage(data, mGeometry.width(), mGeometry.height(),
840                            bytesPerLine, mFormat);
841     delete compositePainter;
842     compositePainter = 0;
843 
844     delete mScreenImage;
845     mScreenImage = new QImage(mGeometry.width(), mGeometry.height(),
846                               mFormat);
847 }
848 
setFormat(QImage::Format format)849 void QLinuxFbScreen::setFormat(QImage::Format format)
850 {
851     mFormat = format;
852     delete mFbScreenImage;
853     mFbScreenImage = new QImage(data, mGeometry.width(), mGeometry.height(),
854                              bytesPerLine, mFormat);
855     delete compositePainter;
856     compositePainter = 0;
857 
858     delete mScreenImage;
859     mScreenImage = new QImage(mGeometry.width(), mGeometry.height(),
860                               mFormat);
861 }
862 
doRedraw()863 QRegion QLinuxFbScreen::doRedraw()
864 {
865     QRegion touched;
866     touched = QFbScreen::doRedraw();
867 
868     if (!compositePainter) {
869         compositePainter = new QPainter(mFbScreenImage);
870     }
871 
872     QVector<QRect> rects = touched.rects();
873     for (int i = 0; i < rects.size(); i++)
874         compositePainter->drawImage(rects[i], *mScreenImage, rects[i]);
875     return touched;
876 }
877 QT_END_NAMESPACE
878