Покрасьте пикселы на экран через Linux FrameBuffer
Недавно меня поразила любопытная идея взять входные данные из /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;
}
Ответы
Ответ 1
Если вы используете X11, вы ДОЛЖНЫ пройти через API X11, чтобы рисовать на экране. Обход X-сервера очень сломан (и, часто, как вы видели, не работает). Это может также вызвать сбои или просто общее повреждение дисплея.
Если вы хотите работать везде (как в консоли, так и под X), посмотрите SDL или GGI. Если вы только заботитесь о X11, вы можете использовать GTK, QT или даже Xlib. Существует много вариантов...
Ответ 2
У меня был успех в следующих нескольких экспериментах.
Сначала выясните, использует ли X TrueColor RGB до 32 бит (или просто предположим, что это так). Затем выясните, есть ли у вас разрешение на запись в fb0 (и что оно существует). Если они верны (и я ожидаю, что многие современные инструментальные средства/настольные компьютеры/ПК могут использовать их как значения по умолчанию), то вы должны иметь возможность сделать следующее (и если эти значения по умолчанию не выполняются, то вы, вероятно, все еще можете добиться успеха с следующие тесты, хотя детали могут отличаться):
Тест 1: откройте виртуальный терминал (в X) и введите:
$ echo "ddd... ddd" > /dev/fb0
где... на самом деле это несколько скриншотов d. Результатом будет одна или несколько (частичных) линий серого в верхней части экрана, в зависимости от того, как долго ваша эхо-строка и какое разрешение пикселей вы включили. Вы также можете выбрать любые буквы (значения ascii все меньше 0x80, поэтому цвет будет темно-серым цветом.. и измените буквы, если вы хотите что-то, кроме серого). Очевидно, что это можно обобщить на цикл оболочки или вы можете написать большой файл, чтобы увидеть эффект более четко: например:
$ cat/lib/libc.so.6 > /dev/fb0
чтобы увидеть истинные цвета некоторых сторонников fsf; -P
Не беспокойтесь, если надпись на большом экране будет написана. X по-прежнему контролирует указатель мыши и все еще имеет представление о том, где отображаются окна. Все, что вам нужно сделать, это захватить любое окно и немного перетащить его, чтобы стереть шум.
Тест 2: cat/dev/fb0 > xxx
затем измените внешний вид рабочего стола (например, откройте новые окна и закройте другие).
Наконец, сделайте обратное: cat xxx > /dev/fb0, чтобы вернуть старый рабочий стол!
Ха, ну, не совсем. Образ старого рабочего стола - это иллюзия, и вы быстро обойдете его, когда вы откроете любое окно на весь экран.
Тест 3: напишите небольшое приложение, которое захватывает предыдущий дамп /dev/fb 0 и изменяет цвета пикселей, например, чтобы удалить красный компонент или увеличить синий цвет или перевернуть красный и зеленый цвета и т.д. Затем напишите эти пиксели в новый файл, который вы можете посмотреть позже, используя простой подход к тестированию оболочки 2. Также обратите внимание, что вы, вероятно, будете иметь дело с 4-байтовыми количествами BGRA на пиксель. Это означает, что вы хотите игнорировать каждый 4-й байт, а также обрабатывать первый в каждом наборе как синий компонент. "ARGB" является big-endian, поэтому, если вы посещаете эти байты через увеличивающийся индекс массива C, синий будет первым, затем зеленым, а затем красным, то есть B-G-R-A (не A-R-G-B).
Тест 4: напишите приложение на любом языке, который на скорости видео отправит не квадратное изображение (думаю, xeyes) на часть экрана, чтобы создать анимацию без каких-либо окон. Для дополнительных очков анимация перемещается по всему экрану. Вам нужно будет пропустить большое пространство после рисования пикселов с небольшим количеством пикселей (чтобы компенсировать ширину экрана, которая, вероятно, намного шире, чем анимация изображения).
Тест 5: сыграйте трюк с другом, например, добавьте тест 4, чтобы изображение анимированного человека появилось на рабочем столе (возможно, сделайте снимок для получения данных пикселя), затем переходите к одному из их важные папки для рабочего стола, подбирает папку и разбивает ее на части, затем истерически смеется, а затем появляется огненный шар и поглощает весь рабочий стол. Хотя это все будет иллюзией, они могут немного поразмыслить... но использовать это как опыт обучения, чтобы продемонстрировать Linux и с открытым исходным кодом и показать, как его гораздо страшнее смотреть на новичков, чем на самом деле. [ "вирус", как правило, безвредные иллюзии в Linux]
Ответ 3
Я бы сказал, будьте осторожны, прежде чем пытаться писать в /dev/fb 0, как было предложено выше. Я попробовал это под X
в ubuntu 10.04 и a) ничего не произошло визуально, b) он разрушил все окна оболочки, даже другие ttys, что привело к ошибкам ядра и отсутствию функциональности.
Ответ 4
Вы должны использовать fb_fix_screeninfo.smem_len для экранирования вместо того, чтобы делать умножение самостоятельно. Буфер может быть выровнен на 4 байта или что-то еще.
screensize = finfo.smem_len;
Ответ 5
Когда я использовал эту программу для записи полного экрана, она разбилась, что связано с неправильным вычислением размера экрана.
// 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);
Ответ 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?, 2-й аргумент mmap(), screensize
не должен быть 0. иначе mmap() вернет MAP_FAILED.
Ответ 7
Я думаю о написании программы на основе фреймбуфера, просто потому, что мне нужно иметь возможность прокручивать (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 не понимает, что я испортил экранный буфер, он знает, что там находится, и соответственно регистрирует щелчки мышью. Если бы я переместил окно и не положил его обратно, щелчки все равно работали бы там, где они были.