#include <stdio.h>
#include <math.h>

#include <X11/Xlib.h>

/* Les ecrans (screen) et les displays sont des arguments de presque toutes les
   fonctions Xlib. C'est plus simple de les declarer en variables globales
   Les mettre en extern, si on decoupe l'application en plusieurs modules. */

/* Declarer une variable du type Display */
Display *display;

/* Specifier le nom du serveur X auquel on veut se connecter NULL
   signifie que l'on veut se connecter sur le serveur precise dans la
   variable d'environnement DISPLAY */
char *display_name=NULL;            

/* Declarer une variable contenant le numero d'ecran (screen) */
int screen;
int root;
int depth;
Visual *visual;
Colormap cmap_defaut;    /* La colormap par defaut */
Colormap cmap_privee;   /* La colormap privee */

int black, white;

/*-----*/
main()
/*-----*/
{
  Window win;                          /* descripteur de la fenetre */

  int display_width, display_height;   /* taille de l'ecran (root window) */
  int width, height;                   /* taille de la fenetre */
  unsigned int border_width=4;         /* largeur de la bordure */
  int x, y;                       /* position de la fenetre */

  XEvent report;                      /* structure des evenements */

  GC gc;                              /* ID du contexte graphique */

  XSetWindowAttributes WAttribs;      /* Attributs de la fenetre */
  XWindowAttributes attr;
  int WMask;                          /* masque d'attributs */

  /* Ouvrir une connection avec le serveur X a l'aide de la fonction
     XOpenDisplay(). Nous allons egalement traiter le cas ou la
     connexion n'a pu etre realisee en affichant un message d'erreur
     du type "pas de connexion avec le serveur <nom du
     serveur>. Attention, comme nous avons initialise le nom du
     serveur a NULL, le nom du serveur sera obtenu a l'aide de la
     fonction XDisplayName() */
  
  if((display=XOpenDisplay(display_name))==NULL) {
    fprintf(stderr,"fenetre de base: ne peut pas se connecter ");
    fprintf(stderr,"au serveur %s\n",XDisplayName(display_name));
    exit(-1);
  }

  /* Recuperer les informations concernant l'ecran (root window) sur
     lequel on va afficher notre application. Le numero de l'ecran
     par defaut s'obtient a l'aide de la macro DefaultScreen(), la
     taille a l'aide des macros DisplayWidth() et
     DisplayHeight(). Il existe de nombreuses macros de ce type.*/
    
  screen         = DefaultScreen(display);
  display_width  = DisplayWidth(display,screen);
  display_height = DisplayHeight(display,screen);
  root           = RootWindow(display, screen);
  visual         = DefaultVisual(display, screen);
  depth          = DefaultDepth(display, screen);
  gc             = DefaultGC(display, screen);
  black          = BlackPixel(display, screen);
  white          = WhitePixel(display, screen);

  /* Initialiser position et la taille de la fenetre en fonction de la
     taille de l'ecran */
  x = 0; 
  y = 0; 
  width = display_width/3;
  height = display_height/4;

  WAttribs.border_pixel     = 0;
  WAttribs.event_mask       = KeyPressMask| ButtonPressMask | 
                              PointerMotionMask | ExposureMask | 
                              StructureNotifyMask;
  WAttribs.background_pixel = black;
	        
	
  WMask = CWBackPixel | CWBorderPixel | CWEventMask;

  win=XCreateWindow(display, root, x, y, width, height, border_width,
		    CopyFromParent, /* depth */
		    CopyFromParent, /* classe de la fenetre, InputOutput */
		    CopyFromParent, /* visual */
		    WMask,          /* masque d'attributs */
		    &WAttribs);     /* les autres attributs */


  installe_colormap_defaut(win);

  /* Affichage de la fenetre */
  XMapWindow(display,win);

							

  /* gestion des evenements:
     -prend un evenement
     -utilise le premier expose pour afficher texte et graphiques
     -utilise ConfigureNotify pour reafficher la fenetre a la taille
     voulue.
     -utilise ButtonPress ou KeyPress pour sortir du programme */

  while(1) {
    XNextEvent(display, &report);
    switch (report.type) {
    case Expose:
      /* on purge la queue des evenements de tous les Expose, pour
	 ne redessiner la fenetre qu'une seule fois */
  
      while(XCheckTypedEvent(display, Expose, &report));
      affiche_colormap(win, gc);
      break;
    case ConfigureNotify:
      /* La fenetre a ete resizee. On met a jour les variables width
	 et height pour que les routines draw_text et draw_graphics
	 reaffichent correctement la fenetre, lors du prochain Expose */
       
      width  = report.xconfigure.width;
      height = report.xconfigure.height;
      affiche_colormap(win, gc);
      break;
    case ButtonPress:
      printf("Bouton %d enfonce\n", report.xbutton.button);
      draw_rectangle(report.xbutton.window, gc, report.xbutton.x, report.xbutton.y);

      break;
    case KeyPress:
      {
	char buffer;
	int bufsize=1;
	int charcount;

	charcount = XLookupString(&report, &buffer, bufsize, NULL, 
				  NULL);
	printf("Touche %c appuyee\n", buffer);

	switch (buffer) {
	case 'd':
	  installe_colormap_defaut(win);
	  affiche_colormap(win, gc);
	  break;
	case 'p':
	  installe_colormap_privee(win);
	  affiche_colormap(win, gc);
	  break;
	case 'q':
	  XCloseDisplay(display);
	  exit(1);
	  break;
	}
      }
      break;
    case MotionNotify:
      break;
    default:
      /* tous les evenements selectionnes par StructureNotifyMask 
	 (DestroyNotify, GravityNotify, etc...) sont filtres ici. */
      break;
    } 
  } 
} 

/*-----------------------*/
affiche_colormap(win, gc)
/*-----------------------*/
Window win;
GC gc;
/* Recuperer la colormap de la fenetre, afficher les valeurs RGB en 8 bits,
   faire flasher la fenetre avec les couleurs */
{
  XWindowAttributes attr;
  Colormap cmap;
  long i, j;
  XColor Col;
  int indice_case_x, indice_case_y;
  Window root_r;
  int depth_r, border_r;
  int x, y, width, height, step_x, step_y;

  /* effacement de la fenetre */
  /*  XClearArea (display, win, 0, 0, 0, 0, False);*/

  /* recuperation des attributs de la fenetre */
  XGetWindowAttributes(display, win , &attr);

  /* Mais c'est surtout la colormap qui nous interesse ! */
  cmap = attr.colormap; 	      /* c'est un identifieur d'une colormap 
					 stockee dans le serveur */

  /* On recupere la taille et la position de la fenetre */
  XGetGeometry(display, win, &root_r, &x, &y, &width, &height, 
	       &border_r, &depth_r);

  step_x = width / 16;
  step_y = height / 16;

    /*********** Afficher le contenu de la colormap *************/
       	
  Col.flags = DoRed | DoGreen | DoBlue;  /* on specifie ce qu'on veut comme 
					    champs */

  for (i=0; i < 256; i++) {		   /* Nb couleurs donne par visual 
					      (on verra plus tard) */
    Col.pixel=i;
    XQueryColor(display, cmap, &Col);

    /* Affichage sur 8 bits au lieu de 16 */
/*    printf("Couleur %d Valeur:r %d g %d b %d\n", i, 
	   Col.red >> 8,
	   Col.green >> 8,
	   Col.blue >> 8);*/

    indice_case_y = i / 16;
    indice_case_x = i % 16;

    XSetForeground(display, gc, Col.pixel);
    XFillRectangle(display, win, gc, 
		   indice_case_x*step_x, indice_case_y*step_y,
		   step_x, step_y);
    XFlush(display);
  }
}


/*----------------*/
installe_colormap_defaut(win)
/*----------------*/
Window win;
/* Creation d'une fenetre avec colormap privee */
{
  XSetWindowAttributes WAttribs;      /* Attributs de la fenetre */
  XWindowAttributes attr;
  int WMask;                          /* masque d'attributs */
  static int colormap_defaut_memorisee = 0;

  if(!colormap_defaut_memorisee) {
    colormap_defaut_memorisee = 1;

    /* recuperation des attributs de la fenetre */
    XGetWindowAttributes(display, win , &attr);

    /* Mais c'est surtout la colormap qui nous interesse ! */
    cmap_defaut = attr.colormap;     /* c'est un identifieur d'une colormap 
				      stockee dans le serveur */
  }

  WAttribs.colormap=cmap_defaut;
  WMask= CWColormap;
	
  XChangeWindowAttributes(display, win, WMask, &WAttribs);	
}

/*----------------*/
installe_colormap_privee(win)
/*----------------*/
Window win;
/* Creation d'une fenetre avec colormap privee */
{
  XSetWindowAttributes WAttribs;
  unsigned long WMask;
  long i;
  XColor Col;
  static colormap_privee_cree=0;

  if(!colormap_privee_cree) {
    /* On alloue et on initialise la colormap */
    colormap_privee_cree = 1;

    /* attention a l'argument win (uniquement pour designer l'ecran) */
    cmap_privee = XCreateColormap(display, win, visual, AllocAll);	


    /****** Charger une palette de gris *********/
	
	
	
    for (i=0;i<256;i++)
      {
	Col.pixel = (unsigned long) i;
	Col.red   = (unsigned short) i << 8 ;
	Col.green = (unsigned short) i << 8 ;
	Col.blue  = (unsigned short) i << 8 ;
	Col.flags = DoRed | DoGreen | DoBlue;

	XStoreColor(display, cmap_privee, &Col);
      }
  }

  WAttribs.colormap=cmap_privee;
  WMask= CWColormap;
	
  /* la win a une colormap privee */
  XChangeWindowAttributes(display, win, WMask, &WAttribs);	

}

/*----------------------*/
get_GC(win, gc)
/*----------------------*/
Window win;
GC *gc;
{
  unsigned long valuemask = 0;                /* Ignore XGCvalues et prend les 
					         valeurs par defaut */
  XGCValues values;

  /* creation d'un contexte graphique par defaut */
  *gc = XCreateGC(display, win, valuemask, &values);


  /* specification d'un background noir, puisque par defaut on fait du
     blanc sur du blanc */
  XSetForeground(display, *gc, BlackPixel(display, screen));
}

/*-----------------------*/
draw_rectangle(win, gc, x, y)
/*-----------------------*/
Window win;
GC gc;
int x, y;
{
  XDrawRectangle(display, win, gc, x, y, 50, 50);
}

