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

2024-11-13 08:36:00
admin
原创
21
摘要:问题描述:最近我突然有了一个奇怪的想法,从 /dev/urandom 获取输入,将相关字符转换为随机整数,并使用这些整数作为像素的 rgb/xy 值来绘制到屏幕上。我做了一些研究(在 StackOverflow 和其他地方),许多人建议你可以直接写入 /dev/fb0,因为它是设备的文件表示。不幸的是,这似乎...

问题描述:

最近我突然有了一个奇怪的想法,从 /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);
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用