Confessioni About Copertina Yacc & Lex

Articoli


Programmazione sotto X con X-Lib

Un esempio di sorgente C che esemplifica le funzioni base per programmare sotto X con le librerie Xlib.

Devo premettere che non sono un esperto nè di C e C++, nè tantomeno di Xlib, tutto quello che so viene da esperienza personale. Ho scritto un semplice programma, appwin.c, che mi accingo a descrivere:


Codice introduttivo

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
#include <X11/Xproto.h>
Header delle librerie Xlib
#include <stdio.h>
#include <stdlib.h>
   
#include "logo.xbm"
Questo è un semplice file xbm che serve per definire l' icona.
void inializzazione();
void disegna();
int Controllo_degli_eventi();

#define TRUE (1)
#define FALSE (0)
Prototipi di funzioni e di void.
Display *display;
GC gc1,
   gc2;
Window window,
       parent,
       root;
int depth,
    screen,
    visibility;
Queste sono le definizioni delle variabili che poi servono per le Xlib.
char *host = NULL;
int windowwidth = 400,
    windowheight = 300,
    window_x_pos = 200,
    window_y_pos = 200;
int col1=1,
    col2=4,
    col3=7,
    col4=9;
int tmp1,
    tmp2,
    tmp3,
    tmp4;
 

Funzione main

main(int argc, char *argv[])
{
 
  if (host == NULL)
    if ((host = (char *) getenv("DISPLAY")) == NULL)
      {
       fprintf(stderr, "%s", "Error: No environment variable DISPLAY\n");
      }
Questo if controlla il valore della variabile d'ambiente DISPLAY.
  inizializzazione();
 
  gc1 = XCreateGC(display, window, 0, NULL);
  gc2 = XCreateGC(display, window, 0, NULL);
Creazione di un graphic context.
  XSetForeground(display, gc1, BlackPixel(display, screen));
  disegna();
}
 

Inizializzazione

void inializzazione()
{
  XSetWindowAttributes attr;
  char *naAme;
  Pixmap iconPixmap;
  XWMHints xwmhints;

  if((display = XOpenDisplay(host)) == NULL)
    {
     fprintf(stderr,"Error: Connection could not be made.\n");
     exit(1);
    }
In questa void si settano alcune propietà della finestra. Ad esempio: su che display la si deve creare, qual'è il suo nome, la dimensione, eccetera.
  screen = DefaultScreen(display);
  root = parent = RootWindow(display,screen);
Vede il numero del display su cui state mandando la finestra (per capirci: quando usate il comando export DISPLAY=display:0.x, x è il valore di screen che in genere è zero).
  depth = DefaultDepth(display,screen);
Serve per vedere quanti planes usa il terminale dove state mandando la finestra.
  XSelectInput(display,root,SubstructureNotifyMask);

  attr.event_mask = StructureNotifyMask | SubstructureNotifyMask | VisibilityChangeMask | ExposureMask;

  attr.background_pixel = BlackPixel(display,screen);
Queste funzioni sappiate solo che esistono.
window = XCreateWindow(display, root, window_x_pos, window_y_pos, windowwidth, windowheight, 0, depth, InputOutput, DefaultVisual(display,screen), CWEventMask|CWBackPixel, &attr); Qui veniamo alla creazione della finestra, anche se non fisicamente, poichè dopo bisogna "mapparla" sul display con la funzione XMapWindow().
  name = "Appwin";
  XChangeProperty(display, window, XA_WM_NAME, XA_STRING, 8, PropModeReplace, name, strlen(name));
  XMapWindow(display,window);
Con la funzione XChangeProperty si modificano le propietà della finestra, in questo caso il nome dell'icona e della finestra.
  iconPixmap = XCreateBitmapFromData(display, window, logo_bits,logo_width,logo_height);

  xwmhints.icon_pixmap = iconPixmap;
  xwmhints.initial_state = NormalState;
  xwmhints.flags = IconPixmapHint | StateHint;
  
  XSetWMHints(display, window, &xwmhints );
Creazione dell 'icona dal file logo.xbm

N.B. I file .xbm sono dei semplici file testo in C, nelle cui prime righe vengono definite le variabili logo_height eccetera. In genere se salvate un file con il nome pluto in formato xbm, avrete come variabili nelle prime linee pluto_height ... Quindi se volete cambiare icona non vi resta che provare.

  XClearWindow(display,window);
  XSync(display, 0);
}
 

Funzione disegna

void disegna()
{
In questa void si disegna un rettangolo, 4 quadrati che si invertono i colori, ed una serie di linee.
  while(TRUE)
    {
     Controllo_degli_eventi();
Il while ci vuole altrimenti vedreste la finestra per un tempo piccolissimo.
     XSetForeground(display, gc2, 1);
Disegna delle linee con il colore definito da XSetForeground, dove il colore si definisce con il terzo argomento (che deve essere un intero) della funzione.
     XDrawLine(display,window,gc2,10,20,300,20);
Disegna una linea dal punto (10,20) a (300,20).
     XSetForeground(display, gc2, 2);
     XDrawLine(display, window, gc2, 300, 20, 300, 200);
     XSetForeground(display, gc2, 9);
     XFillRectangle(display, window, gc2, 254, 102, 45, 99);
Disegna un rettangolo dal punto (254,102) con 45 di larghezza e 99 di altezza.
     tmp1=col1;
     tmp2=col2;
     tmp3=col3;
     tmp4=col4;

     col1=tmp3;
     col2=tmp1;
     col3=tmp4;
     col4=tmp2;

     XSetForeground(display, gc2, col1);
     XFillRectangle(display,window,gc2,30,160,50,50);

     XSetForeground(display, gc2, col2);
     XFillRectangle(display,window,gc2,81,160,50,50);

     XSetForeground(display, gc2, col3);
     XFillRectangle(display,window,gc2,30,211,50,50);

     XSetForeground(display, gc2, col4);
     XFillRectangle(display,window,gc2,81,211,50,50);
 
     usleep(500000);
    }
}
Serve per ritardare la rotazione dei colori altrimenti non si vede niente (per essere precisi si vede il bianco, come la fisica cerca di insegnare).

N.B. Vorrei far notare si può sfruttare le potenzialità del C++ in ambiente grafico senza complicarsi la vita. Infatti definendo una classe rettangolo, si può usare per avere (derivando) un rettangolo con scritta, un bottone che è un rettangolo "attivo" e così altri classi generiche da cui poi derivare. Con questo non voglio dire che per programmare sotto X bisogna usare il C++, infatti questo programma è scritto in C, ma può essere utile per non incasinarsi se si scrive un programma molto complesso.


Controllo degli eventi

int Controllo_degli_eventi()
{
  XEvent event;
  int block = FALSE;
  int status = 0;

  while((XPending(display) > 0) || (block == TRUE))
    {
     XNextEvent(display,&event);

     switch(event.type)
       {
        case ReparentNotify:
          if(event.xreparent.window != window )
            break;
          XSelectInput(display, event.xreparent.parent, StructureNotifyMask);
          XSelectInput(display,parent,0);
          parent = event.xreparent.parent;
          break;

        case UnmapNotify:
          if ((event.xunmap.window != window) && (event.xunmap.window != parent))
            break;
          block = TRUE;
          break;

        case VisibilityNotify:
          if (event.xvisibility.window != window)
            break;
          if (event.xvisibility.state == VisibilityFullyObscured)
            {
             block = TRUE;
             break;
            }
          if ((event.xvisibility.state == VisibilityUnobscured) && (visibility == 1))
            {
             visibility = 0;
             block = FALSE;
             break;
            }
          if (event.xvisibility.state == VisibilityPartiallyObscured)
            {
             visibility = 1;
             block = FALSE;
            }
          break;

        case Expose:
          block = FALSE;
          break;

        case MapNotify:
          if ((event.xmap.window != window) && (event.xmap.window != parent))
            break;
          block = FALSE;
          break;

        case ConfigureNotify:
          if (event.xconfigure.window != window)
            break;
          if ((windowwidth == event.xconfigure.width) && (windowheight == event.xconfigure.height))
            break;
          windowwidth = event.xconfigure.width;
          windowheight = event.xconfigure.height;
          XClearWindow(display, window);
          block = FALSE;
          status = 1;
          break;

        default:
          break;
       }
    }
  return(status);
}
Questa è la funzione più complicata. Bisogna dire che X gestisce degli eventi che possono essere di vario tipo. Non mi permetto di descrivere tutti i tipi di eventi che X può gestire o generare, ma se volete vedere quando un determinato evento si verifica potete usare il programma xev. Prendete questa funzione per buona, magari ne parlerò in un prossimo articolo.

di Gerardo Di Iorio


Confessioni About Copertina Yacc & Lex