通过Linux FrameBuffer将像素绘制到屏幕上

我最近好奇的想法是从/ dev / urandom获取input,将相关字符转换为随机整数,并将这些整数用作像素的rgb / xy值来绘制到屏幕上。

我已经做了一些研究(这里在StackOverflow和其他地方),许多人build议你直接写入/ dev / fb0,因为它是设备的文件表示。 不幸的是,这似乎没有产生任何视觉上明显的结果。

我find了一个来自QT教程(不再可用)的示例C程序,它使用了mmap来写入缓冲区。 该程序运行成功,但再次,没有输出到屏幕上。 有趣的是,当我将笔记本电脑置于暂停状态并稍后恢复时,我看到了较早写入帧缓冲区的图像(红色方块)的瞬间闪烁。 写在帧缓冲器的工作已经在Linux中进行绘画屏幕了吗? 理想情况下,我想写一个(ba)sh脚本,但C或类似的也可以。 谢谢!

编辑:这里是示例程序…可能看起来很熟悉兽医。

#include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <fcntl.h> #include <linux/fb.h> #include <sys/mman.h> #include <sys/ioctl.h> int main() { int fbfd = 0; struct fb_var_screeninfo vinfo; struct fb_fix_screeninfo finfo; long int screensize = 0; char *fbp = 0; int x = 0, y = 0; long int location = 0; // Open the file for reading and writing fbfd = open("/dev/fb0", O_RDWR); if (fbfd == -1) { perror("Error: cannot open framebuffer device"); exit(1); } printf("The framebuffer device was opened successfully.\n"); // Get fixed screen information if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo) == -1) { perror("Error reading fixed information"); exit(2); } // Get variable screen information if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo) == -1) { perror("Error reading variable information"); exit(3); } printf("%dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel); // Figure out the size of the screen in bytes screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8; // Map the device to memory fbp = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0); if ((int)fbp == -1) { perror("Error: failed to map framebuffer device to memory"); exit(4); } printf("The framebuffer device was mapped to memory successfully.\n"); x = 100; y = 100; // Where we are going to put the pixel // Figure out where in memory to put the pixel for (y = 100; y < 300; y++) for (x = 100; x < 300; x++) { location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) + (y+vinfo.yoffset) * finfo.line_length; if (vinfo.bits_per_pixel == 32) { *(fbp + location) = 100; // Some blue *(fbp + location + 1) = 15+(x-100)/2; // A little green *(fbp + location + 2) = 200-(y-100)/5; // A lot of red *(fbp + location + 3) = 0; // No transparency //location += 4; } else { //assume 16bpp int b = 10; int g = (x-100)/6; // A little green int r = 31-(y-100)/16; // A lot of red unsigned short int t = r<<11 | g << 5 | b; *((unsigned short int*)(fbp + location)) = t; } } munmap(fbp, screensize); close(fbfd); return 0; } enter code here 

Solutions Collecting From Web of "通过Linux FrameBuffer将像素绘制到屏幕上"

如果你正在运行X11,你必须通过X11 API来绘制到屏幕上。 绕过X服务器非常糟糕(而且,正如你所看到的那样,这是行不通的)。 它也可能导致崩溃,或只是一般的显示损坏。

如果你想能够在任何地方(控制台和X下)运行,请查看SDL或GGI。 如果你只关心X11,你可以使用GTK,QT甚至Xlib。 有许多选择…

我已经通过以下几个实验取得了成功。

首先,看看X是否使用填充到32位的TrueColor RGB(或者假设是这种情况)。 然后找出你是否有写权限fb0(并且它存在)。 如果这些都是真的(我希望许多现代的工具包/桌面/ PC可能会使用这些作为默认),那么你应该能够做到以下几点(如果这些默认不成立,那么你可能仍然有一些成功以下测试虽然细节可能会有所不同):

测试1:打开一个虚拟终端(在X中)并键入:$ echo“ddd … ddd”> / dev / fb0其中…实际上是d的几个屏幕填满。 结果将会是屏幕顶部的一个或多个(部分)灰度线,具体取决于您的回显字符串的长度和您启用的像素分辨率。 你也可以选择任何字母(ascii的值都小于0x80,所以产生的颜色将是一个深灰色的..并改变字母,如果你想要的东西除了灰色)。 显然,这可以被推广到一个shell循环,或者你可以捕获一个大文件来更清楚地看到这个效果:例如:$ cat /lib/libc.so.6> / dev / fb0,以便看到一些真实的颜色fsf支持者; -P

如果大部分屏幕被覆盖,请不要担心。 X仍然具有鼠标指针的控制权,并且仍然具有其映射窗口的位置的想法。 所有你需要做的就是抓住任何一个窗口,拖动它来消除噪音。

测试2:cat / dev / fb0> xxx然后改变桌面的外观(例如,打开新窗口并关闭其他窗口)。 最后,做相反的:猫xxx> / dev / fb0为了得到您的旧桌面回来!

哈,好吧,不是。 旧桌面的图像是一种幻觉,当你打开任何一个窗口到全屏时,你会很快地放弃它。

测试3:写一个抓取/ dev / fb0之前转储的小应用程序,并修改像素的颜色,例如去除红色成分或增加蓝色,或者翻转红色和绿色等等。像素转化为一个新的文件,稍后您可以通过测试2的简单shell方法来查看。另外,请注意,您可能会在每个像素处理BGRA 4字节的数量。 这意味着你想忽略每一个第四个字节,并且把每个集合中的第一个作为蓝色的组件。 “ARGB”是big-endian,所以如果你通过增加一个C数组索引来访问这些字节,蓝色会先来,然后是绿色,然后是红色,即BGRA(不是ARGB)。

测试4:用任何语言编写的应用程序,以视频的速度循环发送非方形图片(认为xeyes)到屏幕的一部分,以创建一个没有任何窗口边框的动画。 对于额外的点,让动画在整个屏幕上移动。 绘制一小排像素后(为了弥补可能比动态图片更宽的屏幕宽度),您必须确保跳过一大片空间。

测试5:给朋友玩一个窍门,比如扩展测试4,让一个动画人物的图片出现在他们的桌面上(可能拍摄自己的像素数据),然后走到其中一个重要的桌面把文件夹拿起来,把文件夹撕碎,然后歇斯底里地开始大笑,然后有一个火球出来,吞没他们的整个桌面。 虽然这只会是一种幻想,但它们可能会让人大失所望,但是将其作为一种学习体验来炫耀Linux和开放源码,并展示它如何看待新手,而不是实际上。 [“病毒”在Linux上通常是无害的幻想]

如上所述,在写入/ dev / fb0之前,我会说要小心。 我在ubuntu 10.04的X版下试了一下,a)没有任何事情发生,b)它破坏了所有的shell窗口,甚至是其他的ttys,导致内核错误和缺乏功能。

您应该使用fb_fix_screeninfo.smem_len作为屏幕大小,而不是自己进行乘法运算。 缓冲区可能对齐4个字节或其他东西。

 screensize = finfo.smem_len; 

当我使用这个程序写全屏时,它已经崩溃,是由于屏幕尺寸计算是错误的。

 // Figure out the size of the screen in bytes screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8; 

这应该是:

 /* Calculate the size of the screen in bytes */ screensize = vinfo.xres_virtual * vinfo.yres_virtual * (vinfo.bits_per_pixel / 8); 

如果你调试你的程序,你会发现这行:

  screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8; 

screensize是0.因为vinfo.xres是0.您应该将其更改为:

 long ppc_fx = (((long)fixed_info.smem_start) - ((long) fixed_info.smem_start & ~(PAGE_SIZE-1))); screensize = finfo.smem_len + ppc_fx; 

自Linux 2.6.2? ,mmap(), screensize的第二个参数不能是0.否则mmap()将返回MAP_FAILED。