web-dev-qa-db-fra.com

Paint Pixels to Screen via Linux FrameBuffer

J'ai récemment été frappé par une idée curieuse de prendre les entrées de/dev/urandom, de convertir les caractères pertinents en entiers aléatoires et d'utiliser ces entiers comme valeurs rgb/x-y pour les pixels à peindre sur l'écran.

J'ai fait des recherches (ici sur StackOverflow et ailleurs) et beaucoup suggèrent que vous pouvez simplement écrire directement dans/dev/fb0 car il s'agit de la représentation du fichier du périphérique. Malheureusement, cela ne semble produire aucun résultat apparent.

J'ai trouvé un exemple de programme C issu d'un tutoriel QT (qui n'était plus disponible) et qui utilisait un mmap pour écrire dans le tampon. Le programme s'exécute avec succès, mais là encore, aucune sortie à l'écran. Chose intéressante, lorsque j'ai placé mon ordinateur portable dans Suspend et plus tard restauré, j'ai vu un flash momentané de l'image (un carré rouge) qui avait été écrit beaucoup plus tôt dans le framebuffer. L'écriture dans le framebuffer fonctionne-t-elle plus sous Linux pour peindre à l'écran? Idéalement, j'aimerais écrire un script (ba) sh, mais C ou similaire fonctionnerait également. Merci!

EDIT: Voici le programme exemple ... peut paraître familier aux vétérinaires.

#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;
}
34
Richard Martinez

Si vous utilisez X11, vous DEVEZ utiliser les API X11 pour dessiner à l'écran. Faire le tour du serveur X est très défectueux (et, souvent, comme vous l’avez vu, cela ne fonctionne pas). Cela peut également provoquer des pannes ou simplement une corruption générale de l'affichage.

Si vous voulez pouvoir exécuter n'importe où (console et sous X), regardez SDL ou GGI. Si vous ne vous souciez que de X11, vous pouvez utiliser GTK, QT ou même Xlib. Il y a beaucoup, beaucoup d'options ...

7
derobert

J'ai eu du succès avec les quelques expériences suivantes.

Commencez par rechercher si X utilise TrueColor RGB avec remplissage sur 32 bits (ou supposez simplement que tel est le cas). Recherchez ensuite si vous avez le droit d'écriture sur fb0 (et qu'il existe). Si elles sont vraies (et que de nombreux outils/ordinateurs de bureau/PC modernes pourraient les utiliser comme valeurs par défaut), vous devriez pouvoir effectuer les opérations suivantes (et si ces valeurs par défaut ne tiennent pas, vous aurez probablement encore du succès avec les tests suivants, bien que les détails puissent varier):

Test 1: ouvrez un terminal virtuel (sous X) et tapez: $ Echo "ddd ... ddd">/dev/fb0 Où le ... est en réalité un écran plein de d . Le résultat sera une ou plusieurs lignes de gris (partielles) en haut de votre écran, en fonction de la longueur de votre chaîne d'écho et de la résolution en pixels que vous avez activée. Vous pouvez également choisir toutes les lettres (les valeurs ascii sont toutes inférieures à 0x80, de sorte que la couleur produite sera un gris foncé .. et modifiez les lettres si vous souhaitez utiliser autre chose que le gris). Évidemment, ceci peut être généralisé à une boucle Shell ou vous pouvez utiliser un fichier volumineux pour voir l'effet plus clairement: par exemple: $ Cat /lib/libc.so.6>/dev/fb0 Afin de voir les vraies couleurs de certains partisans du FSF ;-P

Ne vous inquiétez pas si une grande partie de votre écran est écrasée. X a toujours le contrôle du pointeur de la souris et a toujours son idée de l'endroit où les fenêtres sont mappées. Tout ce que vous avez à faire est de saisir une fenêtre et de la faire glisser un peu pour supprimer le bruit.

Test 2: cat/dev/fb0> xxx Puis modifiez l’apparence de votre bureau (par exemple, ouvrez de nouvelles fenêtres et fermez d’autres) . Enfin, faites l’inverse: cat xxx>/dev/fb0 afin de récupérez votre ancien bureau!

Ha, bien, pas tout à fait. L'image de votre ancien bureau est une illusion, et vous vous en passerez rapidement lorsque vous ouvrez une fenêtre en plein écran.

Test 3: écrivez une petite application qui saisit une copie antérieure de/dev/fb0 et modifie les couleurs des pixels, par exemple, pour supprimer la composante rouge ou augmenter le bleu, ou inverser les couleurs rouge et verte, etc. pixels dans un nouveau fichier que vous pourrez consulter plus tard via la simple approche Shell du test 2. Notez également que vous aurez probablement affaire à des quantités BGRA de 4 octets par pixel. Cela signifie que vous souhaitez ignorer tous les 4 octets et traiter également le premier de chaque ensemble en tant que composant bleu. "ARGB" est big-endian, donc si vous visitez ces octets en augmentant l'index d'un tableau C, le bleu viendra en premier, puis le vert, puis le rouge .. c'est-à-dire, B-G-R-A (pas A-R-G-B).

Test 4: écrivez une application dans n'importe quelle langue qui effectue une boucle à la vitesse de la vidéo en envoyant une image non carrée (think xeyes) à une partie de l'écran afin de créer une animation sans aucune bordure de fenêtre. Pour des points supplémentaires, faites défiler l'animation sur tout l'écran. Assurez-vous de sauter un grand espace après avoir dessiné une petite rangée de pixels (pour compenser la largeur de l'écran qui est probablement beaucoup plus large que l'image animée).

Test 5: jouez un tour à un ami, par exemple, prolongez le test 4 de manière à ce qu'une image d'une personne animée apparaisse sur son bureau (peut-être vous filmer pour obtenir les données en pixels), puis vous dirigez vers l'un de leurs bureaux importants. dossiers, ramasse le dossier et le déchire, puis commence à rire de façon hystérique, puis fait sortir une boule de feu et engloutit tout leur bureau. Bien que tout cela soit une illusion, ils peuvent paniquer un peu .. mais utilisez cela comme une expérience d'apprentissage pour montrer Linux et l'open source et montrer à quel point c'est beaucoup plus effrayant pour un novice qu'il ne l'est réellement. [les "virus" sont généralement des illusions inoffensives sous Linux]

11
Jose_X

Je dirais qu'il faut faire attention avant d'écrire dans/dev/fb0, comme suggéré ci-dessus. Je l'ai essayé sous Xin Ubuntu 10.04 et a) rien ne s'est passé visuellement, b) il a détruit toutes les fenêtres de Shell, même les autres types, conduisant à des erreurs de noyau et à un manque de fonctionnalités.

2
David

Vous devriez utiliser fb_fix_screeninfo.smem_len pour screensize au lieu de faire la multiplication vous-même. Le tampon peut être aligné sur 4 octets ou autre chose. 

screensize = finfo.smem_len;
1
Alex Arsenault

si vous déboguez votre programme, vous trouverez la ligne:

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

screensize est 0. parce que vinfo.xres est 0 ..__, vous devez le changer en:

long ppc_fx = (((long)fixed_info.smem_start) - ((long) fixed_info.smem_start & ~(PAGE_SIZE-1)));
screensize = finfo.smem_len + ppc_fx;

depuis Linux 2.6.2? , les 2èmes arguments de mmap (), screensize, ne doivent pas être 0. sinon mmap () renverra MAP_FAILED.

0
Hsiang Chen

Je pense écrire un programme à base de framebuffer, juste parce que je dois pouvoir faire défiler (cascade SDR). Voir https://www.raspberrypi.org/forums/viewtopic.php?f=28&t=232493&p=1425567#p1425567 Le test de défilement que je considère comme réussi. Dans ces 2 structures que nous récupérons par ioctl, des informations sur la profondeur de couleur sont également disponibles. Vous semblez avoir basé votre programme sur le même exemple que moi. Comment obtenir une couleur de pixel à partir d'un framebuffer sous Linux (Raspberry Pi)

Le mien fonctionne bien sur mon Raspberry Pi, avec X ou non. Cela n'a aucun effet sur l'écran de mon ordinateur portable. Cela a un/dev/fb0, le programme s'exécute et les chiffres sont corrects, mais cela ne fait rien visuellement. Peut-être que c'est double tampon ou quelque chose.

Sous X, cela ne cause aucun dommage. Si vous faites glisser certaines fenêtres pour que tout soit redessiné, tout revient. Ensuite, j'ai décidé de sauvegarder ce qui était à l'écran et de le remettre à la fin, cela fonctionne aussi. Une fenêtre que j'ai déplacée et mise en place fonctionne comme si je ne l'avais jamais touchée. X ne réalise pas que je me suis planté avec le tampon d'écran, il sait ce qu'il a mis là et enregistre les clics de souris en conséquence. Si je déplaçais une fenêtre sans la remettre en place, les clics fonctionneraient quand même.

0
Alan Corey

Lorsque j'ai utilisé ce programme pour écrire en plein écran, il s'était écrasé, le calcul de la taille de l'écran étant erroné.

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

Ceci est supposé être:

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