Рисование пикселей на экране через Linux FrameBuffer - PullRequest
34 голосов
/ 14 февраля 2011

Недавно меня поразила любопытная идея взять входные данные из / dev / urandom, преобразовать соответствующие символы в случайные целые числа и использовать эти целые числа в качестве значений rgb / xy для пикселей для рисования на экране.

Я провел некоторые исследования (здесь, в StackOverflow и в других местах), и многие предполагают, что вы можете просто написать в / dev / fb0 напрямую, так как это файловое представление устройства.К сожалению, это, похоже, не дает каких-либо визуально видимых результатов.

Я нашел пример программы на C, которая была из учебника по QT (больше недоступна), которая использовала 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 ]

11 голосов
/ 08 мая 2011

Я имел успех в следующих нескольких экспериментах.

Сначала выясните, использует ли X TrueColor RGB с допуском до 32 бит (или просто предположите, что это так). Затем выясните, есть ли у вас разрешение на запись в fb0 (и существует ли оно). Если они верны (и я ожидаю, что многие современные наборы инструментов / настольные ПК / ПК могут использовать их как значения по умолчанию), то вы должны быть в состоянии сделать следующее (и если эти значения по умолчанию не выполняются, то вы, вероятно, все еще можете добиться успеха с следующие тесты, хотя детали могут отличаться):

Тест 1: откройте виртуальный терминал (в X) и введите: $ echo "ddd ... ddd"> / dev / fb0 где ... на самом деле несколько скриншотов из d. Результатом будет одна или несколько (частичных) серых линий в верхней части экрана, в зависимости от того, какова длина вашей эхо-строки и какое разрешение пикселей вы включили. Вы также можете выбрать любые буквы (все значения ascii меньше 0x80, поэтому получаемый цвет будет темно-серым ... и варьируйте буквы, если вы хотите что-то кроме серого). Очевидно, что это может быть обобщено на цикл оболочки, или вы можете просмотреть большой файл, чтобы увидеть эффект более четко: например: $ cat /lib/libc.so.6> / dev / fb0 чтобы увидеть истинные цвета некоторых сторонников ФСФ; -P

Не беспокойтесь, если большая часть вашего экрана будет перезаписана. X по-прежнему контролирует указатель мыши и по-прежнему имеет представление о том, где отображаются окна. Все, что вам нужно сделать, это захватить любое окно и немного перетащить его, чтобы стереть шум.

Тест 2: cat / dev / fb0> xxx затем измените внешний вид вашего рабочего стола (например, откройте новые окна и закройте другие). Наконец, сделайте обратное: cat xxx> / dev / fb0, чтобы вернуть старый рабочий стол!

Ха, ну не совсем. Изображение вашего старого рабочего стола - иллюзия, и вы быстро откажетесь от него, когда откроете окно на весь экран.

Тест 3: Напишите небольшое приложение, которое захватывает предыдущий дамп / dev / fb0 и изменяет цвета пикселей, например, удаляет красный компонент или увеличивает синий, или переворачивает красный и зеленый и т. Д. Затем запишите эти пиксели в новый файл, который вы можете просмотреть позже с помощью простого подхода к оболочке теста 2. Также обратите внимание, что вы, вероятно, будете иметь дело с 4-байтовыми величинами BGRA на пиксель. Это означает, что вы хотите игнорировать каждый 4-й байт, а также рассматривать первый в каждом наборе как синий компонент. "ARGB" является байтом с прямым порядком байтов, поэтому если вы посещаете эти байты по возрастающему индексу массива C, сначала идет синий, затем зеленый, а затем красный .. т.е. B-G-R-A (не A-R-G-B).

Тест 4: напишите приложение на любом языке, которое зацикливается на скорости видео, посылая неквадратное изображение (например, xeyes) на часть экрана, чтобы создать анимацию без каких-либо границ окон. Для получения дополнительных очков, анимация перемещается по всему экрану. Вам нужно будет обязательно пропустить большое пространство после рисования пикселов на небольшую строку (чтобы компенсировать ширину экрана, которая, вероятно, намного шире, чем изображение, которое анимируется).

Тест 5: разыграйте друга, например, продлите тест 4, чтобы на его рабочем столе появилось изображение анимированного человека (может быть, вы сняли фильм, чтобы получить данные пикселей), а затем подошел к одному из их важные папки рабочего стола, подбирает папку и разбивает ее на части, затем начинает истерически хохотать, а затем выходит огненный шар и поглощает весь их рабочий стол. Хотя это все будет иллюзией, они могут немного испугаться ... но использовать это в качестве учебного опыта, чтобы показать Linux и открытый исходный код и показать, насколько он гораздо страшнее для новичка, чем на самом деле. ["вирус" - это вообще безобидные иллюзии в Linux]

7 голосов
/ 15 февраля 2011

Если вы работаете с X11, вы ДОЛЖНЫ пройти через API X11, чтобы перейти на экран. Работа с X-сервером очень неэффективна (и, как вы часто видели, не работает). Это может также вызвать сбои или просто общее повреждение дисплея.

Если вы хотите иметь возможность работать везде (как на консоли, так и под X), посмотрите на SDL или GGI. Если вы заботитесь только о X11, вы можете использовать GTK, QT или даже Xlib. Вариантов много, много ...

2 голосов
/ 28 июня 2012

Я бы сказал, что будьте осторожны, прежде чем пытаться писать в / dev / fb0, как предложено выше.Я попробовал это под X в Ubuntu 10.04 и а) визуально ничего не произошло, б) он разрушил все окна оболочки, даже другие ttys, что привело к ошибкам ядра и отсутствию функциональности.

1 голос
/ 12 января 2016

Вы должны использовать fb_fix_screeninfo.smem_len для размера экрана вместо того, чтобы делать умножение самостоятельно. Буфер может быть выровнен на 4 байта или что-то еще.

screensize = finfo.smem_len;
0 голосов
/ 04 февраля 2019

Я думаю о написании программы на основе фреймбуфера, просто потому, что мне нужно иметь возможность прокручиваться (водопад 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 не понимает, что я испортил экранный буфер, он знает, что там находится, и соответственно регистрирует щелчки мышью.Если бы я переместил окно и не положил его обратно, щелчки все равно сработали бы там, где оно было.

0 голосов
/ 12 июня 2016

если вы отлаживаете вашу программу, вы найдете строку:

 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?2-й аргумент функции mmap (), screensize, не должен быть 0. В противном случае mmap () вернет MAP_FAILED.

0 голосов
/ 23 мая 2014

Когда я использовал эту программу для записи в полноэкранном режиме, она зависала из-за неправильного расчета размера экрана.

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