帧缓冲(Framebuffer)是Linux系统为显示设备提供的一个接口,它将显示缓冲区进行抽象, 允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。即你可以直接在应用程序中修改屏幕上某个像素点的颜色值,让写屏变得简单直观。
帧缓冲设备对应的设备文件为/dev/fb*, 通过操作/dev/fb*,即可实现写屏操作。
直接上代码:
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
struct fb_var_screeninfo vinfo = {0}; //定义结构体变量,读取屏幕信息时用来记录屏幕可变信息的
unsigned char *fb_base;//显存的基地址
unsigned int line_length;
unsigned int bytes_per_pixel;//每像素占用字节数
void set_pixel_color(int x, int y, unsigned int color)
{
int index = y * line_length + x * bytes_per_pixel;
*(unsigned int *)(fb_base + index) = color;
}
int main()
{
int i;
int fd_fb;
int frame_buffer_size;//显存大小
fd_fb = open("/dev/fb0", O_RDWR);
if (fd_fb < 0)
{
perror("open");
return -1;
}
//获取可变的参数fb_var_screeninfo的值
if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &vinfo))
{
perror("ioctl");
return -1;
}
bytes_per_pixel = vinfo.bits_per_pixel / 8;
line_length = vinfo.xres_virtual * bytes_per_pixel;
frame_buffer_size = vinfo.xres_virtual * vinfo.yres_virtual * bytes_per_pixel;
//通过mmap获取显存地址
fb_base = (unsigned char *)mmap(NULL , frame_buffer_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
if (fb_base == (unsigned char *)-1)
{
printf("can't mmap\n");
return -1;
}
// 将全屏设为白色
memset(fb_base, 0xff, frame_buffer_size);
//画一条蓝色的水平线
for (i = 0; i < vinfo.xres_virtual; i++)
{
set_pixel_color(i, 100, 0x0000ff);//修改某个坐标点像素
}
//释放显存映射
munmap(fb_base , frame_buffer_size);
close(fd_fb);
return 0;
}
以上代码最终呈现效果是在白底屏幕上画出一条直线。
有几点需要注意:
1.需要将Linux从图形界面切换到终端模式下运行程序,不然程序运行是没有效果的,ctrl+alt+F1/F2/F3/F4可以切换不同的tty终端。
2.我的调试环境是VirtualBox 6.1 + Ubuntu 18.04, 屏幕大小(xres * yres)与虚拟屏幕(xres_virtual * yres_virtual)是不一样大的,而Framebuffer对应的屏幕其实是虚拟屏幕,实际屏幕与虚拟屏幕的关系参见下图,因此在定位像素点时,要注意坐标的换算要以虚拟屏幕的大小来换算。