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 "qscreenlinuxfb_qws.h"
43 
44 #ifndef QT_NO_QWS_LINUXFB
45 //#include "qmemorymanager_qws.h"
46 #include "qwsdisplay_qws.h"
47 #include "qpixmap.h"
48 #include <private/qwssignalhandler_p.h>
49 #include <private/qcore_unix_p.h> // overrides QT_OPEN
50 
51 #include <unistd.h>
52 #include <stdlib.h>
53 #include <sys/ioctl.h>
54 #include <sys/types.h>
55 #include <sys/stat.h>
56 #include <sys/mman.h>
57 #include <sys/kd.h>
58 #include <fcntl.h>
59 #include <errno.h>
60 #include <stdio.h>
61 #include <limits.h>
62 #include <signal.h>
63 
64 #include "qwindowsystem_qws.h"
65 
66 #if !defined(Q_OS_DARWIN) && !defined(Q_OS_FREEBSD)
67 #include <linux/fb.h>
68 
69 #ifdef __i386__
70 #include <asm/mtrr.h>
71 #endif
72 #endif
73 
74 QT_BEGIN_NAMESPACE
75 
76 extern int qws_client_id;
77 
78 //#define DEBUG_CACHE
79 
80 class QLinuxFbScreenPrivate : public QObject
81 {
82 public:
83     QLinuxFbScreenPrivate();
84     ~QLinuxFbScreenPrivate();
85 
86     void openTty();
87     void closeTty();
88 
89     int fd;
90     int startupw;
91     int startuph;
92     int startupd;
93     bool blank;
94     QLinuxFbScreen::DriverTypes driverType;
95 
96     bool doGraphicsMode;
97 #ifdef QT_QWS_DEPTH_GENERIC
98     bool doGenericColors;
99 #endif
100     int ttyfd;
101     long oldKdMode;
102     QString ttyDevice;
103     QString displaySpec;
104 };
105 
QLinuxFbScreenPrivate()106 QLinuxFbScreenPrivate::QLinuxFbScreenPrivate()
107     : fd(-1), blank(true), doGraphicsMode(true),
108 #ifdef QT_QWS_DEPTH_GENERIC
109       doGenericColors(false),
110 #endif
111       ttyfd(-1), oldKdMode(KD_TEXT)
112 {
113 #ifndef QT_NO_QWS_SIGNALHANDLER
114     QWSSignalHandler::instance()->addObject(this);
115 #endif
116 }
117 
~QLinuxFbScreenPrivate()118 QLinuxFbScreenPrivate::~QLinuxFbScreenPrivate()
119 {
120     closeTty();
121 }
122 
openTty()123 void QLinuxFbScreenPrivate::openTty()
124 {
125     const char *const devs[] = {"/dev/tty0", "/dev/tty", "/dev/console", 0};
126 
127     if (ttyDevice.isEmpty()) {
128         for (const char * const *dev = devs; *dev; ++dev) {
129             ttyfd = QT_OPEN(*dev, O_RDWR);
130             if (ttyfd != -1)
131                 break;
132         }
133     } else {
134         ttyfd = QT_OPEN(ttyDevice.toAscii().constData(), O_RDWR);
135     }
136 
137     if (ttyfd == -1)
138         return;
139 
140     if (doGraphicsMode) {
141         ioctl(ttyfd, KDGETMODE, &oldKdMode);
142         if (oldKdMode != KD_GRAPHICS) {
143             int ret = ioctl(ttyfd, KDSETMODE, KD_GRAPHICS);
144             if (ret == -1)
145                 doGraphicsMode = false;
146         }
147     }
148 
149     // No blankin' screen, no blinkin' cursor!, no cursor!
150     const char termctl[] = "\033[9;0]\033[?33l\033[?25l\033[?1c";
151     QT_WRITE(ttyfd, termctl, sizeof(termctl));
152 }
153 
closeTty()154 void QLinuxFbScreenPrivate::closeTty()
155 {
156     if (ttyfd == -1)
157         return;
158 
159     if (doGraphicsMode)
160         ioctl(ttyfd, KDSETMODE, oldKdMode);
161 
162     // Blankin' screen, blinkin' cursor!
163     const char termctl[] = "\033[9;15]\033[?33h\033[?25h\033[?0c";
164     QT_WRITE(ttyfd, termctl, sizeof(termctl));
165 
166     QT_CLOSE(ttyfd);
167     ttyfd = -1;
168 }
169 
170 /*!
171     \enum QLinuxFbScreen::DriverTypes
172 
173     This enum describes the driver type.
174 
175     \value GenericDriver Generic Linux framebuffer driver
176     \value EInk8Track e-Ink framebuffer driver using the 8Track chipset
177  */
178 
179 /*!
180     \fn QLinuxFbScreen::fixupScreenInfo(fb_fix_screeninfo &finfo, fb_var_screeninfo &vinfo)
181 
182     Adjust the values returned by the framebuffer driver, to work
183     around driver bugs or nonstandard behavior in certain drivers.
184     \a finfo and \a vinfo specify the fixed and variable screen info
185     returned by the driver.
186  */
fixupScreenInfo(fb_fix_screeninfo & finfo,fb_var_screeninfo & vinfo)187 void QLinuxFbScreen::fixupScreenInfo(fb_fix_screeninfo &finfo, fb_var_screeninfo &vinfo)
188 {
189     // 8Track e-ink devices (as found in Sony PRS-505) lie
190     // about their bit depth -- they claim they're 1 bit per
191     // pixel while the only supported mode is 8 bit per pixel
192     // grayscale.
193     // Caused by this, they also miscalculate their line length.
194     if(!strcmp(finfo.id, "8TRACKFB") && vinfo.bits_per_pixel == 1) {
195         vinfo.bits_per_pixel = 8;
196         finfo.line_length = vinfo.xres;
197     }
198 }
199 
200 /*!
201     \internal
202 
203     \class QLinuxFbScreen
204     \ingroup qws
205 
206     \brief The QLinuxFbScreen class implements a screen driver for the
207     Linux framebuffer.
208 
209     Note that this class is only available in \l{Qt for Embedded Linux}.
210     Custom screen drivers can be added by subclassing the
211     QScreenDriverPlugin class, using the QScreenDriverFactory class to
212     dynamically load the driver into the application, but there should
213     only be one screen object per application.
214 
215     The QLinuxFbScreen class provides the cache() function allocating
216     off-screen graphics memory, and the complementary uncache()
217     function releasing the allocated memory. The latter function will
218     first sync the graphics card to ensure the memory isn't still
219     being used by a command in the graphics card FIFO queue. The
220     deleteEntry() function deletes the given memory block without such
221     synchronization.  Given the screen instance and client id, the
222     memory can also be released using the clearCache() function, but
223     this should only be necessary if a client exits abnormally.
224 
225     In addition, when in paletted graphics modes, the set() function
226     provides the possibility of setting a specified color index to a
227     given RGB value.
228 
229     The QLinuxFbScreen class also acts as a factory for the
230     unaccelerated screen cursor and the unaccelerated raster-based
231     implementation of QPaintEngine (\c QRasterPaintEngine);
232     accelerated drivers for Linux should derive from this class.
233 
234     \sa QScreen, QScreenDriverPlugin, {Running Applications}
235 */
236 
237 /*!
238     \fn bool QLinuxFbScreen::useOffscreen()
239     \internal
240 */
241 
242 // Unaccelerated screen/driver setup. Can be overridden by accelerated
243 // drivers
244 
245 /*!
246     \fn QLinuxFbScreen::QLinuxFbScreen(int displayId)
247 
248     Constructs a QLinuxFbScreen object. The \a displayId argument
249     identifies the Qt for Embedded Linux server to connect to.
250 */
251 
QLinuxFbScreen(int display_id)252 QLinuxFbScreen::QLinuxFbScreen(int display_id)
253     : QScreen(display_id, LinuxFBClass), d_ptr(new QLinuxFbScreenPrivate)
254 {
255     canaccel=false;
256     clearCacheFunc = &clearCache;
257 #ifdef QT_QWS_CLIENTBLIT
258     setSupportsBlitInClients(true);
259 #endif
260 }
261 
262 /*!
263     Destroys this QLinuxFbScreen object.
264 */
265 
~QLinuxFbScreen()266 QLinuxFbScreen::~QLinuxFbScreen()
267 {
268 #ifdef QT_NO_QWS_SIGNALHANDLER
269     delete d_ptr;
270 #endif
271 }
272 
273 /*!
274     \reimp
275 
276     This is called by \l{Qt for Embedded Linux} clients to map in the framebuffer.
277     It should be reimplemented by accelerated drivers to map in
278     graphics card registers; those drivers should then call this
279     function in order to set up offscreen memory management. The
280     device is specified in \a displaySpec; e.g. "/dev/fb".
281 
282     \sa disconnect()
283 */
284 
connect(const QString & displaySpec)285 bool QLinuxFbScreen::connect(const QString &displaySpec)
286 {
287     d_ptr->displaySpec = displaySpec;
288 
289     const QStringList args = displaySpec.split(QLatin1Char(':'));
290 
291     if (args.contains(QLatin1String("nographicsmodeswitch")))
292         d_ptr->doGraphicsMode = false;
293 
294 #ifdef QT_QWS_DEPTH_GENERIC
295     if (args.contains(QLatin1String("genericcolors")))
296         d_ptr->doGenericColors = true;
297 #endif
298 
299     QRegExp ttyRegExp(QLatin1String("tty=(.*)"));
300     if (args.indexOf(ttyRegExp) != -1)
301         d_ptr->ttyDevice = ttyRegExp.cap(1);
302 
303 #if Q_BYTE_ORDER == Q_BIG_ENDIAN
304 #ifndef QT_QWS_FRAMEBUFFER_LITTLE_ENDIAN
305     if (args.contains(QLatin1String("littleendian")))
306 #endif
307         QScreen::setFrameBufferLittleEndian(true);
308 #endif
309 
310     QString dev = QLatin1String("/dev/fb0");
311     foreach(QString d, args) {
312 	if (d.startsWith(QLatin1Char('/'))) {
313 	    dev = d;
314 	    break;
315 	}
316     }
317 
318     if (access(dev.toLatin1().constData(), R_OK|W_OK) == 0)
319         d_ptr->fd = QT_OPEN(dev.toLatin1().constData(), O_RDWR);
320     if (d_ptr->fd == -1) {
321         if (QApplication::type() == QApplication::GuiServer) {
322             perror("QScreenLinuxFb::connect");
323             qCritical("Error opening framebuffer device %s", qPrintable(dev));
324             return false;
325         }
326         if (access(dev.toLatin1().constData(), R_OK) == 0)
327             d_ptr->fd = QT_OPEN(dev.toLatin1().constData(), O_RDONLY);
328     }
329 
330     ::fb_fix_screeninfo finfo;
331     ::fb_var_screeninfo vinfo;
332     //#######################
333     // Shut up Valgrind
334     memset(&vinfo, 0, sizeof(vinfo));
335     memset(&finfo, 0, sizeof(finfo));
336     //#######################
337 
338     /* Get fixed screen information */
339     if (d_ptr->fd != -1 && ioctl(d_ptr->fd, FBIOGET_FSCREENINFO, &finfo)) {
340         perror("QLinuxFbScreen::connect");
341         qWarning("Error reading fixed information");
342         return false;
343     }
344 
345     d_ptr->driverType = strcmp(finfo.id, "8TRACKFB") ? GenericDriver : EInk8Track;
346 
347     if (finfo.type == FB_TYPE_VGA_PLANES) {
348         qWarning("VGA16 video mode not supported");
349         return false;
350     }
351 
352     /* Get variable screen information */
353     if (d_ptr->fd != -1 && ioctl(d_ptr->fd, FBIOGET_VSCREENINFO, &vinfo)) {
354         perror("QLinuxFbScreen::connect");
355         qWarning("Error reading variable information");
356         return false;
357     }
358 
359     fixupScreenInfo(finfo, vinfo);
360 
361     grayscale = vinfo.grayscale;
362     d = vinfo.bits_per_pixel;
363     if (d == 24) {
364         d = vinfo.red.length + vinfo.green.length + vinfo.blue.length;
365         if (d <= 0)
366             d = 24; // reset if color component lengths are not reported
367     } else if (d == 16) {
368         d = vinfo.red.length + vinfo.green.length + vinfo.blue.length;
369         if (d <= 0)
370             d = 16;
371     }
372     lstep = finfo.line_length;
373 
374     int xoff = vinfo.xoffset;
375     int yoff = vinfo.yoffset;
376     const char* qwssize;
377     if((qwssize=::getenv("QWS_SIZE")) && sscanf(qwssize,"%dx%d",&w,&h)==2) {
378         if (d_ptr->fd != -1) {
379             if ((uint)w > vinfo.xres) w = vinfo.xres;
380             if ((uint)h > vinfo.yres) h = vinfo.yres;
381         }
382         dw=w;
383         dh=h;
384         int xxoff, yyoff;
385         if (sscanf(qwssize, "%*dx%*d+%d+%d", &xxoff, &yyoff) == 2) {
386             if (xxoff < 0 || xxoff + w > (int)vinfo.xres)
387                 xxoff = vinfo.xres - w;
388             if (yyoff < 0 || yyoff + h > (int)vinfo.yres)
389                 yyoff = vinfo.yres - h;
390             xoff += xxoff;
391             yoff += yyoff;
392         } else {
393             xoff += (vinfo.xres - w)/2;
394             yoff += (vinfo.yres - h)/2;
395         }
396     } else {
397         dw=w=vinfo.xres;
398         dh=h=vinfo.yres;
399     }
400 
401     if (w == 0 || h == 0) {
402         qWarning("QScreenLinuxFb::connect(): Unable to find screen geometry, "
403                  "will use 320x240.");
404         dw = w = 320;
405         dh = h = 240;
406     }
407 
408     setPixelFormat(vinfo);
409 
410     // Handle display physical size spec.
411     QStringList displayArgs = displaySpec.split(QLatin1Char(':'));
412     QRegExp mmWidthRx(QLatin1String("mmWidth=?(\\d+)"));
413     int dimIdxW = displayArgs.indexOf(mmWidthRx);
414     QRegExp mmHeightRx(QLatin1String("mmHeight=?(\\d+)"));
415     int dimIdxH = displayArgs.indexOf(mmHeightRx);
416     if (dimIdxW >= 0) {
417         mmWidthRx.exactMatch(displayArgs.at(dimIdxW));
418         physWidth = mmWidthRx.cap(1).toInt();
419         if (dimIdxH < 0)
420             physHeight = dh*physWidth/dw;
421     }
422     if (dimIdxH >= 0) {
423         mmHeightRx.exactMatch(displayArgs.at(dimIdxH));
424         physHeight = mmHeightRx.cap(1).toInt();
425         if (dimIdxW < 0)
426             physWidth = dw*physHeight/dh;
427     }
428     if (dimIdxW < 0 && dimIdxH < 0) {
429         if (vinfo.width != 0 && vinfo.height != 0
430             && vinfo.width != UINT_MAX && vinfo.height != UINT_MAX) {
431             physWidth = vinfo.width;
432             physHeight = vinfo.height;
433         } else {
434             const int dpi = 72;
435             physWidth = qRound(dw * 25.4 / dpi);
436             physHeight = qRound(dh * 25.4 / dpi);
437         }
438     }
439 
440     dataoffset = yoff * lstep + xoff * d / 8;
441     //qDebug("Using %dx%dx%d screen",w,h,d);
442 
443     /* Figure out the size of the screen in bytes */
444     size = h * lstep;
445 
446     mapsize = finfo.smem_len;
447 
448     data = (unsigned char *)-1;
449     if (d_ptr->fd != -1)
450         data = (unsigned char *)mmap(0, mapsize, PROT_READ | PROT_WRITE,
451                                      MAP_SHARED, d_ptr->fd, 0);
452 
453     if ((long)data == -1) {
454         if (QApplication::type() == QApplication::GuiServer) {
455             perror("QLinuxFbScreen::connect");
456             qWarning("Error: failed to map framebuffer device to memory.");
457             return false;
458         }
459         data = 0;
460     } else {
461         data += dataoffset;
462     }
463 
464     canaccel = useOffscreen();
465     if(canaccel)
466         setupOffScreen();
467 
468     // Now read in palette
469     if((vinfo.bits_per_pixel==8) || (vinfo.bits_per_pixel==4)) {
470         screencols= (vinfo.bits_per_pixel==8) ? 256 : 16;
471         int loopc;
472         ::fb_cmap startcmap;
473         startcmap.start=0;
474         startcmap.len=screencols;
475         startcmap.red=(unsigned short int *)
476                  malloc(sizeof(unsigned short int)*screencols);
477         startcmap.green=(unsigned short int *)
478                    malloc(sizeof(unsigned short int)*screencols);
479         startcmap.blue=(unsigned short int *)
480                   malloc(sizeof(unsigned short int)*screencols);
481         startcmap.transp=(unsigned short int *)
482                     malloc(sizeof(unsigned short int)*screencols);
483         if (d_ptr->fd == -1 || ioctl(d_ptr->fd, FBIOGETCMAP, &startcmap)) {
484             perror("QLinuxFbScreen::connect");
485             qWarning("Error reading palette from framebuffer, using default palette");
486             createPalette(startcmap, vinfo, finfo);
487         }
488         int bits_used = 0;
489         for(loopc=0;loopc<screencols;loopc++) {
490             screenclut[loopc]=qRgb(startcmap.red[loopc] >> 8,
491                                    startcmap.green[loopc] >> 8,
492                                    startcmap.blue[loopc] >> 8);
493             bits_used |= startcmap.red[loopc]
494                          | startcmap.green[loopc]
495                          | startcmap.blue[loopc];
496         }
497         // WORKAROUND: Some framebuffer drivers only return 8 bit
498         // color values, so we need to not bit shift them..
499         if ((bits_used & 0x00ff) && !(bits_used & 0xff00)) {
500             for(loopc=0;loopc<screencols;loopc++) {
501                 screenclut[loopc] = qRgb(startcmap.red[loopc],
502                                          startcmap.green[loopc],
503                                          startcmap.blue[loopc]);
504             }
505             qWarning("8 bits cmap returned due to faulty FB driver, colors corrected");
506         }
507         free(startcmap.red);
508         free(startcmap.green);
509         free(startcmap.blue);
510         free(startcmap.transp);
511     } else {
512         screencols=0;
513     }
514 
515     return true;
516 }
517 
518 /*!
519     \reimp
520 
521     This unmaps the framebuffer.
522 
523     \sa connect()
524 */
525 
disconnect()526 void QLinuxFbScreen::disconnect()
527 {
528     data -= dataoffset;
529     if (data)
530         munmap((char*)data,mapsize);
531     close(d_ptr->fd);
532 }
533 
534 // #define DEBUG_VINFO
535 
createPalette(fb_cmap & cmap,fb_var_screeninfo & vinfo,fb_fix_screeninfo & finfo)536 void QLinuxFbScreen::createPalette(fb_cmap &cmap, fb_var_screeninfo &vinfo, fb_fix_screeninfo &finfo)
537 {
538     if((vinfo.bits_per_pixel==8) || (vinfo.bits_per_pixel==4)) {
539         screencols= (vinfo.bits_per_pixel==8) ? 256 : 16;
540         cmap.start=0;
541         cmap.len=screencols;
542         cmap.red=(unsigned short int *)
543                  malloc(sizeof(unsigned short int)*screencols);
544         cmap.green=(unsigned short int *)
545                    malloc(sizeof(unsigned short int)*screencols);
546         cmap.blue=(unsigned short int *)
547                   malloc(sizeof(unsigned short int)*screencols);
548         cmap.transp=(unsigned short int *)
549                     malloc(sizeof(unsigned short int)*screencols);
550 
551         if (screencols==16) {
552             if (finfo.type == FB_TYPE_PACKED_PIXELS) {
553                 // We'll setup a grayscale cmap for 4bpp linear
554                 int val = 0;
555                 for (int idx = 0; idx < 16; ++idx, val += 17) {
556                     cmap.red[idx] = (val<<8)|val;
557                     cmap.green[idx] = (val<<8)|val;
558                     cmap.blue[idx] = (val<<8)|val;
559                     screenclut[idx]=qRgb(val, val, val);
560                 }
561             } else {
562                 // Default 16 colour palette
563                 // Green is now trolltech green so certain images look nicer
564                 //                             black  d_gray l_gray white  red  green  blue cyan magenta yellow
565                 unsigned char reds[16]   = { 0x00, 0x7F, 0xBF, 0xFF, 0xFF, 0xA2, 0x00, 0xFF, 0xFF, 0x00, 0x7F, 0x7F, 0x00, 0x00, 0x00, 0x82 };
566                 unsigned char greens[16] = { 0x00, 0x7F, 0xBF, 0xFF, 0x00, 0xC5, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x7F, 0x7F, 0x7F };
567                 unsigned char blues[16]  = { 0x00, 0x7F, 0xBF, 0xFF, 0x00, 0x11, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x7F, 0x7F, 0x7F, 0x00, 0x00 };
568 
569                 for (int idx = 0; idx < 16; ++idx) {
570                     cmap.red[idx] = ((reds[idx]) << 8)|reds[idx];
571                     cmap.green[idx] = ((greens[idx]) << 8)|greens[idx];
572                     cmap.blue[idx] = ((blues[idx]) << 8)|blues[idx];
573                     cmap.transp[idx] = 0;
574                     screenclut[idx]=qRgb(reds[idx], greens[idx], blues[idx]);
575                 }
576             }
577         } else {
578             if (grayscale) {
579                 // Build grayscale palette
580                 int i;
581                 for(i=0;i<screencols;++i) {
582                     int bval = screencols == 256 ? i : (i << 4);
583                     ushort val = (bval << 8) | bval;
584                     cmap.red[i] = val;
585                     cmap.green[i] = val;
586                     cmap.blue[i] = val;
587                     cmap.transp[i] = 0;
588                     screenclut[i] = qRgb(bval,bval,bval);
589                 }
590             } else {
591                 // 6x6x6 216 color cube
592                 int idx = 0;
593                 for(int ir = 0x0; ir <= 0xff; ir+=0x33) {
594                     for(int ig = 0x0; ig <= 0xff; ig+=0x33) {
595                         for(int ib = 0x0; ib <= 0xff; ib+=0x33) {
596                             cmap.red[idx] = (ir << 8)|ir;
597                             cmap.green[idx] = (ig << 8)|ig;
598                             cmap.blue[idx] = (ib << 8)|ib;
599                             cmap.transp[idx] = 0;
600                             screenclut[idx]=qRgb(ir, ig, ib);
601                             ++idx;
602                         }
603                     }
604                 }
605                 // Fill in rest with 0
606                 for (int loopc=0; loopc<40; ++loopc) {
607                     screenclut[idx]=0;
608                     ++idx;
609                 }
610                 screencols=idx;
611             }
612         }
613     } else if(finfo.visual==FB_VISUAL_DIRECTCOLOR) {
614         cmap.start=0;
615         int rbits=0,gbits=0,bbits=0;
616         switch (vinfo.bits_per_pixel) {
617         case 8:
618             rbits=vinfo.red.length;
619             gbits=vinfo.green.length;
620             bbits=vinfo.blue.length;
621             if(rbits==0 && gbits==0 && bbits==0) {
622                 // cyber2000 driver bug hack
623                 rbits=3;
624                 gbits=3;
625                 bbits=2;
626             }
627             break;
628         case 15:
629             rbits=5;
630             gbits=5;
631             bbits=5;
632             break;
633         case 16:
634             rbits=5;
635             gbits=6;
636             bbits=5;
637             break;
638         case 18:
639         case 19:
640             rbits=6;
641             gbits=6;
642             bbits=6;
643             break;
644         case 24: case 32:
645             rbits=gbits=bbits=8;
646             break;
647         }
648         screencols=cmap.len=1<<qMax(rbits,qMax(gbits,bbits));
649         cmap.red=(unsigned short int *)
650                  malloc(sizeof(unsigned short int)*256);
651         cmap.green=(unsigned short int *)
652                    malloc(sizeof(unsigned short int)*256);
653         cmap.blue=(unsigned short int *)
654                   malloc(sizeof(unsigned short int)*256);
655         cmap.transp=(unsigned short int *)
656                     malloc(sizeof(unsigned short int)*256);
657         for(unsigned int i = 0x0; i < cmap.len; i++) {
658             cmap.red[i] = i*65535/((1<<rbits)-1);
659             cmap.green[i] = i*65535/((1<<gbits)-1);
660             cmap.blue[i] = i*65535/((1<<bbits)-1);
661             cmap.transp[i] = 0;
662         }
663     }
664 }
665 
666 /*!
667     \reimp
668 
669     This is called by the \l{Qt for Embedded Linux} server at startup time.
670     It turns off console blinking, sets up the color palette, enables write
671     combining on the framebuffer and initialises the off-screen memory
672     manager.
673 */
674 
initDevice()675 bool QLinuxFbScreen::initDevice()
676 {
677     d_ptr->openTty();
678 
679     // Grab current mode so we can reset it
680     fb_var_screeninfo vinfo;
681     fb_fix_screeninfo finfo;
682     //#######################
683     // Shut up Valgrind
684     memset(&vinfo, 0, sizeof(vinfo));
685     memset(&finfo, 0, sizeof(finfo));
686     //#######################
687 
688     if (ioctl(d_ptr->fd, FBIOGET_VSCREENINFO, &vinfo)) {
689         perror("QLinuxFbScreen::initDevice");
690         qFatal("Error reading variable information in card init");
691         return false;
692     }
693 
694 #ifdef DEBUG_VINFO
695     qDebug("Greyscale %d",vinfo.grayscale);
696     qDebug("Nonstd %d",vinfo.nonstd);
697     qDebug("Red %d %d %d",vinfo.red.offset,vinfo.red.length,
698            vinfo.red.msb_right);
699     qDebug("Green %d %d %d",vinfo.green.offset,vinfo.green.length,
700            vinfo.green.msb_right);
701     qDebug("Blue %d %d %d",vinfo.blue.offset,vinfo.blue.length,
702            vinfo.blue.msb_right);
703     qDebug("Transparent %d %d %d",vinfo.transp.offset,vinfo.transp.length,
704            vinfo.transp.msb_right);
705 #endif
706 
707     if (ioctl(d_ptr->fd, FBIOGET_FSCREENINFO, &finfo)) {
708         perror("QLinuxFbScreen::initDevice");
709         qCritical("Error reading fixed information in card init");
710         // It's not an /error/ as such, though definitely a bad sign
711         // so we return true
712         return true;
713     }
714 
715     fixupScreenInfo(finfo, vinfo);
716 
717     d_ptr->startupw=vinfo.xres;
718     d_ptr->startuph=vinfo.yres;
719     d_ptr->startupd=vinfo.bits_per_pixel;
720     grayscale = vinfo.grayscale;
721 
722 #ifdef __i386__
723     // Now init mtrr
724     if(!::getenv("QWS_NOMTRR")) {
725         int mfd=QT_OPEN("/proc/mtrr",O_WRONLY,0);
726         // MTRR entry goes away when file is closed - i.e.
727         // hopefully when QWS is killed
728         if(mfd != -1) {
729             mtrr_sentry sentry;
730             sentry.base=(unsigned long int)finfo.smem_start;
731             //qDebug("Physical framebuffer address %p",(void*)finfo.smem_start);
732             // Size needs to be in 4k chunks, but that's not always
733             // what we get thanks to graphics card registers. Write combining
734             // these is Not Good, so we write combine what we can
735             // (which is not much - 4 megs on an 8 meg card, it seems)
736             unsigned int size=finfo.smem_len;
737             size=size >> 22;
738             size=size << 22;
739             sentry.size=size;
740             sentry.type=MTRR_TYPE_WRCOMB;
741             if(ioctl(mfd,MTRRIOC_ADD_ENTRY,&sentry)==-1) {
742                 //printf("Couldn't add mtrr entry for %lx %lx, %s\n",
743                 //sentry.base,sentry.size,strerror(errno));
744             }
745         }
746 
747         // Should we close mfd here?
748         //QT_CLOSE(mfd);
749     }
750 #endif
751     if ((vinfo.bits_per_pixel==8) || (vinfo.bits_per_pixel==4) || (finfo.visual==FB_VISUAL_DIRECTCOLOR))
752     {
753         fb_cmap cmap;
754         createPalette(cmap, vinfo, finfo);
755         if (ioctl(d_ptr->fd, FBIOPUTCMAP, &cmap)) {
756             perror("QLinuxFbScreen::initDevice");
757             qWarning("Error writing palette to framebuffer");
758         }
759         free(cmap.red);
760         free(cmap.green);
761         free(cmap.blue);
762         free(cmap.transp);
763     }
764 
765     if (canaccel) {
766         *entryp=0;
767         *lowest = mapsize;
768         insert_entry(*entryp, *lowest, *lowest);  // dummy entry to mark start
769     }
770 
771     shared->fifocount = 0;
772     shared->buffer_offset = 0xffffffff;  // 0 would be a sensible offset (screen)
773     shared->linestep = 0;
774     shared->cliptop = 0xffffffff;
775     shared->clipleft = 0xffffffff;
776     shared->clipright = 0xffffffff;
777     shared->clipbottom = 0xffffffff;
778     shared->rop = 0xffffffff;
779 
780 #ifdef QT_QWS_DEPTH_GENERIC
781     if (pixelFormat() == QImage::Format_Invalid && screencols == 0
782         && d_ptr->doGenericColors)
783     {
784         qt_set_generic_blit(this, vinfo.bits_per_pixel,
785                             vinfo.red.length, vinfo.green.length,
786                             vinfo.blue.length, vinfo.transp.length,
787                             vinfo.red.offset, vinfo.green.offset,
788                             vinfo.blue.offset, vinfo.transp.offset);
789     }
790 #endif
791 
792 #ifndef QT_NO_QWS_CURSOR
793     QScreenCursor::initSoftwareCursor();
794 #endif
795     blank(false);
796 
797     return true;
798 }
799 
800 /*
801   The offscreen memory manager's list of entries is stored at the bottom
802   of the offscreen memory area and consistes of a series of QPoolEntry's,
803   each of which keep track of a block of allocated memory. Unallocated memory
804   is implicitly indicated by the gap between blocks indicated by QPoolEntry's.
805   The memory manager looks through any unallocated memory before the end
806   of currently-allocated memory to see if a new block will fit in the gap;
807   if it doesn't it allocated it from the end of currently-allocated memory.
808   Memory is allocated from the top of the framebuffer downwards; if it hits
809   the list of entries then offscreen memory is full and further allocations
810   are made from main RAM (and hence unaccelerated). Allocated memory can
811   be seen as a sort of upside-down stack; lowest keeps track of the
812   bottom of the stack.
813 */
814 
delete_entry(int pos)815 void QLinuxFbScreen::delete_entry(int pos)
816 {
817     if (pos > *entryp || pos < 0) {
818         qWarning("Attempt to delete odd pos! %d %d", pos, *entryp);
819         return;
820     }
821 
822 #ifdef DEBUG_CACHE
823     qDebug("Remove entry: %d", pos);
824 #endif
825 
826     QPoolEntry *qpe = &entries[pos];
827     if (qpe->start <= *lowest) {
828         // Lowest goes up again
829         *lowest = entries[pos-1].start;
830 #ifdef DEBUG_CACHE
831         qDebug("   moved lowest to %d", *lowest);
832 #endif
833     }
834 
835     (*entryp)--;
836     if (pos == *entryp)
837         return;
838 
839     int size = (*entryp)-pos;
840     memmove(&entries[pos], &entries[pos+1], size*sizeof(QPoolEntry));
841 }
842 
insert_entry(int pos,int start,int end)843 void QLinuxFbScreen::insert_entry(int pos, int start, int end)
844 {
845     if (pos > *entryp) {
846         qWarning("Attempt to insert odd pos! %d %d",pos,*entryp);
847         return;
848     }
849 
850 #ifdef DEBUG_CACHE
851     qDebug("Insert entry: %d, %d -> %d", pos, start, end);
852 #endif
853 
854     if (start < (int)*lowest) {
855         *lowest = start;
856 #ifdef DEBUG_CACHE
857         qDebug("    moved lowest to %d", *lowest);
858 #endif
859     }
860 
861     if (pos == *entryp) {
862         entries[pos].start = start;
863         entries[pos].end = end;
864         entries[pos].clientId = qws_client_id;
865         (*entryp)++;
866         return;
867     }
868 
869     int size=(*entryp)-pos;
870     memmove(&entries[pos+1],&entries[pos],size*sizeof(QPoolEntry));
871     entries[pos].start=start;
872     entries[pos].end=end;
873     entries[pos].clientId=qws_client_id;
874     (*entryp)++;
875 }
876 
877 /*!
878     \fn uchar * QLinuxFbScreen::cache(int amount)
879 
880     Requests the specified \a amount of offscreen graphics card memory
881     from the memory manager, and returns a pointer to the data within
882     the framebuffer (or 0 if there is no free memory).
883 
884     Note that the display is locked while memory is allocated in order to
885     preserve the memory pool's integrity.
886 
887     Use the QScreen::onCard() function to retrieve an offset (in
888     bytes) from the start of graphics card memory for the returned
889     pointer.
890 
891     \sa uncache(), clearCache(), deleteEntry()
892 */
893 
cache(int amount)894 uchar * QLinuxFbScreen::cache(int amount)
895 {
896     if (!canaccel || entryp == 0)
897         return 0;
898 
899     qt_fbdpy->grab();
900 
901     int startp = cacheStart + (*entryp+1) * sizeof(QPoolEntry);
902     if (startp >= (int)*lowest) {
903         // We don't have room for another cache QPoolEntry.
904 #ifdef DEBUG_CACHE
905         qDebug("No room for pool entry in VRAM");
906 #endif
907         qt_fbdpy->ungrab();
908         return 0;
909     }
910 
911     int align = pixmapOffsetAlignment();
912 
913     if (*entryp > 1) {
914         // Try to find a gap in the allocated blocks.
915         for (int loopc = 0; loopc < *entryp-1; loopc++) {
916             int freestart = entries[loopc+1].end;
917             int freeend = entries[loopc].start;
918             if (freestart != freeend) {
919                 while (freestart % align) {
920                     freestart++;
921                 }
922                 int len=freeend-freestart;
923                 if (len >= amount) {
924                     insert_entry(loopc+1, freestart, freestart+amount);
925                     qt_fbdpy->ungrab();
926                     return data+freestart;
927                 }
928             }
929         }
930     }
931 
932     // No free blocks in already-taken memory; get some more
933     // if we can
934     int newlowest = (*lowest)-amount;
935     if (newlowest % align) {
936         newlowest -= align;
937         while (newlowest % align) {
938             newlowest++;
939         }
940     }
941     if (startp >= newlowest) {
942         qt_fbdpy->ungrab();
943 #ifdef DEBUG_CACHE
944         qDebug("No VRAM available for %d bytes", amount);
945 #endif
946         return 0;
947     }
948     insert_entry(*entryp, newlowest, *lowest);
949     qt_fbdpy->ungrab();
950 
951     return data + newlowest;
952 }
953 
954 /*!
955     \fn void QLinuxFbScreen::uncache(uchar * memoryBlock)
956 
957     Deletes the specified \a memoryBlock allocated from the graphics
958     card memory.
959 
960     Note that the display is locked while memory is unallocated in
961     order to preserve the memory pool's integrity.
962 
963     This function will first sync the graphics card to ensure the
964     memory isn't still being used by a command in the graphics card
965     FIFO queue. It is possible to speed up a driver by overriding this
966     function to avoid syncing. For example, the driver might delay
967     deleting the memory until it detects that all commands dealing
968     with the memory are no longer in the queue. Note that it will then
969     be up to the driver to ensure that the specified \a memoryBlock no
970     longer is being used.
971 
972     \sa cache(), deleteEntry(), clearCache()
973  */
uncache(uchar * c)974 void QLinuxFbScreen::uncache(uchar * c)
975 {
976     // need to sync graphics card
977 
978     deleteEntry(c);
979 }
980 
981 /*!
982     \fn void QLinuxFbScreen::deleteEntry(uchar * memoryBlock)
983 
984     Deletes the specified \a memoryBlock allocated from the graphics
985     card memory.
986 
987     \sa uncache(), cache(), clearCache()
988 */
deleteEntry(uchar * c)989 void QLinuxFbScreen::deleteEntry(uchar * c)
990 {
991     qt_fbdpy->grab();
992     unsigned long pos=(unsigned long)c;
993     pos-=((unsigned long)data);
994     unsigned int hold=(*entryp);
995     for(unsigned int loopc=1;loopc<hold;loopc++) {
996         if (entries[loopc].start==pos) {
997             if (entries[loopc].clientId == qws_client_id)
998                 delete_entry(loopc);
999             else
1000                 qWarning("Attempt to delete client id %d cache entry",
1001                          entries[loopc].clientId);
1002             qt_fbdpy->ungrab();
1003             return;
1004         }
1005     }
1006     qt_fbdpy->ungrab();
1007     qWarning("Attempt to delete unknown offset %ld",pos);
1008 }
1009 
1010 /*!
1011     Removes all entries from the cache for the specified screen \a
1012     instance and client identified by the given \a clientId.
1013 
1014     Calling this function should only be necessary if a client exits
1015     abnormally.
1016 
1017     \sa cache(), uncache(), deleteEntry()
1018 */
clearCache(QScreen * instance,int clientId)1019 void QLinuxFbScreen::clearCache(QScreen *instance, int clientId)
1020 {
1021     QLinuxFbScreen *screen = (QLinuxFbScreen *)instance;
1022     if (!screen->canaccel || !screen->entryp)
1023         return;
1024     qt_fbdpy->grab();
1025     for (int loopc = 0; loopc < *(screen->entryp); loopc++) {
1026         if (screen->entries[loopc].clientId == clientId) {
1027             screen->delete_entry(loopc);
1028             loopc--;
1029         }
1030     }
1031     qt_fbdpy->ungrab();
1032 }
1033 
1034 
setupOffScreen()1035 void QLinuxFbScreen::setupOffScreen()
1036 {
1037     // Figure out position of offscreen memory
1038     // Set up pool entries pointer table and 64-bit align it
1039     int psize = size;
1040 
1041     // hw: this causes the limitation of cursors to 64x64
1042     // the cursor should rather use the normal pixmap mechanism
1043     psize += 4096;  // cursor data
1044     psize += 8;     // for alignment
1045     psize &= ~0x7;  // align
1046 
1047     unsigned long pos = (unsigned long)data;
1048     pos += psize;
1049     entryp = ((int *)pos);
1050     lowest = ((unsigned int *)pos)+1;
1051     pos += (sizeof(int))*4;
1052     entries = (QPoolEntry *)pos;
1053 
1054     // beginning of offscreen memory available for pixmaps.
1055     cacheStart = psize + 4*sizeof(int) + sizeof(QPoolEntry);
1056 }
1057 
1058 /*!
1059     \reimp
1060 
1061     This is called by the \l{Qt for Embedded Linux} server when it shuts
1062     down, and should be inherited if you need to do any card-specific cleanup.
1063     The default version hides the screen cursor and reenables the blinking
1064     cursor and screen blanking.
1065 */
1066 
shutdownDevice()1067 void QLinuxFbScreen::shutdownDevice()
1068 {
1069     // Causing crashes. Not needed.
1070     //setMode(startupw,startuph,startupd);
1071 /*
1072     if (startupd == 8) {
1073         ioctl(fd,FBIOPUTCMAP,startcmap);
1074         free(startcmap->red);
1075         free(startcmap->green);
1076         free(startcmap->blue);
1077         free(startcmap->transp);
1078         delete startcmap;
1079         startcmap = 0;
1080     }
1081 */
1082     d_ptr->closeTty();
1083 }
1084 
1085 /*!
1086     \fn void QLinuxFbScreen::set(unsigned int index,unsigned int red,unsigned int green,unsigned int blue)
1087 
1088     Sets the specified color \a index to the specified RGB value, (\a
1089     red, \a green, \a blue), when in paletted graphics modes.
1090 */
1091 
set(unsigned int i,unsigned int r,unsigned int g,unsigned int b)1092 void QLinuxFbScreen::set(unsigned int i,unsigned int r,unsigned int g,unsigned int b)
1093 {
1094     if (d_ptr->fd != -1) {
1095         fb_cmap cmap;
1096         cmap.start=i;
1097         cmap.len=1;
1098         cmap.red=(unsigned short int *)
1099                  malloc(sizeof(unsigned short int)*256);
1100         cmap.green=(unsigned short int *)
1101                    malloc(sizeof(unsigned short int)*256);
1102         cmap.blue=(unsigned short int *)
1103                   malloc(sizeof(unsigned short int)*256);
1104         cmap.transp=(unsigned short int *)
1105                     malloc(sizeof(unsigned short int)*256);
1106         cmap.red[0]=r << 8;
1107         cmap.green[0]=g << 8;
1108         cmap.blue[0]=b << 8;
1109         cmap.transp[0]=0;
1110         ioctl(d_ptr->fd, FBIOPUTCMAP, &cmap);
1111         free(cmap.red);
1112         free(cmap.green);
1113         free(cmap.blue);
1114         free(cmap.transp);
1115     }
1116     screenclut[i] = qRgb(r, g, b);
1117 }
1118 
1119 /*!
1120     \reimp
1121 
1122     Sets the framebuffer to a new resolution and bit depth. The width is
1123     in \a nw, the height is in \a nh, and the depth is in \a nd. After
1124     doing this any currently-existing paint engines will be invalid and the
1125     screen should be completely redrawn. In a multiple-process
1126     Embedded Qt situation you must signal all other applications to
1127     call setMode() to the same mode and redraw.
1128 */
1129 
setMode(int nw,int nh,int nd)1130 void QLinuxFbScreen::setMode(int nw,int nh,int nd)
1131 {
1132     if (d_ptr->fd == -1)
1133         return;
1134 
1135     fb_fix_screeninfo finfo;
1136     fb_var_screeninfo vinfo;
1137     //#######################
1138     // Shut up Valgrind
1139     memset(&vinfo, 0, sizeof(vinfo));
1140     memset(&finfo, 0, sizeof(finfo));
1141     //#######################
1142 
1143     if (ioctl(d_ptr->fd, FBIOGET_VSCREENINFO, &vinfo)) {
1144         perror("QLinuxFbScreen::setMode");
1145         qFatal("Error reading variable information in mode change");
1146     }
1147 
1148     vinfo.xres=nw;
1149     vinfo.yres=nh;
1150     vinfo.bits_per_pixel=nd;
1151 
1152     if (ioctl(d_ptr->fd, FBIOPUT_VSCREENINFO, &vinfo)) {
1153         perror("QLinuxFbScreen::setMode");
1154         qCritical("Error writing variable information in mode change");
1155     }
1156 
1157     if (ioctl(d_ptr->fd, FBIOGET_VSCREENINFO, &vinfo)) {
1158         perror("QLinuxFbScreen::setMode");
1159         qFatal("Error reading changed variable information in mode change");
1160     }
1161 
1162     if (ioctl(d_ptr->fd, FBIOGET_FSCREENINFO, &finfo)) {
1163         perror("QLinuxFbScreen::setMode");
1164         qFatal("Error reading fixed information");
1165     }
1166 
1167     fixupScreenInfo(finfo, vinfo);
1168     disconnect();
1169     connect(d_ptr->displaySpec);
1170     exposeRegion(region(), 0);
1171 }
1172 
1173 // save the state of the graphics card
1174 // This is needed so that e.g. we can restore the palette when switching
1175 // between linux virtual consoles.
1176 
1177 /*!
1178     \reimp
1179 
1180     This doesn't do anything; accelerated drivers may wish to reimplement
1181     it to save graphics cards registers. It's called by the
1182     \l{Qt for Embedded Linux} server when the virtual console is switched.
1183 */
1184 
save()1185 void QLinuxFbScreen::save()
1186 {
1187     // nothing to do.
1188 }
1189 
1190 
1191 // restore the state of the graphics card.
1192 /*!
1193     \reimp
1194 
1195     This is called when the virtual console is switched back to
1196     \l{Qt for Embedded Linux} and restores the palette.
1197 */
restore()1198 void QLinuxFbScreen::restore()
1199 {
1200     if (d_ptr->fd == -1)
1201         return;
1202 
1203     if ((d == 8) || (d == 4)) {
1204         fb_cmap cmap;
1205         cmap.start=0;
1206         cmap.len=screencols;
1207         cmap.red=(unsigned short int *)
1208                  malloc(sizeof(unsigned short int)*256);
1209         cmap.green=(unsigned short int *)
1210                    malloc(sizeof(unsigned short int)*256);
1211         cmap.blue=(unsigned short int *)
1212                   malloc(sizeof(unsigned short int)*256);
1213         cmap.transp=(unsigned short int *)
1214                     malloc(sizeof(unsigned short int)*256);
1215         for (int loopc = 0; loopc < screencols; loopc++) {
1216             cmap.red[loopc] = qRed(screenclut[loopc]) << 8;
1217             cmap.green[loopc] = qGreen(screenclut[loopc]) << 8;
1218             cmap.blue[loopc] = qBlue(screenclut[loopc]) << 8;
1219             cmap.transp[loopc] = 0;
1220         }
1221         ioctl(d_ptr->fd, FBIOPUTCMAP, &cmap);
1222         free(cmap.red);
1223         free(cmap.green);
1224         free(cmap.blue);
1225         free(cmap.transp);
1226     }
1227 }
1228 
1229 /*!
1230     \fn int QLinuxFbScreen::sharedRamSize(void * end)
1231     \internal
1232 */
1233 
1234 // This works like the QScreenCursor code. end points to the end
1235 // of our shared structure, we return the amount of memory we reserved
sharedRamSize(void * end)1236 int QLinuxFbScreen::sharedRamSize(void * end)
1237 {
1238     shared=(QLinuxFb_Shared *)end;
1239     shared--;
1240     return sizeof(QLinuxFb_Shared);
1241 }
1242 
1243 /*!
1244     \reimp
1245 */
setDirty(const QRect & r)1246 void QLinuxFbScreen::setDirty(const QRect &r)
1247 {
1248     if(d_ptr->driverType == EInk8Track) {
1249         // e-Ink displays need a trigger to actually show what is
1250         // in their framebuffer memory. The 8-Track driver does this
1251         // by adding custom IOCTLs - FBIO_EINK_DISP_PIC (0x46a2) takes
1252         // an argument specifying whether or not to flash the screen
1253         // while updating.
1254         // There doesn't seem to be a way to tell it to just update
1255         // a subset of the screen.
1256         if(r.left() == 0 && r.top() == 0 && r.width() == dw && r.height() == dh)
1257             ioctl(d_ptr->fd, 0x46a2, 1);
1258         else
1259             ioctl(d_ptr->fd, 0x46a2, 0);
1260     }
1261 }
1262 
1263 /*!
1264     \reimp
1265 */
blank(bool on)1266 void QLinuxFbScreen::blank(bool on)
1267 {
1268     if (d_ptr->blank == on)
1269         return;
1270 
1271 #if defined(QT_QWS_IPAQ)
1272     if (on)
1273         system("apm -suspend");
1274 #else
1275     if (d_ptr->fd == -1)
1276         return;
1277 // Some old kernel versions don't have this.  These defines should go
1278 // away eventually
1279 #if defined(FBIOBLANK)
1280 #if defined(VESA_POWERDOWN) && defined(VESA_NO_BLANKING)
1281     ioctl(d_ptr->fd, FBIOBLANK, on ? VESA_POWERDOWN : VESA_NO_BLANKING);
1282 #else
1283     ioctl(d_ptr->fd, FBIOBLANK, on ? 1 : 0);
1284 #endif
1285 #endif
1286 #endif
1287 
1288     d_ptr->blank = on;
1289 }
1290 
setPixelFormat(struct fb_var_screeninfo info)1291 void QLinuxFbScreen::setPixelFormat(struct fb_var_screeninfo info)
1292 {
1293     const fb_bitfield rgba[4] = { info.red, info.green,
1294                                   info.blue, info.transp };
1295 
1296     QImage::Format format = QImage::Format_Invalid;
1297 
1298     switch (d) {
1299     case 32: {
1300         const fb_bitfield argb8888[4] = {{16, 8, 0}, {8, 8, 0},
1301                                          {0, 8, 0}, {24, 8, 0}};
1302         const fb_bitfield abgr8888[4] = {{0, 8, 0}, {8, 8, 0},
1303                                          {16, 8, 0}, {24, 8, 0}};
1304         if (memcmp(rgba, argb8888, 4 * sizeof(fb_bitfield)) == 0) {
1305             format = QImage::Format_ARGB32;
1306         } else if (memcmp(rgba, argb8888, 3 * sizeof(fb_bitfield)) == 0) {
1307             format = QImage::Format_RGB32;
1308         } else if (memcmp(rgba, abgr8888, 3 * sizeof(fb_bitfield)) == 0) {
1309             format = QImage::Format_RGB32;
1310             pixeltype = QScreen::BGRPixel;
1311         }
1312         break;
1313     }
1314     case 24: {
1315         const fb_bitfield rgb888[4] = {{16, 8, 0}, {8, 8, 0},
1316                                        {0, 8, 0}, {0, 0, 0}};
1317         const fb_bitfield bgr888[4] = {{0, 8, 0}, {8, 8, 0},
1318                                        {16, 8, 0}, {0, 0, 0}};
1319         if (memcmp(rgba, rgb888, 3 * sizeof(fb_bitfield)) == 0) {
1320             format = QImage::Format_RGB888;
1321         } else if (memcmp(rgba, bgr888, 3 * sizeof(fb_bitfield)) == 0) {
1322             format = QImage::Format_RGB888;
1323             pixeltype = QScreen::BGRPixel;
1324         }
1325         break;
1326     }
1327     case 18: {
1328         const fb_bitfield rgb666[4] = {{12, 6, 0}, {6, 6, 0},
1329                                        {0, 6, 0}, {0, 0, 0}};
1330         if (memcmp(rgba, rgb666, 3 * sizeof(fb_bitfield)) == 0)
1331             format = QImage::Format_RGB666;
1332         break;
1333     }
1334     case 16: {
1335         const fb_bitfield rgb565[4] = {{11, 5, 0}, {5, 6, 0},
1336                                        {0, 5, 0}, {0, 0, 0}};
1337         const fb_bitfield bgr565[4] = {{0, 5, 0}, {5, 6, 0},
1338                                        {11, 5, 0}, {0, 0, 0}};
1339         if (memcmp(rgba, rgb565, 3 * sizeof(fb_bitfield)) == 0) {
1340             format = QImage::Format_RGB16;
1341         } else if (memcmp(rgba, bgr565, 3 * sizeof(fb_bitfield)) == 0) {
1342             format = QImage::Format_RGB16;
1343             pixeltype = QScreen::BGRPixel;
1344         }
1345         break;
1346     }
1347     case 15: {
1348         const fb_bitfield rgb1555[4] = {{10, 5, 0}, {5, 5, 0},
1349                                         {0, 5, 0}, {15, 1, 0}};
1350         const fb_bitfield bgr1555[4] = {{0, 5, 0}, {5, 5, 0},
1351                                         {10, 5, 0}, {15, 1, 0}};
1352         if (memcmp(rgba, rgb1555, 3 * sizeof(fb_bitfield)) == 0) {
1353             format = QImage::Format_RGB555;
1354         } else if (memcmp(rgba, bgr1555, 3 * sizeof(fb_bitfield)) == 0) {
1355             format = QImage::Format_RGB555;
1356             pixeltype = QScreen::BGRPixel;
1357         }
1358         break;
1359     }
1360     case 12: {
1361         const fb_bitfield rgb444[4] = {{8, 4, 0}, {4, 4, 0},
1362                                        {0, 4, 0}, {0, 0, 0}};
1363         if (memcmp(rgba, rgb444, 3 * sizeof(fb_bitfield)) == 0)
1364             format = QImage::Format_RGB444;
1365         break;
1366     }
1367     case 8:
1368         break;
1369     case 1:
1370         format = QImage::Format_Mono; //###: LSB???
1371         break;
1372     default:
1373         break;
1374     }
1375 
1376     QScreen::setPixelFormat(format);
1377 }
1378 
useOffscreen()1379 bool QLinuxFbScreen::useOffscreen()
1380 {
1381     // Not done for 8Track because on e-Ink displays,
1382     // everything is offscreen anyway
1383     if (d_ptr->driverType == EInk8Track || ((mapsize - size) < 16*1024))
1384         return false;
1385 
1386     return true;
1387 }
1388 
1389 QT_END_NAMESPACE
1390 
1391 #endif // QT_NO_QWS_LINUXFB
1392