时间:2011-02-14 20:12:41

标签: c linux framebuffer

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

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

我发现了一个来自QT教程(不再可用)的示例C程序,该程序使用mmap写入缓冲区。程序成功运行,但同样没有输出到屏幕。有趣的是,当我将我的笔记本电脑放入Suspend并稍后恢复时,我看到了一个瞬间闪现的图像(一个红色正方形),它被更早地写入帧缓冲区。写入帧缓冲区是否在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;
}

7 个答案:

答案 0 :(得分:11)

我在以下几个实验中取得了成功。

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

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

如果大部分屏幕被覆盖,请不要担心。 X仍然可以控制鼠标指针,并且仍然知道窗口的映射位置。你所要做的就是抓住任何一个窗口并将其拖动一点以消除噪音。

测试2:cat / dev / fb0&gt; XXX 然后更改桌面的外观(例如,打开新窗口并关闭其他窗口)。 最后,反过来:cat xxx&gt; / dev / fb0以便恢复旧桌面!

哈,嗯,不太好。旧桌面的图像是一种幻觉,当您打开任何全屏窗口时,您将很快省去它。

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

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

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

答案 1 :(得分:7)

如果你正在运行X11,你必须通过X11 API绘制到屏幕上。绕过X服务器非常破碎(而且,正如您所见,通常不起作用)。它也可能导致崩溃,或只是一般的显示损坏。

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

答案 2 :(得分:2)

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

答案 3 :(得分:1)

您应该使用fb_fix_screeninfo.smem_len进行屏幕大小而不是自己进行乘法。缓冲区可能在4个字节或其他内容上对齐。

screensize = finfo.smem_len;

答案 4 :(得分:0)

当我使用这个程序写全屏时,由于屏幕尺寸计算错误而导致崩溃。

// 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);

答案 5 :(得分:0)

如果您调试程序,您将找到以下行:

 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。

答案 6 :(得分:0)

我正在考虑编写基于帧缓冲区的程序,只是因为我需要能够滚动(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)

无论我是否使用X,我的Raspberry Pi都可以正常工作。它对笔记本电脑的屏幕没有影响。那有一个/ dev / fb0,程序运行并且数字看起来正确,但是在视觉上什么也没做。也许是双重缓冲之类的。

在X下,它实际上没有造成任何伤害。如果在周围拖动一些窗口,则事物重绘,一切都会恢复。然后,我决定备份屏幕上的内容,并在完成后放回去,这也可行。我移动并放回的窗口就像从未碰过它一样。 X没意识到我已经搞砸了屏幕缓冲区,它知道它放在那里,然后相应地记录了鼠标单击。如果我移动了一个窗口并且没有将其放回原处,则单击仍然可以在原处工作。