【问题标题】:Paint Pixels to Screen via Linux FrameBuffer通过 Linux FrameBuffer 将像素绘制到屏幕上
【发布时间】:2011-06-27 04:12:51
【问题描述】:

我最近有一个奇怪的想法,即从 /dev/urandom 获取输入,将相关字符转换为随机整数,并将这些整数用作像素的 rgb/x-y 值以绘制到屏幕上。

我已经进行了一些研究(在 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.\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;
}

【问题讨论】:

  • 理想情况下,这需要适用于大多数(所有?)Linux 系统(独立于 DE),其他兼容性值得赞赏,但可选。
  • 帧缓冲设备通常(但不总是)独立于 X - 尝试使用 ctrl-alt-f1ctrl-alt-f2 切换到虚拟控制台并在那里运行演示。
  • 有趣...这似乎有效。有没有可能我可以将显示设置为 tty7 (X server)?
  • 注意,Wikipedia link[/link] 提到了一些直接使用帧缓冲区的程序,例如 MPlayer。他们如何将输出附加到桌面环境?
  • @RichardMartinez:正如它所说,“图形程序避免了 X Window 系统的大量开销。”我认为,当这些程序在 X Windows 下运行时(例如,在带有 LCD 屏幕的嵌入式设备上),它们可以直接访问它。

标签: c linux framebuffer


【解决方案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 的简单外壳方法查看。另外,请注意,您可能会处理每个像素的 BGRA 4 字节数量。这意味着您要忽略每 4 个字节,并将每组中的第一个字节视为蓝色分量。 “ARGB”是大端的,所以如果你通过增加 C 数组的索引来访问这些字节,蓝色会先出现,然后是绿色,然后是红色.. 即 B-G-R-A(不是 A-R-G-B)。

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

测试 5:在朋友身上耍花招,例如,扩展测试 4,以便在他们的桌面上弹出一张动画人物的图片(可能拍摄自己以获取像素数据),然后走到其中一个他们重要的桌面文件夹,拿起文件夹并将其撕碎,然后开始歇斯底里地大笑,然后有一个火球出来并吞没了他们的整个桌面。虽然这一切都只是一种错觉,但他们可能会有点惊慌失措……但将其作为一种学习体验来炫耀 Linux 和开源,并展示它对新手来说比实际看起来更可怕。 [“病毒”在 Linux 上通常是无害的幻想]

【讨论】:

  • 1.我从来没有能够用帧缓冲区影响 X。它仅适用于 TTY。 2. 其实是BGRT。如果您在引导期间设置 fb 设备时注意内核消息,您会看到它被引用。我不知道 T 是什么意思,但它肯定不是 alpha 或“透明度”。事实上,第四个字节似乎根本没有做任何事情。我试过用第四个字节从 0 到 255 绘制颜色,它看起来完全一样。我猜他们只是在保存第四个频道,以防外星人向我们介绍新的颜色或其他东西。
  • 编辑:Found out TBGRT 实际上 透明度。还直接从马口中确认它是BGRT 而不是BGRA。虽然我仍然无法获得透明度来制作任何东西,嗯,透明的。
  • @B1KMusic AIUI 您没有获得透明度,因为帧缓冲区是“最后”的东西。它“背后”没有任何东西。对于大多数设备,T 只是一个虚拟值。如果您有一个特殊的帧缓冲设备与支持透明度/不透明度的屏幕通信(例如,也可以生成白色像素的 TFT,而不仅仅是让背光透过的透明像素),您会看到效果。此外,您正在覆盖后台缓冲区,因此您正在破坏任何以前的值,并且必须自己考虑 Alpha。
  • 是的,如果第四个字节是0x77,或者任何会被解释为50%。我似乎确实记得实现某种算法来获取两种颜色和一个 alpha 值,并返回结果颜色。我相信我找到了所说的算法,所以here's a demo showing what I'm talking about.
  • (@uliwitness 该评论针对您)
【解决方案2】:

如果您运行的是 X11,则必须通过 X11 API 才能在屏幕上绘图。绕过 X 服务器非常糟糕(而且,正如您所见,通常是行不通的)。它也可能导致崩溃,或者只是一般的显示损坏。

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

【讨论】:

  • 感谢您的提示。在这和上面 caf 的评论之间,我已经意识到帧缓冲区是多么低级。我想我会为了这个脚本的简单性而坚持使用它,但如果我对更复杂的东西感兴趣,我肯定会使用更高级别的系统(至少 X)。
【解决方案3】:

按照上面的建议,在尝试写入 /dev/fb0 之前要小心。我在X下试过 在 ubuntu 10.04 中,a) 视觉上什么都没有发生,b) 它破坏了所有 shell 窗口,甚至其他 tty,导致内核错误和缺乏功能。

【讨论】:

  • 这很有趣。我已经在几个不同的系统上执行了这个(cat /dev/urandom > /dev/fb0),我通常在 X 上没有输出,但是如果我在一个虚拟 tty 上,我会看到一个充满随机像素的酷屏幕.我很惊讶这样的事情会破坏操作系统的其他部分,但当 X 说东西已经在屏幕上绘制时,也许 X 吓坏了。
  • 据我了解,如果 X 使用帧缓冲区并使用 KMS,则将随机值写入图形内存 (/dev/fd0) 比 X 使用自己的图形管理方式更有可能工作.
【解决方案4】:

我正在考虑编写一个基于帧缓冲区的程序,只是因为我需要能够滚动(SDR 瀑布)。请参阅https://www.raspberrypi.org/forums/viewtopic.php?f=28&t=232493&p=1425567#p1425567 我认为成功的滚动测试。在我们通过 ioctl 获取的这两个结构中,还有关于颜色深度的信息。您似乎将您的程序基于我所做的相同示例。 How to get pixel colour from framebuffer on 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);
        

        【讨论】:

          猜你喜欢
          • 2023-04-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-10-02
          • 1970-01-01
          • 2023-02-16
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多