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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4996777/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 00:20:40  来源:igfitidea点击:

Paint Pixels to Screen via Linux FrameBuffer

clinuxframebuffer

提问by Richard Martinez

I was recently struck by a curious idea to take input from /dev/urandom, convert relevant characters to random integers, and use those integers as the rgb/x-y values for pixels to paint onto the screen.

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

I've done some research (here on StackOverflow and elsewhere) and many suggest that you can simply write to /dev/fb0 directly as it is the file representation of the device. Unfortunately, this does not seem to produce any visually apparent results.

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

I found a sample C program that was from a QT tutorial (no longer available) that used an mmap to write to the buffer. The program runs successfully, but again, no output to the screen. Interestingly enough, when I placed my laptop into Suspend and later restored, I saw a momentary flash of the image (a red square) that was written to the framebuffer much earlier. Does writing to the framebuffer work anymore in Linux for painting to screen? Ideally, I'd like to write a (ba)sh script, but C or similar would work as well. Thanks!

我发现了一个来自 QT 教程(不再可用)的示例 C 程序,它使用 mmap 写入缓冲区。程序运行成功,但同样没有输出到屏幕。有趣的是,当我将笔记本电脑置于 Suspend 并稍后恢复时,我看到了更早写入帧缓冲区的图像(红色方块)的瞬间闪烁。写入帧缓冲区是否在 Linux 中不再用于绘制到屏幕?理想情况下,我想编写一个 (ba)sh 脚本,但 C 或类似的脚本也可以。谢谢!

EDIT: Here's the sample program...may look familiar to vets.

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

#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;
}

采纳答案by derobert

If you're running X11, you MUST go through X11 APIs to draw to the screen. Going around the X server is very broken (and, often as you've seen, does not work). It may also cause crashes, or just general display corruption.

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

If you want to be able to run everywhere (both console & under X), look at SDL or GGI. If you only care about X11, you can use GTK, QT, or even Xlib. There are many, many options...

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

回答by Jose_X

I've had success with the following few experiments.

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

First, find out if X is using TrueColor RGB padded to 32 bits (or just assume this is the case). Then find out if you have write permission to fb0 (and that it exists). If these are true (and I expect many modern toolkits/desktops/PCs might use these as defaults), then you should be able to do the following (and if these defaults don't hold, then you probably can still have some success with the following tests though the details may vary):

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

Test 1: open up a virtual terminal (in X) and type in: $ echo "ddd ... ddd" >/dev/fb0 where the ... is actually a few screen-fulls of d. The result will be one or more (partial) lines of gray across the top of your screen, depending on how long is your echo string and what pixel resolution you have enabled. You can also pick any letters (the ascii values are all less than 0x80, so the color produced will be a dark gray.. and vary the letters if you want something besides gray). Obviously, this can be generalized to a shell loop or you can cat a large file to see the effect more clearly: eg: $ cat /lib/libc.so.6 >/dev/fb0 in order to see the true colors of some fsf supporters ;-P

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

Don't worry if a large chunk of your screen gets written over. X still has control of the mouse pointer and still has its idea of where windows are mapped. All you have to do is to grab any window and drag it around a bit to erase the noise.

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

Test 2: cat /dev/fb0 > xxx then change the appearance of your desktop (eg, open new windows and close others). Finally, do the reverse: cat xxx > /dev/fb0 in order to get your old desktop back!

测试 2: cat /dev/fb0 > xxx 然后更改桌面的外观(例如,打开新窗口并关闭其他窗口)。最后,执行相反的操作: cat xxx > /dev/fb0 以恢复您的旧桌面!

Ha, well, not quite. The image of your old desktop is an illusion, and you will quickly dispense with it when you open any window to full screen.

哈,好吧,不完全是。旧桌面的图像是一种错觉,当您将任何窗口全屏打开时,您将很快摆脱它。

Test 3: Write a little app that grabs a prior dump of /dev/fb0 and modifies the colors of the pixels, eg, to remove the red component or augment the blue, or flip the red and green, etc. Then write back these pixels into a new file you can look at later via the simple shell approach of test 2. Also, note that you will likely be dealing with B-G-R-A 4-byte quantities per pixel. This means that you want to ignore every 4th byte and also treat the first in each set as the blue component. "ARGB" is big-endian, so if you visit these bytes through increasing index of a C array, blue would come first, then green, then red.. ie, B-G-R-A (not A-R-G-B).

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

Test 4: write an app in any language that loops at video speed sending a non square picture (think xeyes) to a part of the screen so as to create an animation without any windows borders. For extra points, have the animation move all over the screen. You will have to make sure to skip a large space after drawing a small row's worth of pixels (to make up for the screen width that is likely much wider than the picture being animated).

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

Test 5: play a trick on a friend, eg, extend test 4 so that a picture of an animated person appears to pop up on their desktop (maybe film yourself to get the pixel data), then walks over to one of their important desktop folders, picks up the folder and shreds it apart, then starts laughing hysterically, and then have a fireball come out and engulf their entire desktop. Though this will all be an illusion, they may freak out a bit.. but use that as a learning experience to show off Linux and open source and show how its much scarier looking to a novice than it actually is. [the "virus" are generally harmless illusions on Linux]

测试 5:对朋友玩一个把戏,例如,扩展测试 4,使他们的桌面上弹出一个动画人物的图片(也许自己拍摄以获得像素数据),然后走到他们的一个重要桌面上文件夹,拿起文件夹并将其撕成碎片,然后开始歇斯底里地大笑,然后有一个火球出来并吞没了他们的整个桌面。尽管这都是一种错觉,但他们可能会有点害怕……但是将其作为学习经验来炫耀 Linux 和开源,并展示它对新手的看法比实际情况要可怕得多。[“病毒”在 Linux 上通常是无害的幻觉]

回答by David

I'd say be careful before trying writing to /dev/fb0, as suggested above. I tried it under X in ubuntu 10.04 and a) nothing happened visually, b) it wrecked all shell windows, even other ttys, leading to kernel errors and lack of functionality.

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

回答by Nataraja KM

When I used this program to write full screen it had crashed that is due screen size calculation is wrong.

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

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

This supposed to be:

这应该是:

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

回答by Alex Arsenault

You should use fb_fix_screeninfo.smem_len for screensize instead of doing the multiplication yourself. The buffer might be align on 4 bytes or something else.

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

screensize = finfo.smem_len;

回答by Hsiang Chen

if you debug your program, you will find the line:

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

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

screensizeis 0. because vinfo.xres is 0. you should change it to:

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;

since Linux 2.6.2? , the 2nd arguments of mmap(), screensize, must not be 0. otherwise mmap() will return MAP_FAILED.

从 Linux 2.6.2 开始?, mmap() 的第二个参数screensize不能为 0。否则 mmap() 将返回 MAP_FAILED。

回答by Alan Corey

I'm thinking of writing a framebuffer-based program, just because I need to be able to scroll (SDR waterfall). See https://www.raspberrypi.org/forums/viewtopic.php?f=28&t=232493&p=1425567#p1425567The scroll test I consider successful. In those 2 structs we fetch by ioctl for info there's also stuff about color depth. You seem to have based your program on the same example I did. How to get pixel colour from framebuffer on linux (Raspberry Pi)

我正在考虑编写一个基于帧缓冲区的程序,只是因为我需要能够滚动(SDR 瀑布)。参见https://www.raspberrypi.org/forums/viewtopic.php?f=28&t=232493&p=1425567#p1425567我认为成功的滚动测试。在我们通过 ioctl 获取信息的那 2 个结构中,还有关于颜色深度的内容。您的程序似乎基于我所做的相同示例。如何从 linux (Raspberry Pi) 上的帧缓冲区获取像素颜色

Mine works fine on my Raspberry Pi, either with X or not. It has no effect on the screen on my laptop. That has a /dev/fb0, the program runs and the numbers look right, but it does nothing visually. Maybe it's double buffered or something.

我的在我的 Raspberry Pi 上运行良好,无论是否使用 X。它对我的笔记本电脑的屏幕没有影响。它有一个 /dev/fb0,程序运行并且数字看起来正确,但它在视觉上没有任何作用。也许它是双缓冲或什么的。

Under X it doesn't actually do any damage. If you drag some windows around so things redraw everything comes back. Then I decided to back up what's on the screen and put it back when I was done, that works too. A window that I moved and put back works the same as if I'd never touched it. X doesn't realize I've messed with the screen buffer, it knows what it put there and registers mouse clicks accordingly. If I moved a window and didn't put it back the clicks would still work where it was.

在 X 下,它实际上不会造成任何损害。如果你拖动一些窗口,那么重绘一切都会回来。然后我决定备份屏幕上的内容并在我完成后将其放回原处,这也有效。我移动并放回的窗户就像我从未接触过它一样工作。X 没有意识到我弄乱了屏幕缓冲区,它知道它在那里放了什么并相应地注册鼠标点击。如果我移动了一个窗口并且没有把它放回原处,点击仍然会在原来的地方工作。