source: trunk/src/gui/embedded/qscreenlinuxfb_qws.cpp@ 707

Last change on this file since 707 was 651, checked in by Dmitry A. Kuminov, 16 years ago

trunk: Merged in qt 4.6.2 sources.

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