/*
  fb_test.c
  affiche le rectangle au milieu de l'écran
  en utilisant memset()/ioctl()/mmap()

  compilation :
    gcc -Wall -Os -o fb_test fb_test.c

  exécution :
    fb_test [/dev/fbX]
*/

/* dimensions du rectangle */
#define RECT_L  200
#define RECT_H  150

#ifndef COULEUR
#define COULEUR 15 /* blanc par défaut */
#endif

/* Quelques inclusions indispensables : */
#include <fcntl.h>     /* open() */
#include <linux/fb.h>  /* fb_***_screeninfo */
#include <stdio.h>     /* printf(), perror() */
#include <stdlib.h>    /* exit() */
#include <sys/ioctl.h> /* ioctl() */
#include <sys/mman.h>  /* mmap() */
#include <sys/stat.h>  /* open() */
#include <string.h>    /* memset */

char *fb_name="/dev/fb0"; /* nom par défaut */
int fb_fd;  /* descripteur de fichier du framebuffer */
/* structures fournies par les ioctl() : */
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;
long int screensize = 0;
char *fbp = 0; /* pointeur vers la mémoire vidéo */

/* routine générique pour initialiser le framebuffer : */
void init_fb(char *fb_name) {

  /* Ouverture du framebuffer en lecture/écriture */
  fb_fd = open(fb_name, O_RDWR);
  if (!fb_fd) {
    perror("\nfopen : ouverture du framebuffer impossible\n");
    exit(1);
  }

  /* Lecture des information "fixes" de l'écran */
  if (ioctl(fb_fd, FBIOGET_FSCREENINFO, &finfo)) {
    perror("FBIOGET_FSCREENINFO :\
      erreur à la lecture des informations fixes.\n");
    exit(2);
  }

  /* Lecture des information "variables" de l'écran */
  if (ioctl(fb_fd, FBIOGET_VSCREENINFO, &vinfo)) {
    perror("FBIOGET_VSCREENINFO :\
      erreur à la lecture des informations variables.\n");
    exit(3);
  }

  if (vinfo.bits_per_pixel != 8) {
    printf("Erreur : l'écran n'est pas en mode 8bpp\n");
    exit(4);
  }

#ifndef NO_DEBUG_MSG
  printf("Résolution actuelle :\n\
    %dx%d, %d bits par pixel\n",
  vinfo.xres, vinfo.yres, vinfo.bits_per_pixel );
#endif

  /* calcule la taille de la zone affichable en octets */
  screensize = (vinfo.xres * vinfo.yres
              * vinfo.bits_per_pixel) >> 3;

  /* projection du framebuffer en mémoire utilisateur */
  fbp = (char *)mmap(0, screensize,
    PROT_READ|PROT_WRITE, MAP_SHARED, fb_fd, 0);
  if ((int)fbp == -1) {
    perror("échec de mmap() du framebuffer.\n");
    exit(5);
  }
}

/* le programme principal */
int main (int argc, char **argv) {
  int i;
  char *start;

  /* sélectionne le fichier du framebuffer */
  if (argc > 2)
    fb_name = argv[1];

  /* appelle la fonction d'initialisation */
  init_fb(fb_name);

  /* adresse du coin haut/gauche du rectangle
       (calcul très factorisé) */
  start = fbp +
   (((vinfo.xres * (1+vinfo.yres-RECT_H))
      - RECT_L) >> 1);

  /* affiche le rectangle */
  for (i=0; i < RECT_H; i++) {
    start+=vinfo.xres;
    memset(start, COULEUR, RECT_L);
  }

  return EXIT_SUCCESS;
}
