通过 Linux FrameBuffer 将像素绘制到屏幕上
- 2024-11-13 08:36:00
- admin 原创
- 21
问题描述:
最近我突然有了一个奇怪的想法,从 /dev/urandom 获取输入,将相关字符转换为随机整数,并使用这些整数作为像素的 rgb/xy 值来绘制到屏幕上。
我做了一些研究(在 StackOverflow 和其他地方),许多人建议你可以直接写入 /dev/fb0,因为它是设备的文件表示。不幸的是,这似乎没有产生任何视觉上明显的结果。
我找到了一个来自 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.
");
// 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
", 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.
");
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;
}
解决方案 1:
我通过以下几个实验取得了成功。
首先,确定 X 是否使用填充为 32 位的 TrueColor RGB(或者假设是这种情况)。然后确定您是否具有 fb0 的写入权限(并且它存在)。如果这些是真的(并且我预计许多现代工具包/台式机/ PC 可能会使用这些作为默认值),那么您应该能够执行以下操作(如果这些默认值不成立,那么您可能仍可以通过以下测试获得一些成功,尽管细节可能会有所不同):
测试 1:打开虚拟终端(在 X 中)并输入:$ echo "ddd ... ddd" >/dev/fb0 其中 ... 实际上是几个屏幕的 d。结果将是屏幕顶部出现一行或多行(部分)灰色,具体取决于您的回显字符串的长度以及您启用的像素分辨率。您还可以选择任何字母(ascii 值都小于 0x80,因此产生的颜色将是深灰色……如果您想要灰色以外的颜色,请改变字母)。显然,这可以推广到 shell 循环,或者您可以 cat 一个大文件以更清楚地看到效果:例如:$ cat /lib/libc.so.6 >/dev/fb0 以查看一些 fsf 支持者的真实颜色 ;-P
如果屏幕上的一大块内容被覆盖,请不要担心。X 仍可控制鼠标指针,并且仍可知道窗口的映射位置。您所要做的就是抓住任何窗口并将其拖动一小段以消除噪音。
测试 2:cat /dev/fb0 > xxx 然后更改桌面的外观(例如,打开新窗口并关闭其他窗口)。最后,执行相反的操作:cat xxx > /dev/fb0 以恢复旧桌面!
哈,其实不完全是。旧桌面的图像只是一种幻觉,当你将任意窗口全屏打开时,你很快就会摆脱它。
测试 3:编写一个小应用程序,抓取 /dev/fb0 的先前转储并修改像素的颜色,例如,删除红色成分或增加蓝色,或翻转红色和绿色等。然后将这些像素写回到新文件中,稍后您可以通过测试 2 的简单 shell 方法查看该文件。另外,请注意,您可能会处理每个像素的 BGRA 4 字节数量。这意味着您要忽略每个第 4 个字节,并将每组中的第一个字节视为蓝色成分。“ARGB”是大端字节序,因此如果您通过增加 C 数组的索引来访问这些字节,蓝色将首先出现,然后是绿色,然后是红色……即 BGRA(不是 ARGB)。
测试 4:用任何语言编写一个应用程序,以视频速度循环,向屏幕的某个部分发送非方形图片(比如 xeyes),从而创建没有任何窗口边框的动画。为了获得加分,让动画在整个屏幕上移动。您必须确保在绘制一小行像素后跳过一大片空间(以弥补可能比动画图片宽得多的屏幕宽度)。
测试 5:捉弄朋友,例如,扩展测试 4,让一张动画人物的图片弹出到他们的桌面上(也许可以自己拍摄以获取像素数据),然后走到他们的一个重要的桌面文件夹前,拿起文件夹并将其撕碎,然后开始歇斯底里地大笑,然后让一个火球出来吞噬他们的整个桌面。虽然这一切都是幻觉,但他们可能会有点害怕……但可以将其作为学习经验来炫耀 Linux 和开源,并向新手展示它看起来比实际上可怕得多。[Linux 上的“病毒”通常是无害的幻觉]
解决方案 2:
如果您正在运行 X11,则必须通过 X11 API 来绘制到屏幕上。绕过 X 服务器非常不方便(而且,正如您所见,通常不起作用)。它还可能导致崩溃,或者只是一般的显示损坏。
如果您希望能够在任何地方运行(控制台和 X 下),请查看 SDL 或 GGI。如果您只关心 X11,您可以使用 GTK、QT 甚至 Xlib。有很多选择……
解决方案 3:
我想说,在尝试写入 /dev/fb0 之前要小心,如上所述。我在 ubuntu 10.04 的 X 下尝试过,结果 a) 没有任何视觉效果,b) 它破坏了所有 shell 窗口,甚至其他 tty,导致内核错误和功能缺失。
解决方案 4:
我正在考虑编写一个基于帧缓冲区的程序,因为我需要能够滚动(SDR 瀑布图)。请参阅https://www.raspberrypi.org/forums/viewtopic.php?f=28&t=232493&p=1425567#p1425567 我认为滚动测试是成功的。在我们通过 ioctl 获取信息的这两个结构中,还有关于颜色深度的内容。您的程序似乎基于我所做的相同示例。如何在 Linux 上从帧缓冲区获取像素颜色(Raspberry Pi)
我的程序在我的 Raspberry Pi 上运行良好,无论是否使用 X。它对我的笔记本电脑的屏幕没有影响。它有一个 /dev/fb0,程序运行并且数字看起来正确,但它在视觉上没有任何作用。也许是双缓冲之类的。
在 X 下,它实际上不会造成任何损害。如果你拖动一些窗口,让一切重新绘制,一切都会恢复。然后我决定备份屏幕上的内容,并在完成后将其放回原位,这也有效。我移动并放回的窗口工作起来就像我从未碰过它一样。X 没有意识到我弄乱了屏幕缓冲区,它知道它在那里放了什么,并相应地记录鼠标点击。如果我移动了一个窗口但没有把它放回去,点击仍然会在原来的位置起作用。
解决方案 5:
您应该使用 fb_fix_screeninfo.smem_len 来计算屏幕尺寸,而不是自己进行乘法运算。缓冲区可能对齐 4 个字节或其他内容。
screensize = finfo.smem_len;
解决方案 6:
如果你调试你的程序,你会发现这一行:
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。
解决方案 7:
当我使用该程序来全屏写入时它崩溃了,这是由于屏幕尺寸计算错误。
// 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);
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件