рисование пикселей на экране через linux framebuffer

<p>недавно меня поразила любопытная идея взять входные данные из/dev/urandom, преобразовать соответствующие символы в случайные целые числа и использовать эти целые числа в качестве значений rgb/x-y для пикселей для рисования на экране.</p><p>я провел некоторые исследования (здесь, в stackoverflow и в других местах), и многие предполагают, что вы можете просто записать в/dev/fb0 напрямую, так как это файловое представление устройства. к сожалению, это не дает каких-либо визуально видимых результатов.</p><p>я нашел пример программы на c, которая была из учебника по qt (больше недоступна), которая использовала mmap для записи в буфер. программа работает успешно, но опять же, нет вывода на экран. интересно, что когда я поместил свой ноутбук в suspend и позже восстановил его, я увидел кратковременную вспышку изображения (красный квадрат), которое было записано в кадровый буфер гораздо раньше. работает ли запись в фреймбуфер в linux для рисования на экран? в идеале я хотел бы написать сценарий (ba) sh, но c или аналогичный вариант тоже подойдут. спасибо!</p><p>правка: вот пример программы ... может показаться знакомым для ветеринаров.</p><pre><code>#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; } </code></pre>

<p>если вы используете x11, вы должны пройти через api x11, чтобы нарисовать на экране. хождение по x-серверу очень нарушено (и, как вы часто видели, не работает). это может также вызвать сбои или просто общее повреждение дисплея.</p><p>если вы хотите иметь возможность работать везде (как на консоли, так и под x), посмотрите на sdl или ggi. если вы заботитесь только о x11, вы можете использовать gtk, qt или даже xlib. есть много, много вариантов ...</p>

<p>я имел успех в следующих нескольких экспериментах.</p><p>во-первых, выясните, использует ли x truecolor rgb с допуском до 32 бит (или просто предположите, что это так). затем выясните, есть ли у вас разрешение на запись в fb0 (и существует ли оно). если они верны (и я ожидаю, что многие современные наборы инструментов/настольные пк/пк могут использовать их как значения по умолчанию), то вы должны быть в состоянии сделать следующее (и если эти значения по умолчанию не выполняются, то вы, вероятно, все еще можете добиться успеха с следующие тесты, хотя детали могут отличаться):</p><p>тест 1: откройте виртуальный терминал (в x) и введите: $ echo "ddd ... ddd">/dev/fb0 где ... на самом деле несколько экранов, полных d , результатом будет одна или несколько (частичных) серых линий в верхней части экрана, в зависимости от того, какова длина вашей эхо-строки и какое разрешение пикселей вы включили. вы также можете выбрать любые буквы (все значения ascii меньше 0x80, поэтому получаемый цвет будет темно-серым ... и варьируйте буквы, если вы хотите что-то кроме серого). очевидно, что это можно обобщить в цикле оболочки, или вы можете подключить большой файл, чтобы увидеть эффект более четко: например: $ cat /lib/libc.so.6>/dev/fb0 , чтобы увидеть истинные цвета некоторых сторонников фсф ;-p</p><p>не беспокойтесь, если большая часть вашего экрана будет перезаписана. x по-прежнему контролирует указатель мыши и по-прежнему имеет представление о том, где отображаются окна. все, что вам нужно сделать, это захватить любое окно и немного перетащить его, чтобы стереть шум.</p><p>тест 2: cat/dev/fb0> xxx затем измените внешний вид вашего рабочего стола (например, откройте новые окна и закройте другие) . наконец, сделайте обратное: cat xxx>/dev/fb0, чтобы верни свой старый рабочий стол!</p><p>ха, ну не совсем. изображение вашего старого рабочего стола - иллюзия, и вы быстро обойдетесь без него, когда откроете окно на весь экран.</p><p>тест 3: напишите небольшое приложение, которое захватывает предыдущий дамп/dev/fb0 и изменяет цвета пикселей, например, удаляет красный компонент или увеличивает синий, или переворачивает красный и зеленый и т.д. затем запишите эти пикселей в новый файл, который вы можете посмотреть позже с помощью простого подхода shell из теста 2. также обратите внимание, что вы, вероятно, будете иметь дело с 4-байтовыми величинами bgra на пиксель. это означает, что вы хотите игнорировать каждый 4-й байт, а также рассматривать первый в каждом наборе как синий компонент. «argb» является байтом с прямым порядком байтов, поэтому если вы посещаете эти байты по возрастающему индексу массива c, сначала идет синий, затем зеленый, а затем красный, т. е. b-g-r-a (не a-r-g-b).</p><p>тест 4: напишите приложение на любом языке, которое зацикливается на скорости видео, отправляя неквадратное изображение (например, xeyes) в часть экрана, чтобы создать анимацию без каких-либо границ окон. для получения дополнительных очков, анимация перемещается по всему экрану. вы должны будете обязательно пропустить большое пространство после рисования пикселов на небольшую строку (чтобы компенсировать ширину экрана, которая, вероятно, намного шире, чем изображение, которое анимируется).</p><p>тест 5: разыграйте с другом, например, продлите тест 4, чтобы на его рабочем столе появилось изображение анимированного человека (может быть, вы сняли фильм, чтобы получить данные о пикселях), а затем подошел к одному из важных рабочих столов. папки, поднимает папку и разбивает ее на части, затем начинает истерически смеяться, а затем выходит огненный шар и поглощает весь их рабочий стол. хотя это все будет иллюзией, они могут немного испугаться ... но использовать это в качестве учебного опыта, чтобы показать linux и открытый исходный код и показать, насколько он выглядит более пугающим для новичка, чем на самом деле. [«вирус» - это вообще безобидные иллюзии в linux]</p>

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

<p>вы должны использовать fb\_fix\_screeninfo.smem\_len для размера экрана вместо того, чтобы делать умножение самостоятельно. буфер может быть выровнен на 4 байта или что-то еще. </p><pre><code>screensize = finfo.smem\_len; </code></pre>

<p>если вы отлаживаете свою программу, вы найдете строку:</p><pre><code> screensize = vinfo.xres * vinfo.yres * vinfo.bits\_per\_pixel / 8; </code></pre><p><code>screensize</code> равен 0. потому что vinfo.xres равен 0 . вы должны изменить его на:</p><pre><code>long ppc\_fx = (((long)fixed\_info.smem\_start) - ((long) fixed\_info.smem\_start & ~(page\_size-1))); screensize = finfo.smem\_len + ppc\_fx; </code></pre><blockquote> <p>начиная с linux 2.6.2? 2-й аргумент функции mmap (), <code>screensize</code>, не должен быть равен 0. в противном случае mmap () вернет map\_failed.</p> </blockquote>

<p>я думаю о написании программы на основе фреймбуфера, просто потому, что мне нужно иметь возможность прокручивать (sdr водопад). см. <a href="https://www.raspberrypi.org/forums/viewtopic.php?f=28&t=232493&p=1425567#p1425567">https://www.raspberrypi.org/forums/viewtopic.php?f=28&t=232493&p=1425567#p1425567</a> тест прокрутки, который я считаю успешным. в этих двух структурах мы извлекаем информацию по ioctl, а также о глубине цвета. вы, кажется, основали свою программу на том же примере, что и я. <a href="https://stackoverflow.com/questions/19542029/how-to-get-pixel-colour-from-framebuffer-on-linux-raspberry-pi">как получить цвет пикселей из кадрового буфера на linux (raspberry pi)</a> </p><p>мой отлично работает на моем raspberry pi, либо с x или нет. это не влияет на экран моего ноутбука. у него есть/dev/fb0, программа запускается и цифры выглядят правильно, но визуально ничего не делает. может быть, это двойной буфер или что-то.</p><p>под x это фактически не наносит ущерба. если вы перетаскиваете несколько окон, чтобы все перерисовывалось, все возвращается. затем я решил сделать резервную копию того, что на экране, и положить его обратно, когда я закончу, это тоже работает. окно, которое я переместил и положил обратно, работает так же, как если бы я никогда не касался его. x не понимает, что я испортил экранный буфер, он знает, что там находится, и соответственно регистрирует щелчки мышью. если бы я переместил окно и не положил его обратно, щелчки все равно работали бы там, где они были.</p>

<p>когда я использовал эту программу для записи в полноэкранном режиме, она зависала из-за неправильного расчета размера экрана.</p><pre><code>// figure out the size of the screen in bytes screensize = vinfo.xres * vinfo.yres * vinfo.bits\_per\_pixel / 8; </code></pre><p>это должно быть:</p><pre><code>/* calculate the size of the screen in bytes */ screensize = vinfo.xres\_virtual * vinfo.yres\_virtual * (vinfo.bits\_per\_pixel / 8); </code></pre>