关键词:Framebuffer、linuxfb、DRM等等。
QT在Linux中支持多种显示插件,包括EGLFS、LinuxFB、DirectFB、Wayland等。可以通过--platfrom选项指定选择何种插件。比如:./analogclock --platform linuxfb。
QT支持多种显示插件,显示插件打开Linux内核fb设备,Linux内核中GPU/Display驱动将应用数据刷新到Display设备上。此处,简单记录以上过程涉及到的模块。
1. QT5中linuxfb显示插件
class QPlatformScreen作为基类派生了显示接口类:
src\plugins\platforms\android\qandroidplatformscreen.h: class QAndroidPlatformScreen: public QObject, public QPlatformScreen, public AndroidSurfaceClient src\plugins\platforms\cocoa\qcocoascreen.h: class QCocoaScreen : public QPlatformScreen src\plugins\platforms\directfb\qdirectfbscreen.h: class QDirectFbScreen : public QPlatformScreen src\plugins\platforms\eglfs\api\qeglfsscreen_p.h: class Q_EGLFS_EXPORT QEglFSScreen : public QPlatformScreen src\platformsupport\fbconvenience: class QFbScreen : public QObject, public QPlatformScreen src\plugins\platforms\haiku\qhaikuscreen.h: class QHaikuScreen : public QPlatformScreen src\plugins\platforms\ios\qiosscreen.h: class QIOSScreen : public QObject, public QPlatformScreen src\plugins\platforms\minimalegl\qminimaleglscreen.h: class QMinimalEglScreen : public QPlatformScreen src\plugins\platforms\minimal\qminimalintegration.h: class QMinimalScreen : public QPlatformScreen src\plugins\platforms\offscreen\qoffscreencommon.h: class QOffscreenScreen : public QPlatformScreen src\plugins\platforms\openwfd\qopenwfdscreen.h: class QOpenWFDScreen : public QPlatformScreen src\plugins\platforms\qnx\qqnxscreen.h: class QQnxScreen : public QObject, public QPlatformScreen src\plugins\platforms\wasm\qwasmscreen.h: class QWasmScreen : public QObject, public QPlatformScreen src\plugins\platforms\windows\qwindowsscreen.h: class QWindowsScreen : public QPlatformScreen src\plugins\platforms\winrt\qwinrtscreen.h: class QWinRTScreen : public QPlatformScreen src\plugins\platforms\xcb\qxcbscreen.h: class Q_XCB_EXPORT QXcbScreen : public QXcbObject, public QPlatformScreen
其中QFbScreen有派生了如下显示类:
src\plugins\platforms\bsdfb\qbsdfbscreen.h: class QBsdFbScreen : public QFbScreen src\plugins\platforms\integrity\qintegrityfbscreen.h: class QIntegrityFbScreen : public QFbScreen src\plugins\platforms\linuxfb\qlinuxfbdrmscreen.h: class QLinuxFbDrmScreen : public QFbScreen src\plugins\platforms\linuxfb\qlinuxfbscreen.h: class QLinuxFbScreen : public QFbScreen src\plugins\platforms\vnc\qvncscreen.h: class QVncScreen : public QFbScreen
通过make menuconfig进入Target packages->Graphic libraries and applications(graphic/text)->QT5配置显示插件,以及默认插件:
编译完成后在Target Rootfs中每个插件以库的形式保存:
/usr/lib/qt/plugins/platforms/ |-- libqeglfs.so |-- libqlinuxfb.so |-- libqminimal.so |-- libqminimalegl.so |-- libqoffscreen.so `-- libqvnc.so
2. QLinuxFbScreen插件
class QLinuxFbScreen : public QFbScreen { Q_OBJECT public: QLinuxFbScreen(const QStringList &args);--参数的初始化赋值。 ~QLinuxFbScreen();--去初始化,内存去映射以及关闭fb句柄。 bool initialize() override;--打开fb,获取显示相关参数,映射frame buffer到用户空间,并以frame buffer映射内存为缓存穿件QImage,最后清空屏幕。 QPixmap grabWindow(WId wid, int x, int y, int width, int height) const override;--通过QPixmap从映射QImage中获取屏幕内容。 QRegion doRedraw() override;--通过QPainter刷新frame buffer。 private: QStringList mArgs; int mFbFd; int mTtyFd; QImage mFbScreenImage; int mBytesPerLine; int mOldTtyMode; struct { uchar *data; int offset, size; } mMmap; QPainter *mBlitter; };
QLinuxFbScreen::initialize()打开fb设备,从中获取屏幕参数,映射frame buffer内存,并以此创建QImage对象,为后续doRedraw()和grabWindow()做好准备。
bool QLinuxFbScreen::initialize() { QRegularExpression ttyRx(QLatin1String("tty=(.*)")); QRegularExpression fbRx(QLatin1String("fb=(.*)")); QRegularExpression mmSizeRx(QLatin1String("mmsize=(\\d+)x(\\d+)")); QRegularExpression sizeRx(QLatin1String("size=(\\d+)x(\\d+)")); QRegularExpression offsetRx(QLatin1String("offset=(\\d+)x(\\d+)")); QString fbDevice, ttyDevice; QSize userMmSize; QRect userGeometry; bool doSwitchToGraphicsMode = true; // Parse arguments for (const QString &arg : qAsConst(mArgs)) { QRegularExpressionMatch match; if (arg == QLatin1String("nographicsmodeswitch")) doSwitchToGraphicsMode = false; else if (arg.contains(mmSizeRx, &match)) userMmSize = QSize(match.captured(1).toInt(), match.captured(2).toInt()); else if (arg.contains(sizeRx, &match)) userGeometry.setSize(QSize(match.captured(1).toInt(), match.captured(2).toInt())); else if (arg.contains(offsetRx, &match)) userGeometry.setTopLeft(QPoint(match.captured(1).toInt(), match.captured(2).toInt())); else if (arg.contains(ttyRx, &match)) ttyDevice = match.captured(1); else if (arg.contains(fbRx, &match)) fbDevice = match.captured(1); } if (fbDevice.isEmpty()) {--如果没有指定fb设备,下面选择默认设备。 fbDevice = QLatin1String("/dev/fb0"); if (!QFile::exists(fbDevice)) fbDevice = QLatin1String("/dev/graphics/fb0"); if (!QFile::exists(fbDevice)) { qWarning("Unable to figure out framebuffer device. Specify it manually."); return false; } } // Open the device mFbFd = openFramebufferDevice(fbDevice);--打开fb设备。 if (mFbFd == -1) { qErrnoWarning(errno, "Failed to open framebuffer %s", qPrintable(fbDevice)); return false; } // Read the fixed and variable screen information fb_fix_screeninfo finfo; fb_var_screeninfo vinfo; memset(&vinfo, 0, sizeof(vinfo)); memset(&finfo, 0, sizeof(finfo)); if (ioctl(mFbFd, FBIOGET_FSCREENINFO, &finfo) != 0) {--获取当前fb设备的固定信息。 qErrnoWarning(errno, "Error reading fixed information"); return false; } if (ioctl(mFbFd, FBIOGET_VSCREENINFO, &vinfo)) {--获取当前fb设备的可变信息,包括分辨率、像素位宽等等。 qErrnoWarning(errno, "Error reading variable information"); return false; } mDepth = determineDepth(vinfo);--得出当前fb设备的色深。 mBytesPerLine = finfo.line_length;--获取一行数据长度。 QRect geometry = determineGeometry(vinfo, userGeometry); mGeometry = QRect(QPoint(0, 0), geometry.size()); mFormat = determineFormat(vinfo, mDepth); mPhysicalSize = determinePhysicalSize(vinfo, userMmSize, geometry.size()); // mmap the framebuffer mMmap.size = finfo.smem_len;--frambuffer的大小。 uchar *data = (unsigned char *)mmap(0, mMmap.size, PROT_READ | PROT_WRITE, MAP_SHARED, mFbFd, 0);--将内核中framebuffer映射为可读写,大小为finfo.smem_len大小的一块buffer。 if ((long)data == -1) { qErrnoWarning(errno, "Failed to mmap framebuffer"); return false; } mMmap.offset = geometry.y() * mBytesPerLine + geometry.x() * mDepth / 8;--预留一帧+一行大小的数据。 mMmap.data = data + mMmap.offset;--QImage的buffer从mMmap.offset开始。 QFbScreen::initializeCompositor(); mFbScreenImage = QImage(mMmap.data, geometry.width(), geometry.height(), mBytesPerLine, mFormat);--QImage类是设备无关的图像,可以进行像素及操作,也可被用作绘图设备。 mCursor = new QFbCursor(this); mTtyFd = openTtyDevice(ttyDevice); if (mTtyFd == -1) qErrnoWarning(errno, "Failed to open tty"); switchToGraphicsMode(mTtyFd, doSwitchToGraphicsMode, &mOldTtyMode); blankScreen(mFbFd, false);--调用FBIOBLANK。 return true; }
3. Linux DRM和framebuffer驱动
make linux-menuconfig中进入Device Driver->Graphics support,打开Direct Rendering Manager和DRM Support for PL111 CLCD driver:
pl111驱动注册framebuffer设备:
pl111_amba_driver ->pl111_amba_probe ->pl111_versatile_init ->pl111_vexpress_clcd_init ->drm_fbdev_generic_setup ->drm_client_init ->drb_fbdev_client_hotplug ->drm_fb_helper_initial_config ->__drm_fb_helper_initial_config_and_unlock ->register_framebuffer
dtb配置:
clcd@10020000 { compatible = "arm,pl111", "arm,primecell"; reg = <0x10020000 0x1000>; interrupt-names = "combined"; interrupts = <0 44 4>; clocks = <&oscclk1>, <&oscclk2>; clock-names = "clcdclk", "apb_pclk"; /* 1024x768 16bpp @65MHz */ max-memory-bandwidth = <95000000>; port { clcd_pads_ct: endpoint { remote-endpoint = <&dvi_bridge_in_ct>; arm,pl11x,tft-r0g0b0-pads = <0 8 16>; }; }; };
Linux DRM子系统属于GPU一部分,更多参考《DRM子系统》。关于Framebuffer,更多参考《Frame Buffer Library — The Linux Kernel documentation》《Mine of Information - Linux Framebuffer Drivers (vonos.net)》《Linux Framebuffer 实验》。
《Linux Graphics Drivers: an Introduction (people.freedesktop.org)》中介绍了Linux显示相关驱动,包括Framebuffer、DRM、OpenGL等。
参考文档:
《Qt for Embedded Linux | Qt 5.15》
标签:src,插件,QT,--,public,platforms,DRM,plugins,class From: https://www.cnblogs.com/arnoldlu/p/17267914.html