XFORMS - THE FORMS LIBRARY

Toolkit per la costruzione di interfacce grafiche (GUI) nel sistema X Window.

Principali vantaggi:

A differenza di altri toolkit (per es. Motif), nasconde completamente X al programmatore: non e' necessario conoscere X per programmare in XForms.

   +---------------+         +---------------+
   |Applicazione   |         |Applicazione   |
   +----------+--+-+         +---------------+
   |Motif     |  | |         |               |
   +----------+  | |         |XForms         |
   |Xt intrinsics| |         |               |
   +-------------+ |         +---------------+
   |Xlib           |         |Xlib           |
   +---------------+         +---------------+ 
Concetti base (Terminologia): XForms non adopera il termine "widget".

Classi di oggetti

Oggetti statici

Non hanno capacita' di interazione, servono solo per visualizzare.

Bottoni

Caratterizzati da interazione col mouse tramite click.

Quantificatori

Interazione col mouse mediante click o trascinamento, permettono di introdurre un valore numerico.

Campi di input

Attivazione col mouse, input da tastiera.

Selezionatori

Interazione col mouse tramite click, permettono di immettere una scelta tra un insieme predefinito.

Altri oggetti

Struttura di un'applicazione in XForms

Un programma applicativo che utilizza XForms deve contenere le seguenti parti: Nota: XForms si pone sempre come "cuscinetto" tra applicazione e Xlib, Xlib rimane trasparente al programmatore.

Vediamo le funzioni di XForms relative alle 4 fasi.

NOTA: i programmi forniti in esempio si compilano con il makefile digitando

make PROG=nomefile.c
per generare l'eseguibile nomefile.

Inizializzazione

fl_initialize (&argc, argv, titolo, 0, 0);
argc, argv = argomenti passati al main da command line
titolo = titolo dell'applicazione (char *)

Definizione delle form

FL_FORM * fl_bgn_form(tipo, w, h);
void fl_end_form();
Delimitano la definizione di una form. Tra fl_bgn_form e fl_end_form trovano posto le definizioni di tutti gli oggetti contenuti nella form.

tipo = tipo di form (costante simbolica), si riferisce all'apparenza del rettangolo che costituisce la finestra, es: FL_UP_BOX (in rilievo), FL_DOWN_BOX (rientrante), FL_BORDER_BOX, FL_FLAT_BOX...
w, h = larghezza e altezza (in pixel)

FL_OBJECT * fl_add_XXXX(tipo, x,y, w,h, label);
Posto fra fl_bgn_form e fl_end_form, definisce un oggetto che viene aggiunto alla form corrente.

XXXX = classe dell'oggetto (es: bottone, slider...)
tipo = tipo di oggetto all'interno della sua classe, di solito si riferisce ad una caratteristica di aspetto o comportamento particolare
x,y = posizione del punto di aggancio dell'oggetto (angolo in alto a sinistra) nel sistema di coordinate della form
w,h = larghezza e altezza dell'oggetto in pixel

Associazione di callback

Ogni oggetto e' sensibile a certi eventi predefiniti, dipendenti dalla classe di oggetto.

Una callback e' una procedura del programma applicativo che viene associata ad un oggetto in fase di definizione e che viene eseguita automaticamente da XForms ogni volta che in tale oggetto si genera un evento.

Un oggetto ha una sola callback per tutti gli eventi a cui e' sensibile. La procedura callback deve interpretare l'evento che si e' verificato interrogando l'oggetto che lo ha prodotto con apposite funzioni, in modo da poter decidere che cosa fare.

Definire una funzione di callback

Una funzione di callback deve essere dichiarata del tipo:
f(FL_OBJECT * obj, long arg);
obj = sara' l'oggetto dove si e' generato l'evento (oggetto che avra' questa funzione come sua callback )
arg = valore numerico deciso dal programmatore quando associa la callback all'oggetto

Associarla ad un oggetto

fl_set_object_callback(FL_OBJECT * obj, function, long arg);
obj = l'oggetto a cui la associo (ritornato da fl_add_XXXX quando ho aggiunto l'oggetto alla form)
function = la callback
arg = valore numerico che verra' passato dal sistema in argomento alla callback.

Il valore di arg viene usato quando la stessa funzione e' associata come callback ad oggetti diversi. Nel corpo della funzione uso il valore di arg per discriminare.

La callback di un oggetto puo' essere cambiata in qualsiasi momento.

Chiamata della callback

La callback di un oggetto e' chiamata da XForms nel ciclo degli eventi, quando si verifica un evento in tale oggetto.

Quando il sistema chiama la callback, le passa in argomento l'oggetto dove l'evento e' accaduto e il valore di arg che ho specificato al momento di associare la callback all'oggetto.

Posso anche chiamare una callback "artificialmente" da programma, senza che nessun evento accada.

fl_call_object_callback(FL_OBJECT *); Invoca la callback di un oggetto.

Visualizzazione delle form

fl_show_form(FL_FORM * form, place, border, title);
form = la form da visualizzare (ritornata da fl_bgn_form quando ho definito la form)
place = dove deve essere posizionata di preferenza la form (costante simbolica), es: FL_PLACE_FREE, FL_PLACE_MOUSE, FL_PLACE_FULLSCREEEN, FL_PLACE_ICONIC...
border = che tipo di bordo deve essere aggiunto di preferenza alla form (costante simbolica)
title = titolo della form, che apparira' come titolo della finestra

Ciclo degli eventi (Main loop)

FL_OBJECT * fl_do_forms();
Implementa il ciclo degli eventi con tipo di interazione bloccante.

Esempio di programma senza callback

Vedere file yesno.c.
Crea una form con un titolo testuale "Do you want to quit?" e due bottoni "Yes" e "No". Visualizza la form, poi chiama fl_do_forms. Quando uno dei due bottoni viene premuto, fl_do_forms esce e lo ritorna. Se il bottone premuto e' "Yes", il programma termina, altrimenti chiama nuovamente fl_do_forms, e cosi' via.
#include "forms.h"

FL_FORM *form;
FL_OBJECT *yes, *no, *but;

main (int argc, char *argv[])
{
  /*** Inizializzazione ***/
  fl_initialize ( &argc, argv, "FormDemo", 0, 0);
  /*** Definizione forms ***/
  form = fl_bgn_form(FL_UP_BOX, 320, 120);
  /* crea form con aspetto di box tridimensionale sporgente da schermo,
     larga 320 pixel e alta 120 */
  fl_add_box(FL_NO_BOX, 160, 40, 0, 0, "Do you want to quit?");
  /* aggiunge alla form che sto definendo un box contenente la
     scritta "Do you want to quit?" */
  yes = fl_add_button(FL_NORMAL_BUTTON, 40, 70, 80, 30, "Yes");
  /* aggiunge alla form un bottone di etichetta "Yes", posizionato con 
     l'angolo in alto a sinistra nel punto 40,70 della form, largo 80 
     pixel e alto 30 */
  no = fl_add_button(FL_NORMAL_BUTTON, 200, 70, 80, 30, "No");
  /* analogo per bottone "No" */
  fl_end_form();
  /*** Visualizzazione forms ***/
  fl_show_form(form, FL_PLACE_MOUSE,FL_TRANSIENT,"Question");
  /* FL_TRANSIENT causa un tipo di bordo adatto a finestre che
     devono stare visualizzate per poco tempo */
  /*** Main loop ***/
  while ( (but = fl_do_forms()) != yes ) ;
  /* fintanto che il bottone cliccato non e' yes, continua a ciclare */
  /*** Terminazione ***/
  fl_hide_form(form);
  return 0;
}

Stesso esempio riscritto usando callback

Vedere file yncallbk.c.
Crea una form come prima. Entrambi i bottoni hanno callback: quella di "Yes" termina il programma, quella di "No" non fa nulla. Il programma visualizza la form, poi chiama fl_do_forms. Quando uno dei due bottoni viene premuto, fl_do_forms chiama la callback corrispondente.
#include "forms.h"

FL_FORM *form;

void yes_callback(FL_OBJECT *ob, long user_data)
{
  printf("Yes is pushed\n");
  fl_finish();
  exit(0);
}

void no_callback(FL_OBJECT *ob, long user_data)
{
  printf("No is pushed\n");
}

main (int argc, char *argv[])
{
  FL_OBJECT *obj;
  /*** Inizializzazione ***/
  fl_initialize ( &argc, argv, "FormDemo", 0, 0);
  /*** Definizione forms ***/
  form = fl_bgn_form(FL_UP_BOX, 320, 120);
  /* crea form con aspetto di box tridimensionale sporgente da schermo,
     larga 320 pixel e alta 120 */
  fl_add_box(FL_NO_BOX, 160, 40, 0, 0, "Do you want to quit?");
  /* aggiunge alla form che sto definendo un box contenente la
     scritta "Do you want to quit?" */
  obj = fl_add_button(FL_NORMAL_BUTTON, 40, 70, 80, 30, "Yes");
  /* aggiunge alla form un bottone di etichetta "Yes", posizionato con 
     l'angolo in alto a sinistra nel punto 40,70 della form, largo 80 
     pixel e alto 30 */
  fl_set_object_callback(obj, yes_callback, 0);
  /* assegna al bottone "Yes" la callback */
  obj = fl_add_button(FL_NORMAL_BUTTON, 200, 70, 80, 30, "No");
  fl_set_object_callback(obj, no_callback, 0);
  /* analogo per bottone "No" */
  fl_end_form();
  /*** Visualizzazione forms ***/
  fl_show_form(form, FL_PLACE_MOUSE,FL_TRANSIENT,"Question");
  /* FL_TRANSIENT causa un tipo di bordo adatto a finestre che
     devono stare visualizzate per poco tempo */
  /*** Main loop ***/
  fl_do_forms();
  /* poiche' ogni oggetto capace di catturare eventi ha una callback,
     fl_do_forms() non esce mai, la terminazione del programma avviene
     dentro la callback di "Yes" */
}

Schemi di programma nelle due modalita'

Interazione non bloccante

Serve nei casi in cui l'applicazione deve fare qualcosa anche in assenza di eventi o tra un evento e l'altro (es: animazione grafica).
FL_OBJECT * fl_check_forms();
Guarda se si e' verificato un evento in un oggetto. Da usare come alternativa a fl_do_forms solo se veramente necessario (consuma CPU).

Aggiungere oggetti ad una form

Questa parte copre le classi principali di oggetti XForms. E' solo un'idea di quello che c'e', per dettagli vedere il manuale.

Oggetti statici

Box

Riquadro con etichetta all'interno.
FL_OBJECT * fl_add_box(tipo, x,y, w,h, label);
tipo = uno fra FL_UP_BOX, FL_DOWN_BOX, FL_FLAT_BOX, FL_SHADOW_BOX...

Frame

"Cornice", riquadro vuoto con etichetta sul bordo.
FL_OBJECT * fl_add_frame(tipo, x,y, w,h, label);
tipo = uno fra FL_NO_FRAME, FL_UP_FRAME, FL_DOWN_FRAME, FL_BORDER_FRAME, FL_ENGRAVED_FRAME...

Text

Semplice etichetta senza decorazioni, di default allineata a sinistra in un box invisibile.
FL_OBJECT * fl_add_text(tipo, x,y, w,h, label);
tipo = sempre FL_NORMAL_TEXT

Bottoni

Si differenziano per aspetto (controllato dalla classe) e comportamento (controllato dal tipo).
FL_OBJECT * fl_add_XXXXbutton(tipo, x,y, w,h, label);
Aggiunge bottone di classe XXXX e di tipo tipo.

Classi di bottoni:
button, roundbutton, checkbutton, lightbutton...


Tipi di bottoni:

Alcune funzioni utili sui bottoni

Gruppi di oggetti

FL_OBJECT * fl_bgn_group();
FL_OBJECT * fl_end_group();
Delimitano la definizione di oggetti che sono visti come un unico oggetto.

Utile per poter assegnare attributi, attivare/disattivare, nascondere/mostrare tutto il gruppo in una istruzione sola.

E' vietato annidare le definizioni di gruppi.

Esiste un tipo di bottone, FL_RADIO_BUTTON, dove la pressione di un bottone del gruppo provoca automaticamente il rilascio di tutti gli altri bottoni del gruppo. Gruppi di bottoni FL_RADIO_BUTTON servono a implementare scelte mutuamente esclusive.

Quantificatori

Slider

Barra con cursore mobile. La barra corrisponde ad un intervallo di valori (per default tra 0 e 1), il cursore ad un valore corrente nell'intervallo (per default inizializzato a 0).
FL_OBJECT * fl_add_slider(tipo, x,y, w,h, label);
FL_OBJECT * fl_add_valslider(tipo, x,y, w,h, label);
Nella seconda variante lo slider ha accanto un'etichetta che mostra il valore corrente.

tipo = definisce direzione (orizzontale, verticale) della barra e aspetto del cursore: FL_VERT_SLIDER, FL_HOR_SLIDER, FL_VERT_FILL_SLIDER, FL_HOR_FILL_SLIDER, FL_VERT_NICE_SLIDER, FL_HOR_NICE_SLIDER

Restituzione di eventi da slider

Per default, lo slider cattura un evento ogni volta che l'utente muove il cursore. Per cambiare politica si usa:
void fl_set_slider_return(FL_OBJECT *, int);
Possibili valori del parametro intero: FL_RETURN_CHANGED (ogni volta che il cursore si sposta), FL_RETURN_END_CHANGED (quando l'utente rilascia il cursore dopo averlo spostato) ...

Alcune funzioni utili sugli slider

Esempio: File demo05.c.
Usa interazione semplice.
Visualizza una form contenente uno slider, un box la cui etichetta mostra il valore corrente dello slider (aggiornato da programma ogni volta che l'utente interagisce con lo slider), e un bottone "Exit".

Dial

Analogo allo slider ma circolare con una lancetta rotante. Le funzioni sui dial sono simili a quelle sugli slider (vedere manuale).

Positioner

Svolge la funzione di uno slider bidimensionale. Permette di inserire una coppia di coordinate (x,y). Si presenta come un riquadro con una retta orizzontale ed una verticale che si incrociano. L'incrocio delle due rette denota il valore corrente.

Le funzioni sui positioner sono analoghe a quelle sugli slider ma doppie (per x e per y, vedere manuale).

Counter

Mostra il valore corrente in un riquadro e frecce per modificare tale valore in crescendo e in diminuendo.

FL_OBJECT * fl_add_counter(tipo, x,y, w,h, label);
tipo = uno fra FL_NORMAL_COUNTER (quattro frecce, per ciascuna direzione una freccia con passo piccolo ed una con passo grande) e FL_SIMPLE_COUNTER (solo due frecce).

Alcune funzioni utili sui counter

Input Fields

Riquadro contenente testo, che puo' essere editato usando la tastiera.

FL_OBJECT * fl_add_input(tipo, x,y, w,h, label);
tipo = FL_NORMAL_INPUT...

L'etichetta viene mostrata a lato del campo di inserimento. Per default, il campo di inserimento all'inizio e' vuoto.

Funzioni utili sui text field

Menu'

Appare come una box con un'etichetta (titolo del menu') al centro. Quando l'utente si posiziona col mouse su tale box (ed eventualente clicca un tasto) appare una tendina contenente le voci del menu', e l'utente puo' selezionarne una.
FL_OBJECT * fl_add_menu(tipo, x,y, w,h, label);
tipo = uno tra:

Definire le voci di un menu'

fl_add_menu non specifica quali sono le voci del menu'. Queste si assegnano con:
fl_set_menu(menu', stringa);
stringa contiene le voci del menu' una di seguito all'altra separate da '|', es: "prima|seconda|terza"

Alcune funzioni utili sui menu'

Esempio

Vedere file menu.c.
#include "forms.h"
#include 

void menu_cb(FL_OBJECT *ob, long user_data)
{
  printf("%s\n",fl_get_menu_item_text(ob,fl_get_menu(ob)));
  switch(fl_get_menu(ob))
  {
    case 1: /* azione corrispondente a prima voce */ break;
    case 2: /* azione corrispondente a seconda voce */ break;
    case 3: /* azione corrispondente a terza voce */ break;
    case 4: exit(0);
  }
}

int main(int argc,char *argv[])
{
  FL_FORM *form;
  FL_OBJECT *menu;

  fl_initialize(&argc, argv, "FormDemo", 0, 0);
  
  form = fl_bgn_form(FL_UP_BOX,140,100);
    menu = fl_add_menu(FL_PULLDOWN_MENU,20,20,100,60,"This is a menu");
    fl_set_object_boxtype(menu,FL_UP_BOX);
    fl_set_menu(menu,"one option|another option|a third option|exit");
    fl_set_object_callback(menu,menu_cb,0);
  fl_end_form();
    
  fl_show_form(form, FL_PLACE_MOUSE, FL_TRANSIENT,"FormDemo");
  fl_do_forms();
}

Modo di una voce di menu'

Ogni voce ha un modo tra: Mediante FL_PUP_BOX e FL_PUP_CHECK si realizzano voci che restano selezionate o disselezionate tra un'apertura e l'altra della tendina. Mediante FL_PUP_RADIO si fa cosa analoga per voci mutuamente esclusive.

Per assegnare / leggere il modo corrente di una voce di menu':

fl_set_menu_item_mode(menu, numero voce, modo);
fl_get_menu_item_mode(menu, numero voce);

Uso di FL_PUP_BOX e FL_PUP_CHECK:
Se una voce ha un quadtratino (vuoto o pieno) a fianco, ogni volta che seleziono la voce il quadratino passa da vuoto a pieno e viceversa (ovvero i modi FL_PUP_BOX e FL_PUP_CHECK si scambiano tra loro).

Uso di FL_PUP_RADIO:
Le voci con un rombetto (FL_PUP_RADIO) costituiscono un gruppo "radio": in ogni istante solo una puo' avere il rombetto pieno, se ne seleziono una il suo rombetto si riempie, e si svuota quello di tutte le altre. Analogo ai gruppi di radio buttons visti in precedenza.

Choice

Si presenta come un box che mostra al suo interno la scelta corrente. L'utente puo' ciclare tra le scelte possibili usando il tasto sinistro o centrale del mouse, oppure ottenere la lista completa in forma di menu' usando il tasto destro. Le funzioni per oggetti choice sono molto simili a quelle dei menu'. Vedere manuale.

Browser

Oggetto che puo' contenere un numero qualsiasi di linee di testo. Se ne contiene piu' di quante stiano nella dimensione dell'oggetto, XForms aggiunge automaticamente una scroll bar.

FL_OBJECT * fl_add_menu(tipo, x,y, w,h, label);
tipo = uno fra: E' possibile anche inizializzare il contenuto testuale di un browser caricandolo da un file, questo permette ad es. di visualizzare schermate di help caricate da file.

Per maggiori dettagli si rimanda al manuale.

Form di utilita'

XForms fornisce alcune form predefinite per compiti particolari: Per dettagli vedere il manuale.

Attributi degli oggetti

Ogni classe di oggetti ha certi attributi, che posso leggere o impostare con apposite funzioni. Alcuni attributi sono comuni a tutte le classi di oggetti: colore, geometria, etichetta.

Colore

I colori sono espressi comeindici interi in una color map. Esistono costanti predefinite: FL_BLACK, FL_RED...

Esempio: File colors4.c.
Visualizza una form con un bottone in alto, etichettato "color", e 4 bottoni ognuno di un colore diverso. Cliccare uno dei 4 bottoni provoca il cambiamento di colore del bottone in alto, che diventa uguale a quello del colore selezionato. Cliccare il bottone in alto provoca l'uscita dal programma.
I 4 bottoni condividono la stessa callback, ma con un diverso valore come secondo argomento (quelle di tipo "long") che e' uguale al colore del bottone, e che viene usato all'interno della callback per cambiare il colore del bottone in alto.
Il bottone in alto non ha callback, cosi' che cliccare su di esso provoca l'uscita da fl_do_forms.

Geometria

Etichetta

Simboli come etichette di oggetti

Finora abbiamo visto oggetti con etichette testuali. L'etichetta di un oggetto puo' essere un simbolo. XForms ha un insieme di simboli predefiniti. Quando l'etichetta di un simbolo inizia con il carattere '@', XForms capisce che deve essere generato un simbolo e non una stringa. Il resto della stringa etichetta (dopo '@') dice che simbolo deve essere generato, esempi: Posso anche specificare che un simbolo deve essere ruotato (es: sono predefinite solo le frecce a dx e sx, per ottenere quelle verso l'alto e il basso devo adoperare rotazioni.

Esempio: File sym.c.

Il programmatore puo' anche creare nuovi simboli per esempio usando pixmap, o usando funzioni di disegno fornite da XForms.

Nascondere / disattivare parti di un'interfaccia

Non tutte le parti di un'interfaccia sono necessariamente visualizzate sempre.

Alcune form (form secondarie) appaiono in seguito ad azioni dell'utente. Una form puo' essere visualizzata e nascosta a varie volte. Inoltre una form puo' essere visualizzata ma disabilitata a ricevere eventi.

Analogamente per un oggetto all'interno di una form. Posso nascondere temporaneamente oggetti presenti in una form e successivamente mostrarli di nuovo, oppure solo disabilitare la cattura di eventi da parte di un oggetto. E' utile disattivare oggetti corrispondenti ad opzioni non valide (non selezionabili) nello stato attuale.

E' possibile anche aggiungere / cancellare oggetti in una form esistente (o in un gruppo all'interno di una form). Vedere il manuale.

Canvas

Un canvas e' una sottofinestra di una form gestita da X direttamente.

Abbiamo detto che XForms si frappone tra Xlib e il programma applicativo rendendo Xlib trasparente a quest'ultimo. Si puo' pensare la canvas come un "buco" in XForms attraverso il quale il programma applicativo puo' vedere Xlib direttamente.

Una canvas e' una finestra X. Vi sono funzioni per settare le caratteristiche di questa finestra (colormap, profondita', ecc.). Una volta che la form contenente la canvas e' stata mostrata, posso ottenere l'identificatore della finestra X che realizza la canvas e usare questa come argomento di routine fornite da Xlib.

Noi non gestiremo una canvas direttamente con Xlib, ma con OpenGL (pacchetto per grafica 3D, internamente e' implementato in X).

Perche' vi possa operare OpenGL, una canvas (ovvero la finestra X che la realizza) deve avere certe caratteristiche. XForms fornisce una routine apposita che aggiunge ad una form una canvas adatta ad essere gestita da OpenGL.

Definizione di una canvas OpenGL

FL_OBJECT * fl_add_glcanvas(tipo, x,y, w,h);
tipo = GL_NORMAL_CANVAS (in futuro anche GL_SCROLLED_CANVAS)
Per default crea una canvas costituita da una finestra X in RGBA mode (colori specificati come terne di valori rosso/verde/blu), dotata di depth buffer (per eliminazione superfici nascoste) e double buffer.

Double buffer: ha un front buffer = quello visualizzato sullo schermo, e un back buffer = in memoria ma non visualizzato; io disegno nel back buffer e poi con un'istruzione scambio i buffer in modo da visualizzare istantaneamente quanto disegnato; invece nelle finestre con single buffer disegno direttamente nel front buffer, e siccome disegnare richiede un certo tempo, si ha un effetto visivo sgradevole).

Una volta aperta una canvas OpenGL, vi si puo' disegnare dentro usando le funzioni di OpenGL.

Gestione di eventi nella canvas

I comandi di disegno OpenGL vanno inviati ogni qual volta la canvas necessita di ridisegnamento (per esempio quando viene visualizzata per la prima volta, quando viene rivisualizzata dopo essere stata coperta da un'altra finestra, ecc.).

Infatti, se XForms si occupa di ridisegnare i suoi oggetti in questi casi, non lo fa per le canvas (per definizione, una canvas e' una sottofinestra che sfugge alla gestione di XForms).

Per ottenere il redisegmanento automatico del contenuto della canvas, devo mettere i comandi necessari all'interno di una funzione che assegno come event-handler della canvas, collegata all'evento X Expose.

Expose e' un evento X che viene generato quando una finestra (in questo caso la canvas) diventa visibile.

Un event-handler e' una funzione chiamata da XForms automaticamente quando si verifica un certo evento X all'interno dell' oggetto canvas. E' analoga a una callback, ma per eventi non predefiniti come catturabili dalla classe di oggetti in esame.

Differenza tra event-handler di una canvas e callback degli altri oggetti Xforms:

Definire un event handler

Un event handler e' una funzione del tipo:
int f(FL_OBJECT *ob, Window win, int w,int h, XEvent *ev, void* user_data);
gli argomenti (generati automaticamente da XForms quando chiama si verifica l'evento), sono:

Associare un event handler

void fl_add_canvas_handler(canvas, evento X, f, user_data);

Event handler per canvas OpenGL

In teoria, bisogna sapere programmare con Xlib per scrivere un event-handler (chiamare sulla finestra win opportune funzioni Xlib a seconda dei valori dei parametri portati dall'evento ev).

In pratica, siccome noi stiamo usando una glcanvas, non usiamo Xlib ma OpenGL per scrivere l'event-handler.

Il nostro event-handler per l'evento Expose conterra' le funzioni OpenGL necessarie a ridisegnare la scena in OpenGL:

  int canvas_expose(FL_OBJECT *ob, Window win,
                    int width, int height, XEvent *xev, void *user_data)
  {
    istruzioni OpenGL necessarie a disegnare la scena;
    return 0; 
  }
Inoltre e' utile definire anche un event handler per l'evento ConfigureNotify (evento generato quando le dimensioni della finestra che realizza la canvas vengono modificate).

Se l'evento ConfigureNotify non e' gestito, OpenGL continua a utilizzare come dimensioni di viewport quelle precedenti, e la scena appare o in un piccolo sottopezzo della finestra attuale (se le dimensioni sono aumentate) o appare tagliata (se le dimensioni sono state ridotte).

L'event-handler per ConfigureNotify riassegna le dimensioni della viewport di OpenGL uguali alle nuove dimensioni della canvas.

  int canvas_reshape(FL_OBJECT *ob, Window win,
                     int width, int height, XEvent *xev, void *user_data)
  {
    glViewport(0, 0, (GLint)width, (GLint)height); /* assegna viewport */
    istruzioni OpenGL necessarie a disegnare la scena;
    return 0;
  }