通过DRM绘制图像

要绕过传统的窗口系统(如X或Wayland),直接通过DRM(Direct Rendering Manager)控制和管理显示设备绘制图像,并显示在屏幕上,通常需要以下步骤:

1. 打开 DRM 设备

首先,程序通过 open() 函数打开 DRM 设备文件 (/dev/dri/card0),并获得一个文件描述符 drm_fd。这个描述符用于后续的所有 DRM 操作。

2. 获取 DRM 资源

使用 drmModeGetResources() 获取当前显示设备的资源信息,包括连接器(Connectors)、编码器(Encoders)、和 CRTC(Cathode Ray Tube Controllers)的列表。这些资源是显示图像的基础。

3. 查找有效的连接器

通过遍历获取的连接器列表,查找一个已经连接并可用的显示器连接器(即 DRM_MODE_CONNECTED 状态的连接器)。连接器代表显示输出接口,如 HDMI、VGA 等。

4. 获取连接器的显示模式

在找到一个有效的连接器后,从该连接器中提取显示模式(如分辨率和刷新率),这是后续设置帧缓冲区所需的参数。

5. 获取编码器和 CRTC

根据连接器的 encoder_id 获取对应的编码器,编码器将连接器与 CRTC 关联起来。再通过编码器的 crtc_id 获取 CRTC,CRTC 是负责实际控制图像扫描输出的硬件模块。

6. 创建帧缓冲区

使用 drm_mode_create_dumb 创建一个 “dumb buffer”(简单的帧缓冲区),这个缓冲区将被用于存储将要显示的图像数据。然后使用 drmModeAddFB 将这个缓冲区注册为 DRM 的帧缓冲区对象,并获取一个帧缓冲区 ID (fb_id),以供后续操作。

7. 映射帧缓冲区到用户空间

通过 mmap 函数将创建的帧缓冲区映射到用户空间内存中,从而可以直接操作它来更新显示内容。

8. 绘制图像

使用 std::fill 将帧缓冲区内存填充为红色(或其他颜色),这相当于绘制了一个纯色图像到屏幕上。

9. 设置 CRTC 并显示图像

使用 drmModeSetCrtc 将帧缓冲区与 CRTC 绑定,指定要显示的内容及其在屏幕上的位置,从而将绘制的图像输出到显示器上。

10. 等待和清理

在显示图像一段时间后(比如 5 秒),程序清理所分配的资源,包括解除内存映射、释放 CRTC、编码器、连接器和资源信息,并关闭 DRM 设备。

总结

  • 打开设备: 获取 DRM 文件描述符。

  • 获取资源: 获取连接器、编码器和 CRTC 的信息。

  • 创建和映射帧缓冲区: 创建用于图像存储的帧缓冲区,并映射到用户空间。

  • 绘制和显示: 填充帧缓冲区并通过 CRTC 显示到屏幕上。

  • 清理资源: 释放分配的所有资源,确保程序干净退出。

代码示例:简单的 DRM 程序

下面是一个完整的简单程序示例,使用 DRM 设置显示模式并绘制一个纯色图像。这个示例会打开 DRM 设备/dev/dri/card0,获取显示资源,设置显示模式,创建帧缓冲区,并绘制一个红色屏幕。

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include <drm/drm.h>
#include <drm/drm_mode.h>

int main() {
    int drm_fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
    if (drm_fd < 0) {
        perror("Cannot open /dev/dri/card0");
        return -1;
    }

    // 获取 DRM 资源
    drmModeRes *resources = drmModeGetResources(drm_fd);
    if (!resources) {
        perror("drmModeGetResources failed");
        close(drm_fd);
        return -1;
    }

    // 获取第一个连接器
    drmModeConnector *connector = NULL;
    for (int i = 0; i < resources->count_connectors; i++) {
        connector = drmModeGetConnector(drm_fd, resources->connectors[i]);
        if (connector->connection == DRM_MODE_CONNECTED) {
            break;
        }
        drmModeFreeConnector(connector);
        connector = NULL;
    }
    if (!connector) {
        fprintf(stderr, "No connected connector found\n");
        drmModeFreeResources(resources);
        close(drm_fd);
        return -1;
    }

    // 获取第一个显示模式
    drmModeModeInfo mode = connector->modes[0];

    // 获取第一个 encoder
    drmModeEncoder *encoder = drmModeGetEncoder(drm_fd, connector->encoder_id);
    if (!encoder) {
        perror("drmModeGetEncoder failed");
        drmModeFreeConnector(connector);
        drmModeFreeResources(resources);
        close(drm_fd);
        return -1;
    }

    // 获取 CRTC
    drmModeCrtc *crtc = drmModeGetCrtc(drm_fd, encoder->crtc_id);

    // 创建 framebuffer
    uint32_t fb_id;
    uint32_t width = mode.hdisplay;
    uint32_t height = mode.vdisplay;
    uint32_t pitch = width * 4;  // 每行字节数(假设 32 位色深)
    uint32_t size = pitch * height;
    uint32_t handle;
    uint32_t *fb_ptr;
    struct drm_mode_create_dumb create_req;
    struct drm_mode_map_dumb map_req;

    memset(&create_req, 0, sizeof(create_req));
    create_req.width = width;
    create_req.height = height;
    create_req.bpp = 32;

    if (drmIoctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_req) < 0) {
        perror("drmIoctl DRM_IOCTL_MODE_CREATE_DUMB failed");
        drmModeFreeCrtc(crtc);
        drmModeFreeEncoder(encoder);
        drmModeFreeConnector(connector);
        drmModeFreeResources(resources);
        close(drm_fd);
        return -1;
    }
    handle = create_req.handle;

    if (drmModeAddFB(drm_fd, width, height, 24, 32, pitch, handle, &fb_id)) {
        perror("drmModeAddFB failed");
        drmModeFreeCrtc(crtc);
        drmModeFreeEncoder(encoder);
        drmModeFreeConnector(connector);
        drmModeFreeResources(resources);
        close(drm_fd);
        return -1;
    }

    memset(&map_req, 0, sizeof(map_req));
    map_req.handle = handle;

    if (drmIoctl(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_req)) {
        perror("drmIoctl DRM_IOCTL_MODE_MAP_DUMB failed");
        drmModeFreeCrtc(crtc);
        drmModeFreeEncoder(encoder);
        drmModeFreeConnector(connector);
        drmModeFreeResources(resources);
        close(drm_fd);
        return -1;
    }

    fb_ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, drm_fd, map_req.offset);
    if (fb_ptr == MAP_FAILED) {
        perror("mmap failed");
        drmModeFreeCrtc(crtc);
        drmModeFreeEncoder(encoder);
        drmModeFreeConnector(connector);
        drmModeFreeResources(resources);
        close(drm_fd);
        return -1;
    }

    // 绘制红色背景
    for (uint32_t i = 0; i < size / 4; i++) {
        fb_ptr[i] = 0x00FF0000;  // ABGR, 所以 0x00FF0000 是红色
    }

    // 设置 CRTC 并显示图像
    if (drmModeSetCrtc(drm_fd, crtc->crtc_id, fb_id, 0, 0, &connector->connector_id, 1, &mode)) {
        perror("drmModeSetCrtc failed");
        munmap(fb_ptr, size);
        drmModeFreeCrtc(crtc);
        drmModeFreeEncoder(encoder);
        drmModeFreeConnector(connector);
        drmModeFreeResources(resources);
        close(drm_fd);
        return -1;
    }
    sleep(5);    // 等待 5 秒

    // 清理
    munmap(fb_ptr, size);
    drmModeFreeCrtc(crtc);
    drmModeFreeEncoder(encoder);
    drmModeFreeConnector(connector);
    drmModeFreeResources(resources);
    close(drm_fd);

    return 0;
}

代码解释

  • 初始化 DRM 设备:程序首先使用open函数打开/dev/dri/card0,以获得 DRM 设备的文件描述符。
  • 获取显示资源:使用drmModeGetResources 获取可用的显示资源,包括连接器、编码器和 CRTC 等。
  • 设置显示模式:选择一个连接器,获取第一个可用的显示模式,并设置该模式。
  • 创建帧缓冲:使用DRM_IOCTL_MODE_CREATE_DUMB 创建一个帧缓冲区,映射到内存中,然后使用drmModeAddFB 将其添加为 framebuffer。
  • 绘制内容:通过直接操作内存,填充整个帧缓冲区为红色(0x00FF0000)。
  • 显示图像:通过drmModeSetCrtc函数设置 CRTC 并显示图像。
  • 等待和清理:程序显示图像5秒后退出,释放所有资源并关闭设备。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值