Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qlinuxfbscreen.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qlinuxfbscreen.h"
5#include <QtFbSupport/private/qfbcursor_p.h>
6#include <QtFbSupport/private/qfbwindow_p.h>
7#include <QtCore/QFile>
8#include <QtCore/QRegularExpression>
9#include <QtGui/QPainter>
10
11#include <private/qcore_unix_p.h> // overrides QT_OPEN
12#include <qimage.h>
13#include <qdebug.h>
14
15#include <unistd.h>
16#include <stdlib.h>
17#include <sys/ioctl.h>
18#include <sys/types.h>
19#include <sys/stat.h>
20#include <sys/mman.h>
21#include <linux/kd.h>
22#include <fcntl.h>
23#include <errno.h>
24#include <stdio.h>
25#include <limits.h>
26#include <signal.h>
27
28#include <linux/fb.h>
29
31
32using namespace Qt::StringLiterals;
33
34static int openFramebufferDevice(const QString &dev)
35{
36 int fd = -1;
37
38 if (access(dev.toLatin1().constData(), R_OK|W_OK) == 0)
39 fd = QT_OPEN(dev.toLatin1().constData(), O_RDWR);
40
41 if (fd == -1) {
42 if (access(dev.toLatin1().constData(), R_OK) == 0)
43 fd = QT_OPEN(dev.toLatin1().constData(), O_RDONLY);
44 }
45
46 return fd;
47}
48
49static int determineDepth(const fb_var_screeninfo &vinfo)
50{
51 int depth = vinfo.bits_per_pixel;
52 if (depth== 24) {
53 depth = vinfo.red.length + vinfo.green.length + vinfo.blue.length;
54 if (depth <= 0)
55 depth = 24; // reset if color component lengths are not reported
56 } else if (depth == 16) {
57 depth = vinfo.red.length + vinfo.green.length + vinfo.blue.length;
58 if (depth <= 0)
59 depth = 16;
60 }
61 return depth;
62}
63
64static QRect determineGeometry(const fb_var_screeninfo &vinfo, const QRect &userGeometry)
65{
66 int xoff = vinfo.xoffset;
67 int yoff = vinfo.yoffset;
68 int w, h;
69 if (userGeometry.isValid()) {
70 w = userGeometry.width();
71 h = userGeometry.height();
72 if ((uint)w > vinfo.xres)
73 w = vinfo.xres;
74 if ((uint)h > vinfo.yres)
75 h = vinfo.yres;
76
77 int xxoff = userGeometry.x(), yyoff = userGeometry.y();
78 if (xxoff != 0 || yyoff != 0) {
79 if (xxoff < 0 || xxoff + w > (int)(vinfo.xres))
80 xxoff = vinfo.xres - w;
81 if (yyoff < 0 || yyoff + h > (int)(vinfo.yres))
82 yyoff = vinfo.yres - h;
83 xoff += xxoff;
84 yoff += yyoff;
85 } else {
86 xoff += (vinfo.xres - w)/2;
87 yoff += (vinfo.yres - h)/2;
88 }
89 } else {
90 w = vinfo.xres;
91 h = vinfo.yres;
92 }
93
94 if (w == 0 || h == 0) {
95 qWarning("Unable to find screen geometry, using 320x240");
96 w = 320;
97 h = 240;
98 }
99
100 return QRect(xoff, yoff, w, h);
101}
102
103static QSizeF determinePhysicalSize(const fb_var_screeninfo &vinfo, const QSize &mmSize, const QSize &res)
104{
105 int mmWidth = mmSize.width(), mmHeight = mmSize.height();
106
107 if (mmWidth <= 0 && mmHeight <= 0) {
108 if (vinfo.width != 0 && vinfo.height != 0
109 && vinfo.width != UINT_MAX && vinfo.height != UINT_MAX) {
110 mmWidth = vinfo.width;
111 mmHeight = vinfo.height;
112 } else {
113 const int dpi = 100;
114 mmWidth = qRound(res.width() * 25.4 / dpi);
115 mmHeight = qRound(res.height() * 25.4 / dpi);
116 }
117 } else if (mmWidth > 0 && mmHeight <= 0) {
118 mmHeight = res.height() * mmWidth/res.width();
119 } else if (mmHeight > 0 && mmWidth <= 0) {
120 mmWidth = res.width() * mmHeight/res.height();
121 }
122
123 return QSize(mmWidth, mmHeight);
124}
125
126static QImage::Format determineFormat(const fb_var_screeninfo &info, int depth)
127{
128 const fb_bitfield rgba[4] = { info.red, info.green,
129 info.blue, info.transp };
130
132
133 switch (depth) {
134 case 32: {
135 const fb_bitfield argb8888[4] = {{16, 8, 0}, {8, 8, 0},
136 {0, 8, 0}, {24, 8, 0}};
137 const fb_bitfield abgr8888[4] = {{0, 8, 0}, {8, 8, 0},
138 {16, 8, 0}, {24, 8, 0}};
139 if (memcmp(rgba, argb8888, 4 * sizeof(fb_bitfield)) == 0) {
141 } else if (memcmp(rgba, argb8888, 3 * sizeof(fb_bitfield)) == 0) {
143 } else if (memcmp(rgba, abgr8888, 3 * sizeof(fb_bitfield)) == 0) {
145 // pixeltype = BGRPixel;
146 }
147 break;
148 }
149 case 24: {
150 const fb_bitfield rgb888[4] = {{16, 8, 0}, {8, 8, 0},
151 {0, 8, 0}, {0, 0, 0}};
152 const fb_bitfield bgr888[4] = {{0, 8, 0}, {8, 8, 0},
153 {16, 8, 0}, {0, 0, 0}};
154 if (memcmp(rgba, rgb888, 3 * sizeof(fb_bitfield)) == 0) {
156 } else if (memcmp(rgba, bgr888, 3 * sizeof(fb_bitfield)) == 0) {
158 // pixeltype = BGRPixel;
159 }
160 break;
161 }
162 case 18: {
163 const fb_bitfield rgb666[4] = {{12, 6, 0}, {6, 6, 0},
164 {0, 6, 0}, {0, 0, 0}};
165 if (memcmp(rgba, rgb666, 3 * sizeof(fb_bitfield)) == 0)
167 break;
168 }
169 case 16: {
170 const fb_bitfield rgb565[4] = {{11, 5, 0}, {5, 6, 0},
171 {0, 5, 0}, {0, 0, 0}};
172 const fb_bitfield bgr565[4] = {{0, 5, 0}, {5, 6, 0},
173 {11, 5, 0}, {0, 0, 0}};
174 if (memcmp(rgba, rgb565, 3 * sizeof(fb_bitfield)) == 0) {
176 } else if (memcmp(rgba, bgr565, 3 * sizeof(fb_bitfield)) == 0) {
178 // pixeltype = BGRPixel;
179 }
180 break;
181 }
182 case 15: {
183 const fb_bitfield rgb1555[4] = {{10, 5, 0}, {5, 5, 0},
184 {0, 5, 0}, {15, 1, 0}};
185 const fb_bitfield bgr1555[4] = {{0, 5, 0}, {5, 5, 0},
186 {10, 5, 0}, {15, 1, 0}};
187 if (memcmp(rgba, rgb1555, 3 * sizeof(fb_bitfield)) == 0) {
189 } else if (memcmp(rgba, bgr1555, 3 * sizeof(fb_bitfield)) == 0) {
191 // pixeltype = BGRPixel;
192 }
193 break;
194 }
195 case 12: {
196 const fb_bitfield rgb444[4] = {{8, 4, 0}, {4, 4, 0},
197 {0, 4, 0}, {0, 0, 0}};
198 if (memcmp(rgba, rgb444, 3 * sizeof(fb_bitfield)) == 0)
200 break;
201 }
202 case 8:
203 break;
204 case 1:
205 format = QImage::Format_Mono; //###: LSB???
206 break;
207 default:
208 break;
209 }
210
211 return format;
212}
213
214static int openTtyDevice(const QString &device)
215{
216 const char *const devs[] = { "/dev/tty0", "/dev/tty", "/dev/console", nullptr };
217
218 int fd = -1;
219 if (device.isEmpty()) {
220 for (const char * const *dev = devs; *dev; ++dev) {
221 fd = QT_OPEN(*dev, O_RDWR);
222 if (fd != -1)
223 break;
224 }
225 } else {
226 fd = QT_OPEN(QFile::encodeName(device).constData(), O_RDWR);
227 }
228
229 return fd;
230}
231
232static void switchToGraphicsMode(int ttyfd, bool doSwitch, int *oldMode)
233{
234 // Do not warn if the switch fails: the ioctl fails when launching from a
235 // remote console and there is nothing we can do about it. The matching
236 // call in resetTty should at least fail then, too, so we do no harm.
237 if (ioctl(ttyfd, KDGETMODE, oldMode) == 0) {
238 if (doSwitch && *oldMode != KD_GRAPHICS)
239 ioctl(ttyfd, KDSETMODE, KD_GRAPHICS);
240 }
241}
242
243static void resetTty(int ttyfd, int oldMode)
244{
245 ioctl(ttyfd, KDSETMODE, oldMode);
246
247 QT_CLOSE(ttyfd);
248}
249
250static void blankScreen(int fd, bool on)
251{
252 ioctl(fd, FBIOBLANK, on ? VESA_POWERDOWN : VESA_NO_BLANKING);
253}
254
256 : mArgs(args), mFbFd(-1), mTtyFd(-1), mBlitter(nullptr)
257{
258 mMmap.data = nullptr;
259}
260
262{
263 if (mFbFd != -1) {
264 if (mMmap.data)
265 munmap(mMmap.data - mMmap.offset, mMmap.size);
266 close(mFbFd);
267 }
268
269 if (mTtyFd != -1)
270 resetTty(mTtyFd, mOldTtyMode);
271
272 delete mBlitter;
273}
274
276{
277 QRegularExpression ttyRx("tty=(.*)"_L1);
278 QRegularExpression fbRx("fb=(.*)"_L1);
279 QRegularExpression mmSizeRx("mmsize=(\\d+)x(\\d+)"_L1);
280 QRegularExpression sizeRx("size=(\\d+)x(\\d+)"_L1);
281 QRegularExpression offsetRx("offset=(\\d+)x(\\d+)"_L1);
282
283 QString fbDevice, ttyDevice;
284 QSize userMmSize;
285 QRect userGeometry;
286 bool doSwitchToGraphicsMode = true;
287
288 // Parse arguments
289 for (const QString &arg : std::as_const(mArgs)) {
291 if (arg == "nographicsmodeswitch"_L1)
292 doSwitchToGraphicsMode = false;
293 else if (arg.contains(mmSizeRx, &match))
294 userMmSize = QSize(match.captured(1).toInt(), match.captured(2).toInt());
295 else if (arg.contains(sizeRx, &match))
296 userGeometry.setSize(QSize(match.captured(1).toInt(), match.captured(2).toInt()));
297 else if (arg.contains(offsetRx, &match))
298 userGeometry.setTopLeft(QPoint(match.captured(1).toInt(), match.captured(2).toInt()));
299 else if (arg.contains(ttyRx, &match))
300 ttyDevice = match.captured(1);
301 else if (arg.contains(fbRx, &match))
302 fbDevice = match.captured(1);
303 }
304
305 if (fbDevice.isEmpty()) {
306 fbDevice = "/dev/fb0"_L1;
307 if (!QFile::exists(fbDevice))
308 fbDevice = "/dev/graphics/fb0"_L1;
309 if (!QFile::exists(fbDevice)) {
310 qWarning("Unable to figure out framebuffer device. Specify it manually.");
311 return false;
312 }
313 }
314
315 // Open the device
316 mFbFd = openFramebufferDevice(fbDevice);
317 if (mFbFd == -1) {
318 qErrnoWarning(errno, "Failed to open framebuffer %s", qPrintable(fbDevice));
319 return false;
320 }
321
322 // Read the fixed and variable screen information
323 fb_fix_screeninfo finfo;
324 fb_var_screeninfo vinfo;
325 memset(&vinfo, 0, sizeof(vinfo));
326 memset(&finfo, 0, sizeof(finfo));
327
328 if (ioctl(mFbFd, FBIOGET_FSCREENINFO, &finfo) != 0) {
329 qErrnoWarning(errno, "Error reading fixed information");
330 return false;
331 }
332
333 if (ioctl(mFbFd, FBIOGET_VSCREENINFO, &vinfo)) {
334 qErrnoWarning(errno, "Error reading variable information");
335 return false;
336 }
337
338 mDepth = determineDepth(vinfo);
339 mBytesPerLine = finfo.line_length;
340 QRect geometry = determineGeometry(vinfo, userGeometry);
341 mGeometry = QRect(QPoint(0, 0), geometry.size());
343 mPhysicalSize = determinePhysicalSize(vinfo, userMmSize, geometry.size());
344
345 // mmap the framebuffer
346 mMmap.size = finfo.smem_len;
347 uchar *data = (unsigned char *)mmap(nullptr, mMmap.size, PROT_READ | PROT_WRITE, MAP_SHARED, mFbFd, 0);
348 if ((long)data == -1) {
349 qErrnoWarning(errno, "Failed to mmap framebuffer");
350 return false;
351 }
352
353 mMmap.offset = geometry.y() * mBytesPerLine + geometry.x() * mDepth / 8;
354 mMmap.data = data + mMmap.offset;
355
357 mFbScreenImage = QImage(mMmap.data, geometry.width(), geometry.height(), mBytesPerLine, mFormat);
358
359 mCursor = new QFbCursor(this);
360
361 mTtyFd = openTtyDevice(ttyDevice);
362 if (mTtyFd == -1)
363 qErrnoWarning(errno, "Failed to open tty");
364
365 switchToGraphicsMode(mTtyFd, doSwitchToGraphicsMode, &mOldTtyMode);
366 blankScreen(mFbFd, false);
367
368 return true;
369}
370
372{
373 QRegion touched = QFbScreen::doRedraw();
374
375 if (touched.isEmpty())
376 return touched;
377
378 if (!mBlitter)
379 mBlitter = new QPainter(&mFbScreenImage);
380
382 for (const QRect &rect : touched)
383 mBlitter->drawImage(rect, mScreenImage, rect);
384
385 return touched;
386}
387
388// grabWindow() grabs "from the screen" not from the backingstores.
389// In linuxfb's case it will also include the mouse cursor.
390QPixmap QLinuxFbScreen::grabWindow(WId wid, int x, int y, int width, int height) const
391{
392 if (!wid) {
393 if (width < 0)
394 width = mFbScreenImage.width() - x;
395 if (height < 0)
396 height = mFbScreenImage.height() - y;
397 return QPixmap::fromImage(mFbScreenImage).copy(x, y, width, height);
398 }
399
401 if (window) {
402 const QRect geom = window->geometry();
403 if (width < 0)
404 width = geom.width() - x;
405 if (height < 0)
406 height = geom.height() - y;
407 QRect rect(geom.topLeft() + QPoint(x, y), QSize(width, height));
408 rect &= window->geometry();
409 return QPixmap::fromImage(mFbScreenImage).copy(rect);
410 }
411
412 return QPixmap();
413}
414
416
417#include "moc_qlinuxfbscreen.cpp"
418
IOBluetoothDevice * device
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
QImage mScreenImage
Definition qfbscreen_p.h:89
QFbWindow * windowForId(WId wid) const
QRect geometry() const override
Reimplement in subclass to return the pixel geometry of the screen.
Definition qfbscreen_p.h:45
void initializeCompositor()
Definition qfbscreen.cpp:32
virtual QRegion doRedraw()
QFbCursor * mCursor
Definition qfbscreen_p.h:84
QRect mGeometry
Definition qfbscreen_p.h:85
QImage::Format mFormat
Definition qfbscreen_p.h:87
QSizeF mPhysicalSize
Definition qfbscreen_p.h:88
static QByteArray encodeName(const QString &fileName)
Converts fileName to an 8-bit encoding that you can use in native APIs.
Definition qfile.h:158
bool exists() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qfile.cpp:351
\inmodule QtGui
Definition qimage.h:37
int width() const
Returns the width of the image.
int height() const
Returns the height of the image.
Format
The following image formats are available in Qt.
Definition qimage.h:41
@ Format_RGB888
Definition qimage.h:55
@ Format_RGB32
Definition qimage.h:46
@ Format_Invalid
Definition qimage.h:42
@ Format_RGB666
Definition qimage.h:51
@ Format_RGB444
Definition qimage.h:56
@ Format_RGB555
Definition qimage.h:53
@ Format_Mono
Definition qimage.h:43
@ Format_RGB16
Definition qimage.h:49
@ Format_BGR888
Definition qimage.h:71
@ Format_ARGB32
Definition qimage.h:47
QLinuxFbScreen(const QStringList &args)
QPixmap grabWindow(WId wid, int x, int y, int width, int height) const override
This function is called when Qt needs to be able to grab the content of a window.
bool initialize() override
QRegion doRedraw() override
The QPainter class performs low-level painting on widgets and other paint devices.
Definition qpainter.h:46
void setCompositionMode(CompositionMode mode)
Sets the composition mode to the given mode.
void drawImage(const QRectF &targetRect, const QImage &image, const QRectF &sourceRect, Qt::ImageConversionFlags flags=Qt::AutoColor)
Draws the rectangular portion source of the given image into the target rectangle in the paint device...
@ CompositionMode_Source
Definition qpainter.h:101
Returns a copy of the pixmap that is transformed using the given transformation transform and transfo...
Definition qpixmap.h:27
static QPixmap fromImage(const QImage &image, Qt::ImageConversionFlags flags=Qt::AutoColor)
Converts the given image to a pixmap using the specified flags to control the conversion.
Definition qpixmap.cpp:1437
\inmodule QtCore\reentrant
Definition qpoint.h:25
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr int height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:239
constexpr QPoint topLeft() const noexcept
Returns the position of the rectangle's top-left corner.
Definition qrect.h:221
constexpr int x() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:185
constexpr QSize size() const noexcept
Returns the size of the rectangle.
Definition qrect.h:242
constexpr int width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:236
constexpr int y() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:188
The QRegion class specifies a clip region for a painter.
Definition qregion.h:27
\inmodule QtCore \reentrant
\inmodule QtCore \reentrant
\inmodule QtCore
Definition qsize.h:208
\inmodule QtCore
Definition qsize.h:25
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QByteArray toLatin1() const &
Definition qstring.h:630
rect
[4]
void qErrnoWarning(const char *msg,...)
Combined button and popup list for selecting options.
static QRect determineGeometry(const struct fbtype &fb, const QRect &userGeometry)
static int openFramebufferDevice(const QString &dev)
static QSizeF determinePhysicalSize(const QSize &mmSize, const QSize &res)
#define QT_OPEN
#define QT_CLOSE
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:327
static QImage::Format determineFormat(const FBInfo *fbinfo)
static QRect determineGeometry(const fb_var_screeninfo &vinfo, const QRect &userGeometry)
static int openTtyDevice(const QString &device)
static void resetTty(int ttyfd, int oldMode)
static void blankScreen(int fd, bool on)
static int determineDepth(const fb_var_screeninfo &vinfo)
static int openFramebufferDevice(const QString &dev)
static QSizeF determinePhysicalSize(const fb_var_screeninfo &vinfo, const QSize &mmSize, const QSize &res)
static void switchToGraphicsMode(int ttyfd, bool doSwitch, int *oldMode)
static QImage::Format determineFormat(const fb_var_screeninfo &info, int depth)
#define qWarning
Definition qlogging.h:166
GLint GLint GLint GLint GLint x
[0]
GLint GLenum GLsizei GLsizei GLsizei depth
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei height
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLint GLsizei width
GLenum access
GLuint64 GLenum GLint fd
GLint GLsizei GLsizei GLenum format
GLint y
GLfloat GLfloat GLfloat GLfloat h
GLuint res
SSL_CTX int void * arg
#define qPrintable(string)
Definition qstring.h:1531
static bool match(const uchar *found, uint foundLen, const char *target, uint targetLen)
unsigned char uchar
Definition qtypes.h:32
unsigned int uint
Definition qtypes.h:34
QObject::connect nullptr
aWidget window() -> setWindowTitle("New Window Title")
[2]
QHostInfo info
[0]
QJSValueList args